From a1cd2a5915c13f6a9b8eafa3807e143a02616bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 20 Feb 2023 01:11:56 +0100 Subject: [PATCH 1/2] refactor(core): definition of "ExtensionFileSource" (#17823) This commit changes definition of "ExtensionFileSource", by changing "code" field to being "ExtensionFileSourceCode" enum. Currently the enum has only a single variant "IncludedInBinary". It is done in preparation to allow embedders to decide if they want to include the source code in the binary when snapshotting (in most cases they shouldn't do that). In the follow up commit we'll add more variants to "ExtensionFileSourceCode". "include_js_files_dir!" macro was removed in favor "include_js_files!" macro which can now accept "dir" option. --- bench_util/benches/utf8.rs | 5 +- cli/build.rs | 12 +++-- core/extensions.rs | 90 +++++++++++++++++++++-------------- core/lib.rs | 1 + core/modules.rs | 7 ++- core/runtime.rs | 6 ++- ext/node/lib.rs | 3 +- ext/url/benches/url_ops.rs | 5 +- ext/web/benches/encoding.rs | 5 +- ext/web/benches/timers_ops.rs | 5 +- ext/webidl/benches/dict.rs | 5 +- runtime/build.rs | 15 ++++-- 12 files changed, 103 insertions(+), 56 deletions(-) diff --git a/bench_util/benches/utf8.rs b/bench_util/benches/utf8.rs index 7a9066d1e06e30..a8d6e20eb1c8cd 100644 --- a/bench_util/benches/utf8.rs +++ b/bench_util/benches/utf8.rs @@ -7,12 +7,14 @@ use deno_bench_util::bencher::Bencher; use deno_bench_util::BenchOptions; use deno_core::Extension; use deno_core::ExtensionFileSource; +use deno_core::ExtensionFileSourceCode; fn setup() -> Vec { vec![Extension::builder("bench_setup") .js(vec![ExtensionFileSource { specifier: "setup.js".to_string(), - code: r#" + code: ExtensionFileSourceCode::IncludedInBinary( + r#" const hello = "hello world\n"; const hello1k = hello.repeat(1e3); const hello1m = hello.repeat(1e6); @@ -20,6 +22,7 @@ fn setup() -> Vec { const hello1kEncoded = Deno.core.encode(hello1k); const hello1mEncoded = Deno.core.encode(hello1m); "#, + ), }]) .build()] } diff --git a/cli/build.rs b/cli/build.rs index 84d8fd5498feef..807009a5d698e4 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -4,10 +4,11 @@ use std::env; use std::path::Path; use std::path::PathBuf; -use deno_core::include_js_files_dir; +use deno_core::include_js_files; use deno_core::snapshot_util::*; use deno_core::Extension; use deno_core::ExtensionFileSource; +use deno_core::ExtensionFileSourceCode; use deno_runtime::deno_cache::SqliteBackedCache; use deno_runtime::permissions::PermissionsContainer; use deno_runtime::*; @@ -17,7 +18,6 @@ mod ts { use crate::deno_webgpu_get_declaration; use deno_core::error::custom_error; use deno_core::error::AnyError; - use deno_core::include_js_files_dir; use deno_core::op; use deno_core::OpState; use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; @@ -260,7 +260,7 @@ mod ts { op_load::decl(), op_script_version::decl(), ]) - .js(include_js_files_dir! { + .js(include_js_files! { dir "tsc", "00_typescript.js", "99_main_compiler.js", @@ -354,13 +354,15 @@ fn create_cli_snapshot(snapshot_path: PathBuf) { deno_flash::init::(false), // No --unstable ]; - let mut esm_files = include_js_files_dir!( + let mut esm_files = include_js_files!( dir "js", "40_testing.js", ); esm_files.push(ExtensionFileSource { specifier: "runtime/js/99_main.js".to_string(), - code: deno_runtime::js::SOURCE_CODE_FOR_99_MAIN_JS, + code: ExtensionFileSourceCode::IncludedInBinary( + deno_runtime::js::SOURCE_CODE_FOR_99_MAIN_JS, + ), }); let extensions_with_js = vec![Extension::builder("cli") // FIXME(bartlomieju): information about which extensions were diff --git a/core/extensions.rs b/core/extensions.rs index e497b800315a71..ab686d8682ed15 100644 --- a/core/extensions.rs +++ b/core/extensions.rs @@ -6,10 +6,21 @@ use std::rc::Rc; use std::task::Context; use v8::fast_api::FastFunction; +#[derive(Clone, Debug)] +pub enum ExtensionFileSourceCode { + /// Source code is included in the binary produced. Either by being defined + /// inline, or included using `include_str!()`. If you are snapshotting, this + /// will result in two copies of the source code being included - one in the + /// snapshot, the other the static string in the `Extension`. + IncludedInBinary(&'static str), + // TODO(bartlomieju): add more variants that allow to read file from the disk, + // and not include it in the binary. +} + #[derive(Clone, Debug)] pub struct ExtensionFileSource { pub specifier: String, - pub code: &'static str, + pub code: ExtensionFileSourceCode, } pub type OpFnRef = v8::FunctionCallback; pub type OpMiddlewareFn = dyn Fn(OpDecl) -> OpDecl; @@ -180,6 +191,9 @@ impl ExtensionBuilder { pub fn js(&mut self, js_files: Vec) -> &mut Self { let js_files = + // TODO(bartlomieju): if we're automatically remapping here, then we should + // use a different result struct that `ExtensionFileSource` as it's confusing + // when (and why) the remapping happens. js_files.into_iter().map(|file_source| ExtensionFileSource { specifier: format!("internal:{}/{}", self.name, file_source.specifier), code: file_source.code, @@ -189,16 +203,15 @@ impl ExtensionBuilder { } pub fn esm(&mut self, esm_files: Vec) -> &mut Self { - let esm_files = - esm_files - .into_iter() - .map(|file_source| ExtensionFileSource { - specifier: format!( - "internal:{}/{}", - self.name, file_source.specifier - ), - code: file_source.code, - }); + let esm_files = esm_files + .into_iter() + // TODO(bartlomieju): if we're automatically remapping here, then we should + // use a different result struct that `ExtensionFileSource` as it's confusing + // when (and why) the remapping happens. + .map(|file_source| ExtensionFileSource { + specifier: format!("internal:{}/{}", self.name, file_source.specifier), + code: file_source.code, + }); self.esm.extend(esm_files); self } @@ -259,48 +272,53 @@ impl ExtensionBuilder { } /// Helps embed JS files in an extension. Returns a vector of -/// `ExtensionFileSource`, that represent the filename and source code. +/// `ExtensionFileSource`, that represent the filename and source code. All +/// specified files are rewritten into "internal:/". /// -/// Example: +/// An optional "dir" option can be specified to prefix all files with a +/// directory name. +/// +/// Example (for "my_extension"): /// ```ignore /// include_js_files!( /// "01_hello.js", /// "02_goodbye.js", /// ) -/// ``` -#[macro_export] -macro_rules! include_js_files { - ($($file:literal,)+) => { - vec![ - $($crate::ExtensionFileSource { - specifier: $file.to_string(), - code: include_str!($file), - },)+ - ] - }; -} - -/// Helps embed JS files in an extension. Returns a vector of -/// `ExtensionFileSource`, that represent the filename and source code. -/// Additional "dir" option is required, that specifies which directory in the -/// crate root contains the listed files. "dir" option will be prepended to -/// each file name. +/// // Produces following specifiers: +/// - "internal:my_extension/01_hello.js" +/// - "internal:my_extension/02_goodbye.js" /// -/// Example: +/// /// Example with "dir" option (for "my_extension"): /// ```ignore -/// include_js_files_dir!( -/// dir "example", +/// include_js_files!( +/// dir "js", /// "01_hello.js", /// "02_goodbye.js", /// ) +/// // Produces following specifiers: +/// - "internal:my_extension/js/01_hello.js" +/// - "internal:my_extension/js/02_goodbye.js" /// ``` #[macro_export] -macro_rules! include_js_files_dir { +macro_rules! include_js_files { (dir $dir:literal, $($file:literal,)+) => { vec![ $($crate::ExtensionFileSource { specifier: concat!($dir, "/", $file).to_string(), - code: include_str!(concat!($dir, "/", $file)), + code: $crate::ExtensionFileSourceCode::IncludedInBinary( + include_str!(concat!($dir, "/", $file) + )), + },)+ + ] + }; + + ($($file:literal,)+) => { + vec![ + $($crate::ExtensionFileSource { + specifier: $file.to_string(), + code: $crate::ExtensionFileSourceCode::IncludedInBinary( + include_str!($file) + ), },)+ ] }; diff --git a/core/lib.rs b/core/lib.rs index a7770133993188..51a03493d606de 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -55,6 +55,7 @@ pub use crate::async_cell::RcRef; pub use crate::extensions::Extension; pub use crate::extensions::ExtensionBuilder; pub use crate::extensions::ExtensionFileSource; +pub use crate::extensions::ExtensionFileSourceCode; pub use crate::extensions::OpDecl; pub use crate::extensions::OpMiddlewareFn; pub use crate::flags::v8_set_flags; diff --git a/core/modules.rs b/core/modules.rs index 1f427508a11cc7..43dabf4112b6ad 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -3,6 +3,7 @@ use crate::bindings; use crate::error::generic_error; use crate::extensions::ExtensionFileSource; +use crate::extensions::ExtensionFileSourceCode; use crate::module_specifier::ModuleSpecifier; use crate::resolve_import; use crate::resolve_url; @@ -402,7 +403,11 @@ impl ModuleLoader for InternalModuleLoader { let result = if let Some(load_callback) = &self.maybe_load_callback { load_callback(file_source) } else { - Ok(file_source.code.to_string()) + match file_source.code { + ExtensionFileSourceCode::IncludedInBinary(code) => { + Ok(code.to_string()) + } + } }; return async move { diff --git a/core/runtime.rs b/core/runtime.rs index 25a09e85e7ddb4..ae2b0489b1b62e 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -21,6 +21,7 @@ use crate::source_map::SourceMapCache; use crate::source_map::SourceMapGetter; use crate::Extension; use crate::ExtensionFileSource; +use crate::ExtensionFileSourceCode; use crate::NoopModuleLoader; use crate::OpMiddlewareFn; use crate::OpResult; @@ -868,11 +869,14 @@ impl JsRuntime { { let js_files = ext.get_js_sources(); for file_source in js_files { + let ExtensionFileSourceCode::IncludedInBinary(code) = + file_source.code; + // TODO(@AaronO): use JsRuntime::execute_static() here to move src off heap realm.execute_script( self.v8_isolate(), &file_source.specifier, - file_source.code, + code, )?; } } diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 43861ef46fbddb..697100c8fe6e8a 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -2,7 +2,6 @@ use deno_core::error::AnyError; use deno_core::include_js_files; -use deno_core::include_js_files_dir; use deno_core::located_script_name; use deno_core::op; use deno_core::Extension; @@ -96,7 +95,7 @@ fn op_node_build_os() -> String { } pub fn init_polyfill() -> Extension { - let esm_files = include_js_files_dir!( + let esm_files = include_js_files!( dir "polyfills", "_core.ts", "_crypto/crypto_browserify/asn1.js/base/buffer.js", diff --git a/ext/url/benches/url_ops.rs b/ext/url/benches/url_ops.rs index 001b5de9265e20..828b022978b5a3 100644 --- a/ext/url/benches/url_ops.rs +++ b/ext/url/benches/url_ops.rs @@ -7,6 +7,7 @@ use deno_bench_util::bencher::Bencher; use deno_core::Extension; use deno_core::ExtensionFileSource; +use deno_core::ExtensionFileSourceCode; fn setup() -> Vec { vec![ @@ -15,9 +16,11 @@ fn setup() -> Vec { Extension::builder("bench_setup") .esm(vec![ExtensionFileSource { specifier: "internal:setup".to_string(), - code: r#"import { URL } from "internal:deno_url/00_url.js"; + code: ExtensionFileSourceCode::IncludedInBinary( + r#"import { URL } from "internal:deno_url/00_url.js"; globalThis.URL = URL; "#, + ), }]) .build(), ] diff --git a/ext/web/benches/encoding.rs b/ext/web/benches/encoding.rs index bb297a1bfeb187..bfae079371f404 100644 --- a/ext/web/benches/encoding.rs +++ b/ext/web/benches/encoding.rs @@ -6,6 +6,7 @@ use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; use deno_core::Extension; use deno_core::ExtensionFileSource; +use deno_core::ExtensionFileSourceCode; use deno_web::BlobStore; struct Permissions; @@ -32,11 +33,13 @@ fn setup() -> Vec { Extension::builder("bench_setup") .esm(vec![ExtensionFileSource { specifier: "internal:setup".to_string(), - code: r#" + code: ExtensionFileSourceCode::IncludedInBinary( + r#" import { TextDecoder } from "internal:deno_web/08_text_encoding.js"; globalThis.TextDecoder = TextDecoder; globalThis.hello12k = Deno.core.encode("hello world\n".repeat(1e3)); "#, + ), }]) .state(|state| { state.put(Permissions {}); diff --git a/ext/web/benches/timers_ops.rs b/ext/web/benches/timers_ops.rs index f01b4c53239bc7..657082df4b5741 100644 --- a/ext/web/benches/timers_ops.rs +++ b/ext/web/benches/timers_ops.rs @@ -6,6 +6,7 @@ use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; use deno_core::Extension; use deno_core::ExtensionFileSource; +use deno_core::ExtensionFileSourceCode; use deno_web::BlobStore; struct Permissions; @@ -32,11 +33,11 @@ fn setup() -> Vec { .esm(vec![ ExtensionFileSource { specifier: "internal:setup".to_string(), - code: r#" + code: ExtensionFileSourceCode::IncludedInBinary(r#" import { setTimeout, handleTimerMacrotask } from "internal:deno_web/02_timers.js"; globalThis.setTimeout = setTimeout; Deno.core.setMacrotaskCallback(handleTimerMacrotask); - "# + "#) }, ]) .state(|state| { diff --git a/ext/webidl/benches/dict.rs b/ext/webidl/benches/dict.rs index 00bef4935000ea..df0844fce2b104 100644 --- a/ext/webidl/benches/dict.rs +++ b/ext/webidl/benches/dict.rs @@ -7,6 +7,7 @@ use deno_bench_util::bencher::Bencher; use deno_core::Extension; use deno_core::ExtensionFileSource; +use deno_core::ExtensionFileSourceCode; fn setup() -> Vec { vec![ @@ -14,7 +15,9 @@ fn setup() -> Vec { Extension::builder("deno_webidl_bench") .esm(vec![ExtensionFileSource { specifier: "internal:setup".to_string(), - code: include_str!("dict.js"), + code: ExtensionFileSourceCode::IncludedInBinary(include_str!( + "dict.js" + )), }]) .build(), ] diff --git a/runtime/build.rs b/runtime/build.rs index bbdec8f57e09c9..a813d63cb44da6 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -1,6 +1,6 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use deno_core::include_js_files_dir; +use deno_core::include_js_files; use std::env; use std::path::PathBuf; @@ -18,6 +18,7 @@ mod not_docs { use deno_ast::SourceTextInfo; use deno_core::error::AnyError; use deno_core::ExtensionFileSource; + use deno_core::ExtensionFileSourceCode; fn transpile_ts_for_snapshotting( file_source: &ExtensionFileSource, @@ -34,13 +35,15 @@ mod not_docs { ), }; + let ExtensionFileSourceCode::IncludedInBinary(code) = file_source.code; + if !should_transpile { - return Ok(file_source.code.to_string()); + return Ok(code.to_string()); } let parsed = deno_ast::parse_module(ParseParams { specifier: file_source.specifier.to_string(), - text_info: SourceTextInfo::from_string(file_source.code.to_string()), + text_info: SourceTextInfo::from_string(code.to_string()), media_type, capture_tokens: false, scope_analysis: false, @@ -188,7 +191,7 @@ mod not_docs { "deno_http", "deno_flash", ]) - .esm(include_js_files_dir!( + .esm(include_js_files!( dir "js", "01_build.js", "01_errors.js", @@ -283,7 +286,9 @@ mod not_docs { .dependencies(vec!["runtime"]) .esm(vec![ExtensionFileSource { specifier: "js/99_main.js".to_string(), - code: include_str!("js/99_main.js"), + code: ExtensionFileSourceCode::IncludedInBinary(include_str!( + "js/99_main.js" + )), }]) .build(), ); From 6915a9b7a701dde0e1078867961c9a91811c1850 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 21 Feb 2023 00:35:04 +0900 Subject: [PATCH 2/2] test(ext/node): more node compat tests (#17827) This PR adds the remaining ~650 Node.js compat test cases from std/node. Among these 650 cases, about 130 cases are now failing. These failing cases are prefixed with `TODO:` in `tests/node_compat/config.json`. These will be addressed in later PRs. --- .dprint.json | 1 + cli/tests/node_compat/config.json | 842 ++++- cli/tests/node_compat/test.ts | 8 +- .../node_compat/test/common/child_process.js | 56 + .../node_compat/test/common/countdown.js | 35 + cli/tests/node_compat/test/common/dns.js | 327 ++ .../node_compat/test/common/duplexpair.js | 55 + cli/tests/node_compat/test/common/fixtures.js | 45 + .../node_compat/test/common/hijackstdio.js | 39 + cli/tests/node_compat/test/common/index.mjs | 113 + cli/tests/node_compat/test/common/internet.js | 68 + .../test/fixtures/GH-1899-output.js | 30 + cli/tests/node_compat/test/fixtures/a.js | 53 + .../test/fixtures/child-process-spawn-node.js | 14 + .../child_process_should_emit_error.js | 36 + cli/tests/node_compat/test/fixtures/echo.js | 41 + .../node_compat/test/fixtures/elipses.txt | 1 + cli/tests/node_compat/test/fixtures/empty.txt | 0 cli/tests/node_compat/test/fixtures/exit.js | 31 + .../test/fixtures/keys/agent1-cert.pem | 30 + .../test/fixtures/keys/agent1-key.pem | 34 + cli/tests/node_compat/test/fixtures/loop.js | 17 + .../node_compat/test/fixtures/package.json | 1 + .../node_compat/test/fixtures/print-chars.js | 35 + cli/tests/node_compat/test/fixtures/x.txt | 1 + .../node_compat/test/internet/package.json | 1 + .../test/internet/test-dgram-connect.js | 26 + .../node_compat/test/internet/test-dns-any.js | 192 + .../test/internet/test-dns-idna2008.js | 76 + .../test/internet/test-dns-ipv4.js | 257 ++ .../test/internet/test-dns-ipv6.js | 250 ++ .../test/internet/test-dns-lookup.js | 61 + .../internet/test-dns-promises-resolve.js | 49 + .../test/internet/test-dns-regress-6244.js | 35 + ...t-dns-setserver-in-callback-of-resolve4.js | 25 + .../node_compat/test/internet/test-dns.js | 761 ++++ .../test/parallel/test-assert-async.js | 244 ++ .../node_compat/test/parallel/test-assert.js | 1615 +++++++++ .../test/parallel/test-bad-unicode.js | 40 + .../test/parallel/test-buffer-alloc.js | 1181 ++++++ .../test/parallel/test-buffer-arraybuffer.js | 162 + .../test/parallel/test-buffer-ascii.js | 53 + .../test/parallel/test-buffer-badhex.js | 55 + .../test/parallel/test-buffer-bigint64.js | 62 + .../test/parallel/test-buffer-bytelength.js | 142 + .../parallel/test-buffer-compare-offset.js | 101 + .../test/parallel/test-buffer-concat.js | 107 + .../test/parallel/test-buffer-constants.js | 25 + .../test/parallel/test-buffer-copy.js | 236 ++ .../test/parallel/test-buffer-equals.js | 32 + .../test-buffer-failed-alloc-typed-arrays.js | 40 + .../test/parallel/test-buffer-fakes.js | 61 + .../test/parallel/test-buffer-from.js | 74 + .../test/parallel/test-buffer-includes.js | 317 ++ .../test/parallel/test-buffer-indexof.js | 646 ++++ .../test/parallel/test-buffer-inheritance.js | 46 + .../test/parallel/test-buffer-isencoding.js | 45 + .../test/parallel/test-buffer-iterator.js | 69 + .../test/parallel/test-buffer-new.js | 18 + .../test-buffer-no-negative-allocation.js | 45 + .../parallel/test-buffer-nopendingdep-map.js | 20 + .../parallel/test-buffer-of-no-deprecation.js | 14 + .../parallel/test-buffer-over-max-length.js | 37 + .../parallel/test-buffer-parent-property.js | 28 + .../test/parallel/test-buffer-read.js | 113 + .../test/parallel/test-buffer-readdouble.js | 151 + .../test/parallel/test-buffer-readfloat.js | 113 + .../test/parallel/test-buffer-readint.js | 204 ++ .../test/parallel/test-buffer-readuint.js | 172 + .../test/parallel/test-buffer-safe-unsafe.js | 31 + .../parallel/test-buffer-sharedarraybuffer.js | 34 + .../test/parallel/test-buffer-slice.js | 136 + .../test/parallel/test-buffer-slow.js | 69 + .../test/parallel/test-buffer-swap.js | 159 + .../test/parallel/test-buffer-tojson.js | 42 + .../parallel/test-buffer-tostring-range.js | 107 + .../test-buffer-tostring-rangeerror.js | 28 + .../test/parallel/test-buffer-tostring.js | 44 + .../test/parallel/test-buffer-write.js | 115 + .../test/parallel/test-buffer-writedouble.js | 140 + .../test/parallel/test-buffer-writefloat.js | 124 + .../test/parallel/test-buffer-writeint.js | 277 ++ .../test/parallel/test-buffer-writeuint.js | 237 ++ .../parallel/test-buffer-zero-fill-cli.js | 39 + .../parallel/test-buffer-zero-fill-reset.js | 26 + .../test/parallel/test-buffer-zero-fill.js | 21 + .../test-child-process-can-write-to-stdout.js | 29 + .../test-child-process-default-options.js | 58 + .../test-child-process-double-pipe.js | 129 + ...rocess-exec-abortcontroller-promisified.js | 54 + .../parallel/test-child-process-exec-cwd.js | 46 + .../test-child-process-exec-encoding.js | 59 + .../parallel/test-child-process-exec-env.js | 71 + .../parallel/test-child-process-exec-error.js | 51 + .../test-child-process-exec-kill-throws.js | 42 + .../test-child-process-exec-maxbuf.js | 161 + .../test-child-process-exec-std-encoding.js | 33 + ...-process-exec-stdout-stderr-data-string.js | 20 + .../test-child-process-exec-timeout-expire.js | 61 + .../test-child-process-exec-timeout-kill.js | 50 + ...-child-process-exec-timeout-not-expired.js | 45 + ...ss-execFile-promisified-abortController.js | 66 + .../test-child-process-execfile-maxbuf.js | 99 + .../parallel/test-child-process-execfile.js | 135 + .../test-child-process-execfilesync-maxbuf.js | 60 + .../test-child-process-execsync-maxbuf.js | 76 + .../parallel/test-child-process-exit-code.js | 51 + .../test-child-process-flush-stdio.js | 40 + .../test/parallel/test-child-process-ipc.js | 73 + .../test/parallel/test-child-process-kill.js | 48 + .../test-child-process-set-blocking.js | 43 + .../parallel/test-child-process-spawn-args.js | 62 + .../test-child-process-spawn-event.js | 34 + .../test-child-process-spawnsync-args.js | 55 + .../test-child-process-spawnsync-env.js | 47 + .../test-child-process-spawnsync-maxbuf.js | 65 + ...ild-process-spawnsync-validation-errors.js | 223 ++ .../parallel/test-child-process-spawnsync.js | 74 + .../test-child-process-stdio-inherit.js | 66 + .../test-child-process-stdout-flush-exit.js | 67 + .../test-child-process-stdout-flush.js | 58 + .../parallel/test-client-request-destroy.js | 20 + .../test-console-async-write-error.js | 22 + .../test/parallel/test-console-group.js | 248 ++ .../test/parallel/test-console-instance.js | 156 + .../test-console-log-stdio-broken-dest.js | 31 + .../test-console-log-throw-primitive.js | 21 + .../test-console-no-swallow-stack-overflow.js | 26 + .../parallel/test-console-sync-write-error.js | 46 + .../test/parallel/test-console-table.js | 300 ++ .../test/parallel/test-console-tty-colors.js | 102 + .../test/parallel/test-crypto-hmac.js | 483 +++ .../test/parallel/test-dgram-address.js | 88 + .../test-dgram-bind-default-address.js | 60 + .../test/parallel/test-dgram-bind.js | 50 + .../test/parallel/test-dgram-bytes-length.js | 46 + .../parallel/test-dgram-close-during-bind.js | 26 + .../parallel/test-dgram-close-in-listening.js | 33 + .../test-dgram-close-is-not-callback.js | 28 + .../test/parallel/test-dgram-close-signal.js | 38 + .../test/parallel/test-dgram-close.js | 63 + ...ram-connect-send-callback-buffer-length.js | 30 + ...test-dgram-connect-send-callback-buffer.js | 27 + ...gram-connect-send-callback-multi-buffer.js | 36 + .../test-dgram-connect-send-default-host.js | 55 + .../test-dgram-connect-send-empty-array.js | 29 + .../test-dgram-connect-send-empty-buffer.js | 27 + .../test-dgram-connect-send-empty-packet.js | 35 + ...st-dgram-connect-send-multi-buffer-copy.js | 36 + ...t-dgram-connect-send-multi-string-array.js | 24 + .../test/parallel/test-dgram-connect.js | 73 + .../parallel/test-dgram-createSocket-type.js | 68 + .../test/parallel/test-dgram-custom-lookup.js | 56 + .../test-dgram-error-message-address.js | 64 + .../test/parallel/test-dgram-implicit-bind.js | 53 + .../test/parallel/test-dgram-ipv6only.js | 44 + .../parallel/test-dgram-listen-after-bind.js | 52 + .../test/parallel/test-dgram-msgsize.js | 46 + .../test/parallel/test-dgram-oob-buffer.js | 52 + .../test/parallel/test-dgram-recv-error.js | 26 + .../parallel/test-dgram-send-bad-arguments.js | 162 + ...gram-send-callback-buffer-empty-address.js | 23 + ...nd-callback-buffer-length-empty-address.js | 28 + .../test-dgram-send-callback-buffer-length.js | 50 + .../test-dgram-send-callback-buffer.js | 26 + ...end-callback-multi-buffer-empty-address.js | 34 + .../test-dgram-send-callback-multi-buffer.js | 34 + .../test-dgram-send-callback-recursive.js | 50 + .../test-dgram-send-cb-quelches-error.js | 47 + .../parallel/test-dgram-send-default-host.js | 79 + .../parallel/test-dgram-send-empty-array.js | 32 + .../parallel/test-dgram-send-empty-buffer.js | 50 + .../parallel/test-dgram-send-empty-packet.js | 36 + .../test/parallel/test-dgram-send-error.js | 77 + .../test-dgram-send-invalid-msg-type.js | 43 + .../test-dgram-send-multi-buffer-copy.js | 35 + .../test-dgram-send-multi-string-array.js | 20 + .../parallel/test-dgram-socket-buffer-size.js | 178 + .../test/parallel/test-dgram-udp4.js | 59 + .../test-dgram-udp6-link-local-address.js | 61 + .../test-dgram-udp6-send-default-host.js | 83 + ...est-diagnostics-channel-has-subscribers.js | 17 + .../parallel/test-diagnostics-channel-net.js | 32 + ...gnostics-channel-object-channel-pub-sub.js | 53 + .../test-diagnostics-channel-pub-sub.js | 51 + .../test-diagnostics-channel-symbol-named.js | 35 + .../parallel/test-diagnostics-channel-udp.js | 22 + .../test/parallel/test-dns-lookup.js | 179 + .../test/parallel/test-dns-memory-error.js | 23 + .../test/parallel/test-dns-multi-channel.js | 59 + .../test/parallel/test-dns-promises-exists.js | 40 + .../test/parallel/test-dns-resolveany.js | 78 + .../parallel/test-dns-resolvens-typeerror.js | 62 + .../test-dns-setservers-type-check.js | 127 + .../node_compat/test/parallel/test-dns.js | 471 +++ .../test-eval-strict-referenceerror.js | 34 + .../node_compat/test/parallel/test-eval.js | 14 + .../test-event-emitter-add-listeners.js | 93 + ...test-event-emitter-check-listener-leaks.js | 110 + .../test-event-emitter-emit-context.js | 25 + .../test-event-emitter-error-monitor.js | 39 + .../parallel/test-event-emitter-errors.js | 44 + .../test-event-emitter-get-max-listeners.js | 26 + .../test-event-emitter-invalid-listener.js | 27 + .../test-event-emitter-listener-count.js | 25 + ...st-event-emitter-listeners-side-effects.js | 67 + .../parallel/test-event-emitter-listeners.js | 131 + ...-emitter-max-listeners-warning-for-null.js | 30 + ...mitter-max-listeners-warning-for-symbol.js | 32 + ...est-event-emitter-max-listeners-warning.js | 38 + .../test-event-emitter-max-listeners.js | 80 + .../test-event-emitter-method-names.js | 42 + .../test-event-emitter-modify-in-emit.js | 87 + ...mitter-no-error-provided-to-error-event.js | 65 + .../parallel/test-event-emitter-num-args.js | 61 + .../test/parallel/test-event-emitter-once.js | 77 + .../parallel/test-event-emitter-prepend.js | 50 + ...test-event-emitter-remove-all-listeners.js | 130 + .../test-event-emitter-remove-listeners.js | 177 + ...-emitter-set-max-listeners-side-effects.js | 39 + .../test-event-emitter-special-event-names.js | 44 + .../parallel/test-event-emitter-subclass.js | 74 + .../parallel/test-event-emitter-symbols.js | 30 + .../test/parallel/test-events-list.js | 26 + .../parallel/test-events-on-async-iterator.js | 399 ++ .../test/parallel/test-events-once.js | 272 ++ .../test-events-uncaught-exception-stack.js | 23 + .../parallel/test-eventtarget-brandcheck.js | 104 + .../test/parallel/test-exception-handler.js | 47 + .../test/parallel/test-exception-handler2.js | 43 + .../test/parallel/test-file-read-noexist.js | 39 + .../test/parallel/test-file-write-stream.js | 91 + .../test/parallel/test-file-write-stream2.js | 116 + .../test/parallel/test-file-write-stream3.js | 221 ++ .../test/parallel/test-file-write-stream4.js | 28 + .../test/parallel/test-fs-access.js | 246 ++ .../test/parallel/test-fs-append-file-sync.js | 115 + .../test/parallel/test-fs-append-file.js | 202 ++ .../test/parallel/test-fs-chmod-mask.js | 106 + .../test/parallel/test-fs-chmod.js | 167 + .../test/parallel/test-fs-chown-type-check.js | 60 + .../test/parallel/test-fs-copyfile.js | 176 + .../test/parallel/test-fs-empty-readStream.js | 57 + .../test/parallel/test-fs-mkdir.js | 379 ++ .../test/parallel/test-fs-open-flags.js | 101 + .../test/parallel/test-fs-open-mode-mask.js | 48 + .../test/parallel/test-fs-open-no-close.js | 38 + .../parallel/test-fs-open-numeric-flags.js | 23 + .../node_compat/test/parallel/test-fs-open.js | 128 + .../test/parallel/test-fs-opendir.js | 300 ++ .../parallel/test-fs-read-stream-autoClose.js | 23 + .../test-fs-read-stream-concurrent-reads.js | 54 + .../test-fs-read-stream-double-close.js | 26 + .../parallel/test-fs-read-stream-encoding.js | 24 + .../test/parallel/test-fs-read-stream-fd.js | 53 + .../parallel/test-fs-read-stream-inherit.js | 212 ++ .../test-fs-read-stream-patch-open.js | 24 + .../parallel/test-fs-read-stream-resume.js | 59 + .../test-fs-read-stream-throw-type-error.js | 84 + .../test/parallel/test-fs-read-stream.js | 284 ++ .../test/parallel/test-fs-read-type.js | 250 ++ .../test/parallel/test-fs-read-zero-length.js | 25 + .../node_compat/test/parallel/test-fs-read.js | 109 + .../test-fs-readdir-stack-overflow.js | 26 + .../test/parallel/test-fs-readdir.js | 60 + .../test/parallel/test-fs-readfile-empty.js | 52 + .../test/parallel/test-fs-realpath-native.js | 25 + .../node_compat/test/parallel/test-fs-rm.js | 433 +++ ...fs-rmdir-recursive-sync-warns-not-found.js | 30 + ...t-fs-rmdir-recursive-sync-warns-on-file.js | 30 + ...est-fs-rmdir-recursive-throws-not-found.js | 43 + .../test-fs-rmdir-recursive-throws-on-file.js | 36 + ...test-fs-rmdir-recursive-warns-not-found.js | 29 + .../test-fs-rmdir-recursive-warns-on-file.js | 29 + .../test/parallel/test-fs-rmdir-recursive.js | 252 ++ .../test/parallel/test-fs-rmdir-type-check.js | 29 + .../test/parallel/test-fs-watch.js | 105 + .../test/parallel/test-fs-watchfile.js | 112 + .../test/parallel/test-fs-write-buffer.js | 172 + .../parallel/test-fs-write-file-buffer.js | 62 + .../test-fs-write-file-invalid-path.js | 53 + .../test/parallel/test-fs-write-file-sync.js | 128 + .../test/parallel/test-fs-write-file.js | 115 + .../test/parallel/test-fs-write-no-fd.js | 19 + .../test-fs-write-stream-autoclose-option.js | 66 + ...-fs-write-stream-close-without-callback.js | 20 + .../test-fs-write-stream-double-close.js | 53 + .../test/parallel/test-fs-write-stream-end.js | 67 + .../test/parallel/test-fs-write-stream-fs.js | 45 + .../test-fs-write-stream-throw-type-error.js | 39 + .../test/parallel/test-fs-write-stream.js | 74 + .../test/parallel/test-fs-write-sync.js | 63 + .../test/parallel/test-fs-write.js | 212 ++ .../test/parallel/test-fs-writev-sync.js | 104 + .../test/parallel/test-fs-writev.js | 114 + .../parallel/test-handle-wrap-close-abort.js | 44 + .../test/parallel/test-http-agent-getname.js | 63 + .../test/parallel/test-http-client-get-url.js | 53 + .../test-http-client-read-in-error.js | 48 + .../parallel/test-http-outgoing-buffer.js | 26 + .../parallel/test-http-outgoing-destroy.js | 24 + .../test-http-outgoing-finish-writable.js | 47 + ...tp-outgoing-internal-headernames-getter.js | 30 + ...tp-outgoing-internal-headernames-setter.js | 22 + .../test-http-outgoing-internal-headers.js | 51 + .../test-http-outgoing-message-inheritance.js | 43 + .../test-http-outgoing-renderHeaders.js | 57 + .../parallel/test-http-outgoing-settimeout.js | 37 + .../test/parallel/test-http-url.parse-auth.js | 55 + .../test/parallel/test-http-url.parse-path.js | 53 + .../test/parallel/test-http-url.parse-post.js | 61 + .../parallel/test-http-url.parse-search.js | 54 + .../parallel/test-net-access-byteswritten.js | 28 + .../test/parallel/test-net-after-close.js | 58 + .../test/parallel/test-net-allow-half-open.js | 54 + ...t-net-better-error-messages-listen-path.js | 17 + .../test-net-better-error-messages-listen.js | 19 + .../test-net-better-error-messages-path.js | 29 + ...net-better-error-messages-port-hostname.js | 44 + .../test/parallel/test-net-bind-twice.js | 43 + .../test/parallel/test-net-buffersize.js | 59 + .../parallel/test-net-bytes-written-large.js | 74 + .../parallel/test-net-can-reset-timeout.js | 64 + .../test-net-connect-after-destroy.js | 16 + .../test/parallel/test-net-connect-buffer.js | 86 + .../test/parallel/test-net-connect-buffer2.js | 63 + .../test-net-connect-call-socket-connect.js | 46 + .../test/parallel/test-net-connect-destroy.js | 14 + .../test-net-connect-immediate-destroy.js | 18 + .../test-net-connect-immediate-finish.js | 66 + .../test/parallel/test-net-connect-no-arg.js | 42 + .../parallel/test-net-connect-options-ipv6.js | 74 + .../parallel/test-net-connect-options-port.js | 237 ++ .../parallel/test-net-dns-custom-lookup.js | 61 + .../test/parallel/test-net-dns-error.js | 48 + .../test/parallel/test-net-dns-lookup-skip.js | 26 + .../test/parallel/test-net-dns-lookup.js | 47 + .../test/parallel/test-net-during-close.js | 49 + .../test/parallel/test-net-eaddrinuse.js | 44 + .../test/parallel/test-net-end-close.js | 44 + .../test/parallel/test-net-end-destroyed.js | 33 + .../parallel/test-net-end-without-connect.js | 34 + .../test/parallel/test-net-isip.js | 103 + .../test/parallel/test-net-isipv4.js | 53 + .../test/parallel/test-net-isipv6.js | 251 ++ .../test-net-listen-after-destroying-stdin.js | 29 + ...n-close-server-callback-is-not-function.js | 18 + .../parallel/test-net-listen-close-server.js | 37 + .../test/parallel/test-net-listen-error.js | 36 + .../parallel/test-net-listen-invalid-port.js | 52 + .../test/parallel/test-net-listening.js | 23 + .../parallel/test-net-local-address-port.js | 50 + .../test/parallel/test-net-localerror.js | 51 + .../test/parallel/test-net-options-lookup.js | 55 + .../test-net-pause-resume-connecting.js | 102 + .../parallel/test-net-persistent-ref-unref.js | 48 + .../parallel/test-net-pipe-connect-errors.js | 104 + .../parallel/test-net-remote-address-port.js | 91 + ...t-net-server-call-listen-multiple-times.js | 56 + .../test-net-server-capture-rejection.js | 34 + .../test/parallel/test-net-server-close.js | 52 + .../test-net-server-listen-options-signal.js | 39 + .../test-net-server-listen-options.js | 101 + .../parallel/test-net-server-listen-path.js | 100 + .../test-net-server-listen-remove-callback.js | 51 + .../test-net-server-max-connections.js | 114 + .../test/parallel/test-net-server-options.js | 23 + .../test-net-server-pause-on-connect.js | 79 + .../parallel/test-net-server-try-ports.js | 54 + .../test-net-server-unref-persistent.js | 19 + .../test/parallel/test-net-server-unref.js | 37 + .../test-net-socket-close-after-end.js | 38 + .../test-net-socket-connect-without-cb.js | 33 + .../parallel/test-net-socket-connecting.js | 28 + .../parallel/test-net-socket-destroy-send.js | 31 + .../parallel/test-net-socket-destroy-twice.js | 43 + .../test-net-socket-end-before-connect.js | 20 + .../parallel/test-net-socket-end-callback.js | 29 + .../test-net-socket-no-halfopen-enforcer.js | 18 + .../test-net-socket-ready-without-cb.js | 27 + .../test/parallel/test-net-socket-timeout.js | 88 + .../test-net-socket-write-after-close.js | 49 + .../parallel/test-net-socket-write-error.js | 29 + .../test/parallel/test-net-sync-cork.js | 40 + .../parallel/test-net-timeout-no-handle.js | 24 + .../test/parallel/test-net-writable.js | 22 + .../parallel/test-net-write-after-end-nt.js | 39 + .../test/parallel/test-net-write-arguments.js | 46 + .../test-net-write-fully-async-buffer.js | 41 + .../test-net-write-fully-async-hex-string.js | 39 + .../test/parallel/test-net-write-slow.js | 70 + .../parallel/test-next-tick-doesnt-hang.js | 37 + .../test-next-tick-fixed-queue-regression.js | 25 + .../test-next-tick-intentional-starvation.js | 68 + .../test/parallel/test-next-tick-ordering.js | 62 + .../test/parallel/test-next-tick-ordering2.js | 46 + .../parallel/test-next-tick-when-exiting.js | 21 + .../test/parallel/test-next-tick.js | 70 + .../test/parallel/test-nodeeventtarget.js | 190 + .../node_compat/test/parallel/test-os.js | 278 ++ .../parallel/test-outgoing-message-destroy.js | 20 + .../parallel/test-outgoing-message-pipe.js | 22 + .../test/parallel/test-path-basename.js | 83 + .../test/parallel/test-path-dirname.js | 66 + .../test/parallel/test-path-extname.js | 106 + .../test/parallel/test-path-isabsolute.js | 35 + .../test/parallel/test-path-join.js | 150 + .../test/parallel/test-path-makelong.js | 94 + .../test/parallel/test-path-normalize.js | 79 + .../test/parallel/test-path-parse-format.js | 233 ++ .../test/parallel/test-path-posix-exists.js | 13 + .../test/parallel/test-path-relative.js | 76 + .../test/parallel/test-path-resolve.js | 96 + .../test/parallel/test-path-win32-exists.js | 13 + .../parallel/test-path-zero-length-strings.js | 46 + .../node_compat/test/parallel/test-path.js | 81 + .../test/parallel/test-process-beforeexit.js | 88 + ...ocess-binding-internalbinding-allowlist.js | 48 + .../test-process-env-allowed-flags.js | 109 + .../test-process-exit-from-before-exit.js | 37 + .../parallel/test-process-exit-handler.js | 21 + .../parallel/test-process-exit-recursive.js | 44 + .../test/parallel/test-process-exit.js | 42 + .../test/parallel/test-process-kill-pid.js | 116 + .../test/parallel/test-process-uptime.js | 44 + .../parallel/test-promise-unhandled-silent.js | 28 + .../test-promise-unhandled-throw-handler.js | 43 + .../test/parallel/test-punycode.js | 280 ++ .../test/parallel/test-querystring-escape.js | 48 + .../test-querystring-maxKeys-non-finite.js | 65 + .../test-querystring-multichar-separator.js | 32 + .../test/parallel/test-querystring.js | 490 +++ .../test/parallel/test-readline-csi.js | 183 + .../test-readline-emit-keypress-events.js | 79 + ...st-readline-interface-escapecodetimeout.js | 53 + .../test/parallel/test-readline-interface.js | 1217 +++++++ .../test/parallel/test-readline-keys.js | 351 ++ .../test/parallel/test-readline-position.js | 43 + .../parallel/test-readline-promises-csi.mjs | 233 ++ .../test-readline-promises-interface.js | 1149 ++++++ .../test/parallel/test-readline-reopen.js | 51 + .../parallel/test-readline-set-raw-mode.js | 97 + .../test-readline-undefined-columns.js | 53 + .../test/parallel/test-readline.js | 158 + .../parallel/test-stdin-from-file-spawn.js | 52 + .../parallel/test-stream-add-abort-signal.js | 34 + .../parallel/test-stream-aliases-legacy.js | 21 + .../parallel/test-stream-asIndexedPairs.mjs | 60 + .../test/parallel/test-stream-auto-destroy.js | 119 + ...riters-in-synchronously-recursion-write.js | 35 + .../test/parallel/test-stream-backpressure.js | 46 + .../test/parallel/test-stream-big-packet.js | 72 + .../test/parallel/test-stream-big-push.js | 81 + .../test/parallel/test-stream-buffer-list.js | 91 + .../parallel/test-stream-catch-rejections.js | 58 + .../test/parallel/test-stream-construct.js | 287 ++ .../test-stream-destroy-event-order.js | 31 + .../parallel/test-stream-duplex-destroy.js | 264 ++ .../test/parallel/test-stream-duplex-end.js | 48 + .../test/parallel/test-stream-duplex-from.js | 287 ++ .../test/parallel/test-stream-duplex-props.js | 38 + .../test-stream-duplex-readable-end.js | 36 + .../test-stream-duplex-writable-finished.js | 37 + .../test/parallel/test-stream-duplex.js | 140 + .../test/parallel/test-stream-end-paused.js | 57 + .../test/parallel/test-stream-error-once.js | 26 + .../parallel/test-stream-events-prepend.js | 33 + .../test/parallel/test-stream-inheritance.js | 70 + .../test/parallel/test-stream-ispaused.js | 51 + .../test-stream-objectmode-undefined.js | 51 + .../test-stream-once-readable-pipe.js | 68 + .../parallel/test-stream-pipe-after-end.js | 76 + ...t-stream-pipe-await-drain-manual-resume.js | 82 + ...tream-pipe-await-drain-push-while-write.js | 43 + .../parallel/test-stream-pipe-await-drain.js | 74 + .../test-stream-pipe-cleanup-pause.js | 44 + .../test/parallel/test-stream-pipe-cleanup.js | 132 + .../test-stream-pipe-error-handling.js | 131 + .../test/parallel/test-stream-pipe-event.js | 58 + .../test-stream-pipe-flow-after-unpipe.js | 36 + .../test/parallel/test-stream-pipe-flow.js | 97 + .../test-stream-pipe-manual-resume.js | 42 + .../test-stream-pipe-multiple-pipes.js | 58 + .../parallel/test-stream-pipe-needDrain.js | 38 + ...test-stream-pipe-same-destination-twice.js | 85 + .../test-stream-pipe-unpipe-streams.js | 103 + .../test-stream-pipe-without-listenerCount.js | 24 + .../test-stream-pipeline-async-iterator.js | 38 + ...t-stream-pipeline-queued-end-in-destroy.js | 46 + .../test-stream-pipeline-with-empty-string.js | 25 + .../test/parallel/test-stream-push-strings.js | 74 + .../parallel/test-stream-readable-aborted.js | 73 + ...t-stream-readable-add-chunk-during-data.js | 28 + ...stream-readable-constructor-set-methods.js | 18 + .../parallel/test-stream-readable-data.js | 26 + .../parallel/test-stream-readable-destroy.js | 412 +++ .../parallel/test-stream-readable-didRead.js | 118 + ...eam-readable-emit-readable-short-stream.js | 153 + .../test-stream-readable-emittedReadable.js | 80 + .../test-stream-readable-end-destroyed.js | 24 + .../parallel/test-stream-readable-ended.js | 53 + .../test-stream-readable-error-end.js | 22 + .../parallel/test-stream-readable-event.js | 135 + .../test-stream-readable-flow-recursion.js | 84 + .../test-stream-readable-hwm-0-async.js | 34 + ...test-stream-readable-hwm-0-no-flow-data.js | 111 + .../parallel/test-stream-readable-hwm-0.js | 37 + .../test-stream-readable-infinite-read.js | 39 + .../test-stream-readable-invalid-chunk.js | 41 + .../test-stream-readable-needReadable.js | 106 + .../test-stream-readable-next-no-null.js | 26 + ...st-stream-readable-no-unneeded-readable.js | 69 + ...stream-readable-object-multi-push-async.js | 190 + .../test-stream-readable-pause-and-resume.js | 81 + ...st-stream-readable-readable-then-resume.js | 38 + .../parallel/test-stream-readable-readable.js | 52 + ...est-stream-readable-reading-readingMore.js | 178 + .../test-stream-readable-resume-hwm.js | 28 + .../test-stream-readable-resumeScheduled.js | 72 + ...m-readable-setEncoding-existing-buffers.js | 67 + .../test-stream-readable-setEncoding-null.js | 22 + .../parallel/test-stream-readable-unshift.js | 177 + ...tream-readable-with-unimplemented-_read.js | 20 + .../test-stream-readableListening-state.js | 41 + .../parallel/test-stream-some-find-every.mjs | 179 + .../test-stream-transform-callback-twice.js | 21 + ...tream-transform-constructor-set-methods.js | 50 + .../parallel/test-stream-transform-destroy.js | 150 + .../test-stream-transform-final-sync.js | 117 + .../parallel/test-stream-transform-final.js | 119 + .../test-stream-transform-flush-data.js | 35 + ...tream-transform-objectmode-falsey-value.js | 58 + ...st-stream-transform-split-highwatermark.js | 80 + .../test-stream-transform-split-objectmode.js | 88 + .../test/parallel/test-stream-uint8array.js | 108 + .../test/parallel/test-stream-unpipe-event.js | 92 + .../test-stream-unshift-empty-chunk.js | 87 + .../parallel/test-stream-unshift-read-race.js | 135 + ...stream-writable-change-default-encoding.js | 85 + .../test-stream-writable-clear-buffer.js | 42 + ...stream-writable-constructor-set-methods.js | 48 + .../test-stream-writable-decoded-encoding.js | 65 + .../parallel/test-stream-writable-destroy.js | 496 +++ .../test-stream-writable-end-cb-error.js | 85 + .../test-stream-writable-end-multiple.js | 29 + .../test-stream-writable-ended-state.js | 39 + .../test-stream-writable-finish-destroyed.js | 50 + .../test-stream-writable-finished-state.js | 29 + .../parallel/test-stream-writable-finished.js | 106 + .../test-stream-writable-invalid-chunk.js | 43 + .../test-stream-writable-needdrain-state.js | 32 + .../parallel/test-stream-writable-null.js | 54 + .../test-stream-writable-properties.js | 29 + .../parallel/test-stream-writable-writable.js | 55 + .../test-stream-writable-write-cb-error.js | 65 + .../test-stream-writable-write-cb-twice.js | 59 + .../test-stream-writable-write-error.js | 82 + ...est-stream-writable-write-writev-finish.js | 159 + .../test-stream-writableState-ending.js | 44 + ...ableState-uncorked-bufferedRequestCount.js | 64 + .../parallel/test-stream-write-destroy.js | 69 + .../test/parallel/test-stream-write-drain.js | 23 + .../test/parallel/test-stream-write-final.js | 31 + .../test/parallel/test-stream-writev.js | 137 + ...est-stream2-base64-single-char-read-end.js | 63 + .../test/parallel/test-stream2-basic.js | 452 +++ .../parallel/test-stream2-compatibility.js | 77 + .../parallel/test-stream2-decode-partial.js | 30 + .../test/parallel/test-stream2-finish-pipe.js | 51 + .../parallel/test-stream2-large-read-stall.js | 81 + .../test/parallel/test-stream2-objects.js | 304 ++ .../test-stream2-pipe-error-handling.js | 113 + .../test-stream2-pipe-error-once-listener.js | 60 + .../test/parallel/test-stream2-push.js | 143 + .../parallel/test-stream2-read-sync-stack.js | 53 + ...st-stream2-readable-empty-buffer-no-eof.js | 124 + .../test-stream2-readable-from-list.js | 108 + .../test-stream2-readable-legacy-drain.js | 62 + .../test-stream2-readable-non-empty-end.js | 79 + .../test-stream2-readable-wrap-destroy.js | 34 + .../test-stream2-readable-wrap-empty.js | 45 + .../test-stream2-readable-wrap-error.js | 44 + .../parallel/test-stream2-readable-wrap.js | 107 + .../parallel/test-stream2-set-encoding.js | 330 ++ .../test/parallel/test-stream2-transform.js | 477 +++ .../parallel/test-stream2-unpipe-drain.js | 79 + .../test/parallel/test-stream2-unpipe-leak.js | 80 + .../test/parallel/test-stream2-writable.js | 466 +++ .../test/parallel/test-stream3-cork-end.js | 98 + .../test/parallel/test-stream3-cork-uncork.js | 93 + .../parallel/test-stream3-pause-then-read.js | 177 + .../parallel/test-streams-highwatermark.js | 94 + .../test/parallel/test-timers-api-refs.js | 28 + .../test/parallel/test-timers-args.js | 38 + ...-timers-clear-null-does-not-throw-error.js | 18 + ...imers-clear-object-does-not-throw-error.js | 15 + ...imers-clear-timeout-interval-equivalent.js | 25 + .../parallel/test-timers-clearImmediate.js | 20 + .../parallel/test-timers-interval-throw.js | 24 + .../parallel/test-timers-non-integer-delay.js | 88 + .../test/parallel/test-timers-refresh.js | 109 + ...-timers-same-timeout-wrong-list-deleted.js | 41 + .../test-timers-timeout-with-non-integer.js | 22 + .../test-timers-uncaught-exception.js | 46 + .../test-timers-unref-throw-then-ref.js | 26 + .../test/parallel/test-timers-user-call.js | 47 + .../test/parallel/test-timers-zero-timeout.js | 56 + .../test/parallel/test-tls-connect-simple.js | 69 + .../parallel/test-url-domain-ascii-unicode.js | 38 + .../test/parallel/test-url-fileurltopath.js | 161 + .../parallel/test-url-format-invalid-input.js | 34 + .../test/parallel/test-url-format-whatwg.js | 149 + .../test/parallel/test-url-format.js | 284 ++ .../test/parallel/test-url-parse-format.js | 1053 ++++++ .../parallel/test-url-parse-invalid-input.js | 83 + .../test/parallel/test-url-parse-query.js | 97 + .../test/parallel/test-url-pathtofileurl.js | 153 + .../test/parallel/test-url-relative.js | 441 +++ .../test/parallel/test-url-urltooptions.js | 45 + .../test-util-deprecate-invalid-code.js | 21 + .../test/parallel/test-util-deprecate.js | 64 + .../test/parallel/test-util-format.js | 504 +++ .../test/parallel/test-util-inherits.js | 117 + .../test-util-inspect-long-running.js | 27 + .../parallel/test-util-inspect-namespace.js | 28 + .../test/parallel/test-util-inspect-proxy.js | 172 + .../test/parallel/test-util-inspect.js | 3200 +++++++++++++++++ .../parallel/test-util-isDeepStrictEqual.js | 608 ++++ .../test/parallel/test-util-promisify.js | 219 ++ .../test/parallel/test-util-types-exists.js | 13 + .../test/parallel/test-util-types.js | 306 ++ .../node_compat/test/parallel/test-util.js | 203 ++ .../test/parallel/test-vm-static-this.js | 72 + .../parallel/test-webcrypto-sign-verify.js | 154 + .../test-whatwg-encoding-custom-api-basics.js | 68 + ...-whatwg-encoding-custom-fatal-streaming.js | 68 + ...hatwg-encoding-custom-textdecoder-fatal.js | 91 + ...g-encoding-custom-textdecoder-ignorebom.js | 37 + ...g-encoding-custom-textdecoder-streaming.js | 45 + ...ing-custom-textdecoder-utf16-surrogates.js | 63 + ...ents-add-event-listener-options-passive.js | 72 + ...vents-add-event-listener-options-signal.js | 166 + .../test-whatwg-events-customevent.js | 40 + .../test-whatwg-url-custom-deepequal.js | 25 + .../test-whatwg-url-custom-domainto.js | 64 + .../parallel/test-whatwg-url-custom-global.js | 33 + ...test-whatwg-url-custom-href-side-effect.js | 22 + .../test-whatwg-url-custom-inspect.js | 75 + .../test-whatwg-url-custom-parsing.js | 87 + .../test-whatwg-url-custom-setters.js | 67 + .../test-whatwg-url-custom-tostringtag.js | 39 + .../test-whatwg-url-override-hostname.js | 27 + .../parallel/test-whatwg-url-properties.js | 111 + .../test/parallel/test-whatwg-url-toascii.js | 93 + .../parallel/test-zlib-close-after-error.js | 23 + .../parallel/test-zlib-close-after-write.js | 37 + .../parallel/test-zlib-convenience-methods.js | 144 + .../test-zlib-deflate-raw-inherits.js | 34 + .../test/parallel/test-zlib-destroy-pipe.js | 28 + .../test/parallel/test-zlib-empty-buffer.js | 35 + .../test/parallel/test-zlib-from-string.js | 90 + .../test/parallel/test-zlib-invalid-input.js | 68 + .../test/parallel/test-zlib-no-stream.js | 21 + .../parallel/test-zlib-random-byte-pipes.js | 166 + .../test/parallel/test-zlib-sync-no-event.js | 26 + .../test/parallel/test-zlib-truncated.js | 71 + .../test-zlib-unzip-one-byte-chunks.js | 37 + .../parallel/test-zlib-write-after-end.js | 23 + .../parallel/test-zlib-write-after-flush.js | 57 + .../test/parallel/test-zlib-zero-byte.js | 53 + .../parallel/test-zlib-zero-windowBits.js | 41 + .../test/pseudo-tty/console-dumb-tty.js | 16 + .../test/pseudo-tty/console_colors.js | 29 + .../test/pseudo-tty/no_dropped_stdio.js | 26 + .../test/pseudo-tty/no_interleaved_stdio.js | 28 + .../node_compat/test/pseudo-tty/package.json | 1 + .../test-tty-color-support-warning-2.js | 15 + .../test-tty-color-support-warning.js | 16 + .../test/pseudo-tty/test-tty-stdin-end.js | 14 + .../test/pseudo-tty/test-tty-stdout-end.js | 11 + .../node_compat/test/pummel/package.json | 1 + ...t-net-bytes-per-incoming-chunk-overhead.js | 58 + .../test/pummel/test-net-pingpong-delay.js | 114 + .../test/pummel/test-net-write-callbacks.js | 80 + .../node_compat/test/sequential/package.json | 1 + .../sequential/test-child-process-exit.js | 69 + 686 files changed, 65427 insertions(+), 7 deletions(-) create mode 100644 cli/tests/node_compat/test/common/child_process.js create mode 100644 cli/tests/node_compat/test/common/countdown.js create mode 100644 cli/tests/node_compat/test/common/dns.js create mode 100644 cli/tests/node_compat/test/common/duplexpair.js create mode 100644 cli/tests/node_compat/test/common/fixtures.js create mode 100644 cli/tests/node_compat/test/common/hijackstdio.js create mode 100644 cli/tests/node_compat/test/common/index.mjs create mode 100644 cli/tests/node_compat/test/common/internet.js create mode 100644 cli/tests/node_compat/test/fixtures/GH-1899-output.js create mode 100644 cli/tests/node_compat/test/fixtures/a.js create mode 100644 cli/tests/node_compat/test/fixtures/child-process-spawn-node.js create mode 100644 cli/tests/node_compat/test/fixtures/child_process_should_emit_error.js create mode 100644 cli/tests/node_compat/test/fixtures/echo.js create mode 100644 cli/tests/node_compat/test/fixtures/elipses.txt create mode 100644 cli/tests/node_compat/test/fixtures/empty.txt create mode 100644 cli/tests/node_compat/test/fixtures/exit.js create mode 100644 cli/tests/node_compat/test/fixtures/keys/agent1-cert.pem create mode 100644 cli/tests/node_compat/test/fixtures/keys/agent1-key.pem create mode 100644 cli/tests/node_compat/test/fixtures/loop.js create mode 100644 cli/tests/node_compat/test/fixtures/package.json create mode 100644 cli/tests/node_compat/test/fixtures/print-chars.js create mode 100644 cli/tests/node_compat/test/fixtures/x.txt create mode 100644 cli/tests/node_compat/test/internet/package.json create mode 100644 cli/tests/node_compat/test/internet/test-dgram-connect.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-any.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-idna2008.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-ipv4.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-ipv6.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-lookup.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-promises-resolve.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-regress-6244.js create mode 100644 cli/tests/node_compat/test/internet/test-dns-setserver-in-callback-of-resolve4.js create mode 100644 cli/tests/node_compat/test/internet/test-dns.js create mode 100644 cli/tests/node_compat/test/parallel/test-assert-async.js create mode 100644 cli/tests/node_compat/test/parallel/test-assert.js create mode 100644 cli/tests/node_compat/test/parallel/test-bad-unicode.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-alloc.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-arraybuffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-ascii.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-badhex.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-bigint64.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-bytelength.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-compare-offset.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-concat.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-constants.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-copy.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-equals.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-failed-alloc-typed-arrays.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-fakes.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-from.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-includes.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-indexof.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-inheritance.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-isencoding.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-iterator.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-new.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-no-negative-allocation.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-nopendingdep-map.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-of-no-deprecation.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-over-max-length.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-parent-property.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-read.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-readdouble.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-readfloat.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-readint.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-readuint.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-safe-unsafe.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-sharedarraybuffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-slice.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-slow.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-swap.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-tojson.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-tostring-range.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-tostring-rangeerror.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-tostring.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-write.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-writedouble.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-writefloat.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-writeint.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-writeuint.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-zero-fill-cli.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-zero-fill-reset.js create mode 100644 cli/tests/node_compat/test/parallel/test-buffer-zero-fill.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-can-write-to-stdout.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-default-options.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-double-pipe.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-abortcontroller-promisified.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-cwd.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-encoding.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-env.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-kill-throws.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-maxbuf.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-std-encoding.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-stdout-stderr-data-string.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-expire.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-kill.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-not-expired.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-execFile-promisified-abortController.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-execfile-maxbuf.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-execfile.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-execfilesync-maxbuf.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-execsync-maxbuf.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-exit-code.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-flush-stdio.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-ipc.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-kill.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-set-blocking.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-spawn-args.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-spawn-event.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-spawnsync-args.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-spawnsync-env.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-spawnsync-maxbuf.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-spawnsync-validation-errors.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-spawnsync.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-stdio-inherit.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-stdout-flush-exit.js create mode 100644 cli/tests/node_compat/test/parallel/test-child-process-stdout-flush.js create mode 100644 cli/tests/node_compat/test/parallel/test-client-request-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-async-write-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-group.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-instance.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-log-stdio-broken-dest.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-log-throw-primitive.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-no-swallow-stack-overflow.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-sync-write-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-table.js create mode 100644 cli/tests/node_compat/test/parallel/test-console-tty-colors.js create mode 100644 cli/tests/node_compat/test/parallel/test-crypto-hmac.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-address.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-bind-default-address.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-bind.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-bytes-length.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-close-during-bind.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-close-in-listening.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-close-is-not-callback.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-close-signal.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer-length.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-multi-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-default-host.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-array.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-packet.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-buffer-copy.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-string-array.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-connect.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-createSocket-type.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-custom-lookup.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-error-message-address.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-implicit-bind.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-ipv6only.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-listen-after-bind.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-msgsize.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-oob-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-recv-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-bad-arguments.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-empty-address.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-callback-recursive.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-cb-quelches-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-default-host.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-empty-array.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-invalid-msg-type.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-multi-buffer-copy.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-send-multi-string-array.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-socket-buffer-size.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-udp4.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-udp6-link-local-address.js create mode 100644 cli/tests/node_compat/test/parallel/test-dgram-udp6-send-default-host.js create mode 100644 cli/tests/node_compat/test/parallel/test-diagnostics-channel-has-subscribers.js create mode 100644 cli/tests/node_compat/test/parallel/test-diagnostics-channel-net.js create mode 100644 cli/tests/node_compat/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js create mode 100644 cli/tests/node_compat/test/parallel/test-diagnostics-channel-pub-sub.js create mode 100644 cli/tests/node_compat/test/parallel/test-diagnostics-channel-symbol-named.js create mode 100644 cli/tests/node_compat/test/parallel/test-diagnostics-channel-udp.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns-lookup.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns-memory-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns-multi-channel.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns-promises-exists.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns-resolveany.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns-resolvens-typeerror.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns-setservers-type-check.js create mode 100644 cli/tests/node_compat/test/parallel/test-dns.js create mode 100644 cli/tests/node_compat/test/parallel/test-eval-strict-referenceerror.js create mode 100644 cli/tests/node_compat/test/parallel/test-eval.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-add-listeners.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-check-listener-leaks.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-emit-context.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-error-monitor.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-errors.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-get-max-listeners.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-invalid-listener.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-listener-count.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-listeners-side-effects.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-listeners.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-null.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-method-names.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-modify-in-emit.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-no-error-provided-to-error-event.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-num-args.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-once.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-prepend.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-remove-all-listeners.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-remove-listeners.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-set-max-listeners-side-effects.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-special-event-names.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-subclass.js create mode 100644 cli/tests/node_compat/test/parallel/test-event-emitter-symbols.js create mode 100644 cli/tests/node_compat/test/parallel/test-events-list.js create mode 100644 cli/tests/node_compat/test/parallel/test-events-on-async-iterator.js create mode 100644 cli/tests/node_compat/test/parallel/test-events-once.js create mode 100644 cli/tests/node_compat/test/parallel/test-events-uncaught-exception-stack.js create mode 100644 cli/tests/node_compat/test/parallel/test-eventtarget-brandcheck.js create mode 100644 cli/tests/node_compat/test/parallel/test-exception-handler.js create mode 100644 cli/tests/node_compat/test/parallel/test-exception-handler2.js create mode 100644 cli/tests/node_compat/test/parallel/test-file-read-noexist.js create mode 100644 cli/tests/node_compat/test/parallel/test-file-write-stream.js create mode 100644 cli/tests/node_compat/test/parallel/test-file-write-stream2.js create mode 100644 cli/tests/node_compat/test/parallel/test-file-write-stream3.js create mode 100644 cli/tests/node_compat/test/parallel/test-file-write-stream4.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-access.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-append-file-sync.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-append-file.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-chmod-mask.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-chmod.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-chown-type-check.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-copyfile.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-empty-readStream.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-mkdir.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-open-flags.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-open-mode-mask.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-open-no-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-open-numeric-flags.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-open.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-opendir.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-autoClose.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-concurrent-reads.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-double-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-encoding.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-fd.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-inherit.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-patch-open.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-resume.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream-throw-type-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-stream.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-type.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read-zero-length.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-read.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-readdir-stack-overflow.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-readdir.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-readfile-empty.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-realpath-native.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rm.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-not-found.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-on-file.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-not-found.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-on-file.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-not-found.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-on-file.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-rmdir-type-check.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-watch.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-watchfile.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-file-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-file-invalid-path.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-file-sync.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-file.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-no-fd.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-stream-autoclose-option.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-stream-close-without-callback.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-stream-double-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-stream-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-stream-fs.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-stream-throw-type-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-stream.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write-sync.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-write.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-writev-sync.js create mode 100644 cli/tests/node_compat/test/parallel/test-fs-writev.js create mode 100644 cli/tests/node_compat/test/parallel/test-handle-wrap-close-abort.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-agent-getname.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-client-get-url.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-client-read-in-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-finish-writable.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-getter.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-setter.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headers.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-message-inheritance.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-renderHeaders.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-outgoing-settimeout.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-url.parse-auth.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-url.parse-path.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-url.parse-post.js create mode 100644 cli/tests/node_compat/test/parallel/test-http-url.parse-search.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-access-byteswritten.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-after-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-allow-half-open.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen-path.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-better-error-messages-path.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-better-error-messages-port-hostname.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-bind-twice.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-buffersize.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-bytes-written-large.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-can-reset-timeout.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-after-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-buffer2.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-call-socket-connect.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-immediate-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-immediate-finish.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-no-arg.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-options-ipv6.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-connect-options-port.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-dns-custom-lookup.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-dns-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-dns-lookup-skip.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-dns-lookup.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-during-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-eaddrinuse.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-end-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-end-destroyed.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-end-without-connect.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-isip.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-isipv4.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-isipv6.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-listen-after-destroying-stdin.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-listen-close-server-callback-is-not-function.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-listen-close-server.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-listen-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-listen-invalid-port.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-listening.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-local-address-port.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-localerror.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-options-lookup.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-pause-resume-connecting.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-persistent-ref-unref.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-pipe-connect-errors.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-remote-address-port.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-call-listen-multiple-times.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-capture-rejection.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-listen-options-signal.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-listen-options.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-listen-path.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-listen-remove-callback.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-max-connections.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-options.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-pause-on-connect.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-try-ports.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-unref-persistent.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-server-unref.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-close-after-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-connect-without-cb.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-connecting.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-destroy-send.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-destroy-twice.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-end-before-connect.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-end-callback.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-no-halfopen-enforcer.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-ready-without-cb.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-timeout.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-write-after-close.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-socket-write-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-sync-cork.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-timeout-no-handle.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-writable.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-write-after-end-nt.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-write-arguments.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-write-fully-async-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-write-fully-async-hex-string.js create mode 100644 cli/tests/node_compat/test/parallel/test-net-write-slow.js create mode 100644 cli/tests/node_compat/test/parallel/test-next-tick-doesnt-hang.js create mode 100644 cli/tests/node_compat/test/parallel/test-next-tick-fixed-queue-regression.js create mode 100644 cli/tests/node_compat/test/parallel/test-next-tick-intentional-starvation.js create mode 100644 cli/tests/node_compat/test/parallel/test-next-tick-ordering.js create mode 100644 cli/tests/node_compat/test/parallel/test-next-tick-ordering2.js create mode 100644 cli/tests/node_compat/test/parallel/test-next-tick-when-exiting.js create mode 100644 cli/tests/node_compat/test/parallel/test-next-tick.js create mode 100644 cli/tests/node_compat/test/parallel/test-nodeeventtarget.js create mode 100644 cli/tests/node_compat/test/parallel/test-os.js create mode 100644 cli/tests/node_compat/test/parallel/test-outgoing-message-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-outgoing-message-pipe.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-basename.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-dirname.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-extname.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-isabsolute.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-join.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-makelong.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-normalize.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-parse-format.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-posix-exists.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-relative.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-resolve.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-win32-exists.js create mode 100644 cli/tests/node_compat/test/parallel/test-path-zero-length-strings.js create mode 100644 cli/tests/node_compat/test/parallel/test-path.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-beforeexit.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-binding-internalbinding-allowlist.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-env-allowed-flags.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-exit-from-before-exit.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-exit-handler.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-exit-recursive.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-exit.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-kill-pid.js create mode 100644 cli/tests/node_compat/test/parallel/test-process-uptime.js create mode 100644 cli/tests/node_compat/test/parallel/test-promise-unhandled-silent.js create mode 100644 cli/tests/node_compat/test/parallel/test-promise-unhandled-throw-handler.js create mode 100644 cli/tests/node_compat/test/parallel/test-punycode.js create mode 100644 cli/tests/node_compat/test/parallel/test-querystring-escape.js create mode 100644 cli/tests/node_compat/test/parallel/test-querystring-maxKeys-non-finite.js create mode 100644 cli/tests/node_compat/test/parallel/test-querystring-multichar-separator.js create mode 100644 cli/tests/node_compat/test/parallel/test-querystring.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-csi.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-emit-keypress-events.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-interface-escapecodetimeout.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-interface.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-keys.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-position.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-promises-csi.mjs create mode 100644 cli/tests/node_compat/test/parallel/test-readline-promises-interface.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-reopen.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-set-raw-mode.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline-undefined-columns.js create mode 100644 cli/tests/node_compat/test/parallel/test-readline.js create mode 100644 cli/tests/node_compat/test/parallel/test-stdin-from-file-spawn.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-add-abort-signal.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-aliases-legacy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-asIndexedPairs.mjs create mode 100644 cli/tests/node_compat/test/parallel/test-stream-auto-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-backpressure.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-big-packet.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-big-push.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-buffer-list.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-catch-rejections.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-construct.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-destroy-event-order.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-duplex-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-duplex-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-duplex-from.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-duplex-props.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-duplex-readable-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-duplex-writable-finished.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-duplex.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-end-paused.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-error-once.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-events-prepend.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-inheritance.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-ispaused.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-objectmode-undefined.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-once-readable-pipe.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-after-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-manual-resume.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-push-while-write.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup-pause.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-error-handling.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-event.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-flow-after-unpipe.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-flow.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-manual-resume.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-multiple-pipes.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-needDrain.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-same-destination-twice.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-unpipe-streams.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipe-without-listenerCount.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipeline-async-iterator.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipeline-queued-end-in-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-pipeline-with-empty-string.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-push-strings.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-aborted.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-add-chunk-during-data.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-constructor-set-methods.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-data.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-didRead.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-emit-readable-short-stream.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-emittedReadable.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-end-destroyed.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-ended.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-error-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-event.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-flow-recursion.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-async.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-no-flow-data.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-infinite-read.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-invalid-chunk.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-needReadable.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-next-no-null.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-no-unneeded-readable.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-object-multi-push-async.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-pause-and-resume.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-readable-then-resume.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-readable.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-reading-readingMore.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-resume-hwm.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-resumeScheduled.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-existing-buffers.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-null.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-unshift.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readable-with-unimplemented-_read.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-readableListening-state.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-some-find-every.mjs create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-callback-twice.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-constructor-set-methods.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-final-sync.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-final.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-flush-data.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-objectmode-falsey-value.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-split-highwatermark.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-transform-split-objectmode.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-uint8array.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-unpipe-event.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-unshift-empty-chunk.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-unshift-read-race.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-change-default-encoding.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-clear-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-constructor-set-methods.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-decoded-encoding.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-end-cb-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-end-multiple.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-ended-state.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-finish-destroyed.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-finished-state.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-finished.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-invalid-chunk.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-needdrain-state.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-null.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-properties.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-writable.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-twice.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-write-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writable-write-writev-finish.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writableState-ending.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-write-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-write-drain.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-write-final.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream-writev.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-base64-single-char-read-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-basic.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-compatibility.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-decode-partial.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-finish-pipe.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-large-read-stall.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-objects.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-pipe-error-handling.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-pipe-error-once-listener.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-push.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-read-sync-stack.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-empty-buffer-no-eof.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-from-list.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-legacy-drain.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-non-empty-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-destroy.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-empty.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-readable-wrap.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-set-encoding.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-transform.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-unpipe-drain.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-unpipe-leak.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream2-writable.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream3-cork-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream3-cork-uncork.js create mode 100644 cli/tests/node_compat/test/parallel/test-stream3-pause-then-read.js create mode 100644 cli/tests/node_compat/test/parallel/test-streams-highwatermark.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-api-refs.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-args.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-clear-null-does-not-throw-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-clear-object-does-not-throw-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-clear-timeout-interval-equivalent.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-clearImmediate.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-interval-throw.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-non-integer-delay.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-refresh.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-same-timeout-wrong-list-deleted.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-timeout-with-non-integer.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-uncaught-exception.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-unref-throw-then-ref.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-user-call.js create mode 100644 cli/tests/node_compat/test/parallel/test-timers-zero-timeout.js create mode 100644 cli/tests/node_compat/test/parallel/test-tls-connect-simple.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-domain-ascii-unicode.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-fileurltopath.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-format-invalid-input.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-format-whatwg.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-format.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-parse-format.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-parse-invalid-input.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-parse-query.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-pathtofileurl.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-relative.js create mode 100644 cli/tests/node_compat/test/parallel/test-url-urltooptions.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-deprecate-invalid-code.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-deprecate.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-format.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-inherits.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-inspect-long-running.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-inspect-namespace.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-inspect-proxy.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-inspect.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-isDeepStrictEqual.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-promisify.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-types-exists.js create mode 100644 cli/tests/node_compat/test/parallel/test-util-types.js create mode 100644 cli/tests/node_compat/test/parallel/test-util.js create mode 100644 cli/tests/node_compat/test/parallel/test-vm-static-this.js create mode 100644 cli/tests/node_compat/test/parallel/test-webcrypto-sign-verify.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-api-basics.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-passive.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-signal.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-events-customevent.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-deepequal.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-domainto.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-global.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-href-side-effect.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-inspect.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-parsing.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-setters.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-custom-tostringtag.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-override-hostname.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-properties.js create mode 100644 cli/tests/node_compat/test/parallel/test-whatwg-url-toascii.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-close-after-error.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-close-after-write.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-convenience-methods.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-deflate-raw-inherits.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-destroy-pipe.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-empty-buffer.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-from-string.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-invalid-input.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-no-stream.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-random-byte-pipes.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-sync-no-event.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-truncated.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-unzip-one-byte-chunks.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-write-after-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-write-after-flush.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-zero-byte.js create mode 100644 cli/tests/node_compat/test/parallel/test-zlib-zero-windowBits.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/console-dumb-tty.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/console_colors.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/no_dropped_stdio.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/no_interleaved_stdio.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/package.json create mode 100644 cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning-2.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/test-tty-stdin-end.js create mode 100644 cli/tests/node_compat/test/pseudo-tty/test-tty-stdout-end.js create mode 100644 cli/tests/node_compat/test/pummel/package.json create mode 100644 cli/tests/node_compat/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js create mode 100644 cli/tests/node_compat/test/pummel/test-net-pingpong-delay.js create mode 100644 cli/tests/node_compat/test/pummel/test-net-write-callbacks.js create mode 100644 cli/tests/node_compat/test/sequential/package.json create mode 100644 cli/tests/node_compat/test/sequential/test-child-process-exit.js diff --git a/.dprint.json b/.dprint.json index f2e243076599ba..422cd41e5ac258 100644 --- a/.dprint.json +++ b/.dprint.json @@ -22,6 +22,7 @@ "cli/bench/testdata/express-router.js", "cli/bench/testdata/npm/*", "cli/tsc/dts/*.d.ts", + "cli/tests/node_compat/test", "cli/tests/testdata/fmt/badly_formatted.json", "cli/tests/testdata/fmt/badly_formatted.md", "cli/tests/testdata/byte_order_mark.ts", diff --git a/cli/tests/node_compat/config.json b/cli/tests/node_compat/config.json index e032c699dbcbae..5bf150d9bb0fc1 100644 --- a/cli/tests/node_compat/config.json +++ b/cli/tests/node_compat/config.json @@ -1,13 +1,847 @@ { "nodeVersion": "18.12.1", - "ignore": {}, + "ignore": { + "common": ["index.js", "internet.js", "tmpdir.js"], + "fixtures": [ + "child-process-spawn-node.js", + "echo.js", + "elipses.txt", + "empty.txt", + "exit.js", + "print-chars.js", + "x.txt" + ], + "internet": [ + "test-dns-any.js", + "test-dns-ipv4.js", + "test-dns-ipv6.js", + "test-dns.js" + ], + "parallel": [ + "test-assert.js", + "test-buffer-alloc.js", + "test-buffer-arraybuffer.js", + "test-buffer-bytelength.js", + "test-buffer-from.js", + "test-buffer-includes.js", + "test-buffer-indexof.js", + "test-child-process-exec-abortcontroller-promisified.js", + "test-child-process-exec-encoding.js", + "test-child-process-exec-kill-throws.js", + "test-child-process-exec-maxbuf.js", + "test-child-process-exec-std-encoding.js", + "test-child-process-exec-timeout-expire.js", + "test-child-process-exec-timeout-kill.js", + "test-child-process-exec-timeout-not-expired.js", + "test-child-process-execFile-promisified-abortController.js", + "test-child-process-execfile.js", + "test-child-process-execsync-maxbuf.js", + "test-child-process-exit-code.js", + "test-child-process-ipc.js", + "test-child-process-spawnsync-env.js", + "test-child-process-stdio-inherit.js", + "test-child-process-stdout-flush-exit.js", + "test-child-process-stdout-flush.js", + "test-console-instance.js", + "test-crypto-hmac.js", + "test-dgram-custom-lookup.js", + "test-dgram-ipv6only.js", + "test-dgram-send-cb-quelches-error.js", + "test-dgram-socket-buffer-size.js", + "test-dgram-udp6-link-local-address.js", + "test-dns-lookup.js", + "test-dns-resolveany.js", + "test-dns.js", + "test-event-emitter-max-listeners.js", + "test-event-emitter-no-error-provided-to-error-event.js", + "test-event-emitter-prepend.js", + "test-events-once.js", + "test-fs-append-file.js", + "test-fs-chmod-mask.js", + "test-fs-chmod.js", + "test-fs-mkdir.js", + "test-fs-open.js", + "test-fs-opendir.js", + "test-fs-rmdir-recursive.js", + "test-fs-write-file.js", + "test-fs-write.js", + "test-net-better-error-messages-path.js", + "test-net-connect-buffer.js", + "test-net-connect-buffer2.js", + "test-net-end-close.js", + "test-net-listen-invalid-port.js", + "test-net-server-call-listen-multiple-times.js", + "test-net-server-listen-path.js", + "test-net-server-try-ports.js", + "test-net-socket-timeout.js", + "test-net-write-arguments.js", + "test-os.js", + "test-path-resolve.js", + "test-path.js", + "test-querystring.js", + "test-readline-interface.js", + "test-stdin-from-file-spawn.js", + "test-url-urltooptions.js", + "test-util-format.js", + "test-util-inspect-namespace.js", + "test-util-inspect-proxy.js", + "test-util-inspect.js", + "test-util-isDeepStrictEqual.js", + "test-util-promisify.js", + "test-util-types.js", + "test-util.js", + "test-webcrypto-sign-verify.js", + "test-whatwg-url-properties.js", + "test-zlib-convenience-methods.js", + "test-zlib-empty-buffer.js", + "test-zlib-invalid-input.js", + "test-zlib-random-byte-pipes.js", + "test-zlib-write-after-flush.js", + "test-zlib-zero-byte.js", + "test-zlib-zero-windowBits.js" + ], + "pummel": [ + "test-net-bytes-per-incoming-chunk-overhead.js", + "test-net-write-callbacks.js" + ], + "sequential": [ + "test-child-process-exit.js" + ] + }, "tests": { + "common": [ + "child_process.js", + "countdown.js", + "dns.js", + "duplexpair.js", + "fixtures.js", + "hijackstdio", + "index.mjs", + "internet.js" + ], + "fixtures": [ + "GH-1899-output.js", + "a.js", + "child-process-spawn-node.js", + "child_process_should_emit_error.js", + "echo.js", + "elipses.txt", + "empty.txt", + "exit.js", + "loop.js", + "print-chars.js", + "x.txt" + ], + "fixtures/keys": [ + "agent1-cert.pem", + "agent1-key.pem" + ], + "internet": [ + "TODO:test-dgram-connect.js", + "test-dns-any.js", + "test-dns-idna2008.js", + "test-dns-ipv4.js", + "test-dns-ipv6.js", + "test-dns-lookup.js", + "test-dns-promises-resolve.js", + "test-dns-regress-6244.js", + "test-dns-setserver-in-callback-of-resolve4.js", + "test-dns.js" + ], "parallel": [ + "test-assert-async.js", "test-assert-fail.js", "test-assert-strict-exists.js", - "test-btoa-atob.js" + "test-assert.js", + "test-bad-unicode.js", + "test-btoa-atob.js", + "TODO:test-buffer-alloc.js", + "test-buffer-arraybuffer.js", + "test-buffer-ascii.js", + "test-buffer-badhex.js", + "test-buffer-bigint64.js", + "test-buffer-bytelength.js", + "test-buffer-compare-offset.js", + "test-buffer-concat.js", + "test-buffer-constants.js", + "test-buffer-copy.js", + "test-buffer-equals.js", + "test-buffer-failed-alloc-typed-arrays.js", + "test-buffer-fakes.js", + "test-buffer-from.js", + "test-buffer-includes.js", + "TODO:test-buffer-indexof.js", + "test-buffer-inheritance.js", + "test-buffer-isencoding.js", + "test-buffer-iterator.js", + "test-buffer-new.js", + "test-buffer-no-negative-allocation.js", + "test-buffer-nopendingdep-map.js", + "test-buffer-of-no-deprecation.js", + "test-buffer-over-max-length.js", + "test-buffer-parent-property.js", + "test-buffer-read.js", + "test-buffer-readdouble.js", + "test-buffer-readfloat.js", + "test-buffer-readint.js", + "test-buffer-readuint.js", + "test-buffer-safe-unsafe.js", + "TODO:test-buffer-sharedarraybuffer.js", + "test-buffer-slice.js", + "test-buffer-slow.js", + "test-buffer-swap.js", + "test-buffer-tojson.js", + "test-buffer-tostring-range.js", + "test-buffer-tostring-rangeerror.js", + "test-buffer-tostring.js", + "TODO:test-buffer-write.js", + "test-buffer-writedouble.js", + "test-buffer-writefloat.js", + "test-buffer-writeint.js", + "test-buffer-writeuint.js", + "test-buffer-zero-fill-cli.js", + "test-buffer-zero-fill-reset.js", + "test-buffer-zero-fill.js", + "test-child-process-can-write-to-stdout.js", + "test-child-process-default-options.js", + "test-child-process-double-pipe.js", + "test-child-process-exec-abortcontroller-promisified.js", + "test-child-process-exec-cwd.js", + "TODO:test-child-process-exec-encoding.js", + "test-child-process-exec-env.js", + "test-child-process-exec-error.js", + "test-child-process-exec-kill-throws.js", + "test-child-process-exec-maxbuf.js", + "TODO:test-child-process-exec-std-encoding.js", + "test-child-process-exec-stdout-stderr-data-string.js", + "test-child-process-exec-timeout-expire.js", + "test-child-process-exec-timeout-kill.js", + "TODO:test-child-process-exec-timeout-not-expired.js", + "test-child-process-execFile-promisified-abortController.js", + "test-child-process-execfile-maxbuf.js", + "TODO:test-child-process-execfile.js", + "test-child-process-execfilesync-maxbuf.js", + "test-child-process-execsync-maxbuf.js", + "TODO:test-child-process-exit-code.js", + "test-child-process-flush-stdio.js", + "TODO:test-child-process-ipc.js", + "test-child-process-kill.js", + "test-child-process-set-blocking.js", + "test-child-process-spawn-args.js", + "test-child-process-spawn-event.js", + "test-child-process-spawnsync-args.js", + "TODO:test-child-process-spawnsync-env.js", + "test-child-process-spawnsync-maxbuf.js", + "test-child-process-spawnsync-validation-errors.js", + "test-child-process-spawnsync.js", + "TODO:test-child-process-stdio-inherit.js", + "TODO:test-child-process-stdout-flush-exit.js", + "TODO:test-child-process-stdout-flush.js", + "test-client-request-destroy.js", + "test-console-async-write-error.js", + "test-console-group.js", + "TODO:test-console-instance.js", + "test-console-log-stdio-broken-dest.js", + "test-console-log-throw-primitive.js", + "test-console-no-swallow-stack-overflow.js", + "test-console-sync-write-error.js", + "test-console-table.js", + "test-console-tty-colors.js", + "test-crypto-hmac.js", + "TODO:test-dgram-address.js", + "TODO:test-dgram-bind-default-address.js", + "TODO:test-dgram-bind.js", + "TODO:test-dgram-bytes-length.js", + "test-dgram-close-during-bind.js", + "TODO:test-dgram-close-in-listening.js", + "TODO:test-dgram-close-is-not-callback.js", + "test-dgram-close-signal.js", + "TODO:test-dgram-close.js", + "TODO:test-dgram-connect-send-callback-buffer-length.js", + "TODO:test-dgram-connect-send-callback-buffer.js", + "TODO:test-dgram-connect-send-callback-multi-buffer.js", + "TODO:test-dgram-connect-send-default-host.js", + "TODO:test-dgram-connect-send-empty-array.js", + "TODO:test-dgram-connect-send-empty-buffer.js", + "TODO:test-dgram-connect-send-empty-packet.js", + "TODO:test-dgram-connect-send-multi-buffer-copy.js", + "TODO:test-dgram-connect-send-multi-string-array.js", + "TODO:test-dgram-connect.js", + "TODO:test-dgram-createSocket-type.js", + "TODO:test-dgram-custom-lookup.js", + "TODO:test-dgram-error-message-address.js", + "TODO:test-dgram-implicit-bind.js", + "TODO:test-dgram-ipv6only.js", + "TODO:test-dgram-listen-after-bind.js", + "TODO:test-dgram-msgsize.js", + "TODO:test-dgram-oob-buffer.js", + "TODO:test-dgram-recv-error.js", + "TODO:test-dgram-send-bad-arguments.js", + "TODO:test-dgram-send-callback-buffer-empty-address.js", + "TODO:test-dgram-send-callback-buffer-length-empty-address.js", + "TODO:test-dgram-send-callback-buffer-length.js", + "TODO:test-dgram-send-callback-buffer.js", + "TODO:test-dgram-send-callback-multi-buffer-empty-address.js", + "TODO:test-dgram-send-callback-multi-buffer.js", + "TODO:test-dgram-send-callback-recursive.js", + "TODO:test-dgram-send-cb-quelches-error.js", + "TODO:test-dgram-send-default-host.js", + "TODO:test-dgram-send-empty-array.js", + "TODO:test-dgram-send-empty-buffer.js", + "TODO:test-dgram-send-empty-packet.js", + "TODO:test-dgram-send-error.js", + "TODO:test-dgram-send-invalid-msg-type.js", + "TODO:test-dgram-send-multi-buffer-copy.js", + "TODO:test-dgram-send-multi-string-array.js", + "TODO:test-dgram-socket-buffer-size.js", + "TODO:test-dgram-udp4.js", + "TODO:test-dgram-udp6-link-local-address.js", + "TODO:test-dgram-udp6-send-default-host.js", + "test-diagnostics-channel-has-subscribers.js", + "TODO:test-diagnostics-channel-net.js", + "test-diagnostics-channel-object-channel-pub-sub.js", + "test-diagnostics-channel-pub-sub.js", + "test-diagnostics-channel-symbol-named.js", + "test-diagnostics-channel-udp.js", + "test-dns-lookup.js", + "test-dns-memory-error.js", + "TODO:test-dns-multi-channel.js", + "test-dns-promises-exists.js", + "TODO:test-dns-resolveany.js", + "test-dns-resolvens-typeerror.js", + "test-dns-setservers-type-check.js", + "TODO:test-dns.js", + "test-eval-strict-referenceerror.js", + "test-eval.js", + "test-event-emitter-add-listeners.js", + "TODO:test-event-emitter-check-listener-leaks.js", + "test-event-emitter-emit-context.js", + "test-event-emitter-error-monitor.js", + "test-event-emitter-errors.js", + "test-event-emitter-get-max-listeners.js", + "test-event-emitter-invalid-listener.js", + "test-event-emitter-listener-count.js", + "test-event-emitter-listeners-side-effects.js", + "test-event-emitter-listeners.js", + "TODO:test-event-emitter-max-listeners-warning-for-null.js", + "TODO:test-event-emitter-max-listeners-warning-for-symbol.js", + "TODO:test-event-emitter-max-listeners-warning.js", + "test-event-emitter-max-listeners.js", + "test-event-emitter-method-names.js", + "test-event-emitter-modify-in-emit.js", + "test-event-emitter-no-error-provided-to-error-event.js", + "test-event-emitter-num-args.js", + "test-event-emitter-once.js", + "test-event-emitter-prepend.js", + "test-event-emitter-remove-all-listeners.js", + "test-event-emitter-remove-listeners.js", + "test-event-emitter-set-max-listeners-side-effects.js", + "test-event-emitter-special-event-names.js", + "test-event-emitter-subclass.js", + "test-event-emitter-symbols.js", + "test-events-list.js", + "test-events-on-async-iterator.js", + "test-events-once.js", + "test-events-uncaught-exception-stack.js", + "test-eventtarget-brandcheck.js", + "test-exception-handler.js", + "test-exception-handler2.js", + "test-file-read-noexist.js", + "test-file-write-stream.js", + "test-file-write-stream2.js", + "test-file-write-stream3.js", + "test-file-write-stream4.js", + "test-fs-access.js", + "test-fs-append-file-sync.js", + "test-fs-append-file.js", + "test-fs-chmod-mask.js", + "test-fs-chmod.js", + "test-fs-chown-type-check.js", + "test-fs-copyfile.js", + "test-fs-empty-readStream.js", + "test-fs-mkdir.js", + "test-fs-open-flags.js", + "test-fs-open-mode-mask.js", + "test-fs-open-no-close.js", + "test-fs-open-numeric-flags.js", + "test-fs-open.js", + "test-fs-opendir.js", + "test-fs-read-stream-autoClose.js", + "test-fs-read-stream-concurrent-reads.js", + "test-fs-read-stream-double-close.js", + "test-fs-read-stream-encoding.js", + "test-fs-read-stream-fd.js", + "test-fs-read-stream-inherit.js", + "test-fs-read-stream-patch-open.js", + "test-fs-read-stream-resume.js", + "test-fs-read-stream-throw-type-error.js", + "test-fs-read-stream.js", + "test-fs-read-type.js", + "test-fs-read-zero-length.js", + "test-fs-read.js", + "test-fs-readdir-stack-overflow.js", + "test-fs-readdir.js", + "test-fs-readfile-empty.js", + "test-fs-realpath-native.js", + "TODO:test-fs-rm.js", + "test-fs-rmdir-recursive-sync-warns-not-found.js", + "test-fs-rmdir-recursive-sync-warns-on-file.js", + "test-fs-rmdir-recursive-throws-not-found.js", + "test-fs-rmdir-recursive-throws-on-file.js", + "test-fs-rmdir-recursive-warns-not-found.js", + "test-fs-rmdir-recursive-warns-on-file.js", + "test-fs-rmdir-recursive.js", + "test-fs-rmdir-type-check.js", + "TODO:test-fs-watch.js", + "test-fs-watchfile.js", + "test-fs-write-buffer.js", + "test-fs-write-file-buffer.js", + "test-fs-write-file-invalid-path.js", + "test-fs-write-file-sync.js", + "test-fs-write-file.js", + "test-fs-write-no-fd.js", + "test-fs-write-stream-autoclose-option.js", + "test-fs-write-stream-close-without-callback.js", + "test-fs-write-stream-double-close.js", + "test-fs-write-stream-end.js", + "test-fs-write-stream-fs.js", + "test-fs-write-stream-throw-type-error.js", + "test-fs-write-stream.js", + "test-fs-write-sync.js", + "test-fs-write.js", + "test-fs-writev-sync.js", + "TODO:test-fs-writev.js", + "test-handle-wrap-close-abort.js", + "test-http-agent-getname.js", + "test-http-client-get-url.js", + "test-http-client-read-in-error.js", + "test-http-outgoing-buffer.js", + "TODO:test-http-outgoing-destroy.js", + "TODO:test-http-outgoing-finish-writable.js", + "test-http-outgoing-internal-headernames-getter.js", + "test-http-outgoing-internal-headernames-setter.js", + "test-http-outgoing-internal-headers.js", + "test-http-outgoing-message-inheritance.js", + "test-http-outgoing-renderHeaders.js", + "test-http-outgoing-settimeout.js", + "test-http-url.parse-auth.js", + "test-http-url.parse-path.js", + "test-http-url.parse-post.js", + "test-http-url.parse-search.js", + "test-net-access-byteswritten.js", + "TODO:test-net-after-close.js", + "TODO:test-net-allow-half-open.js", + "test-net-better-error-messages-listen-path.js", + "TODO:test-net-better-error-messages-listen.js", + "test-net-better-error-messages-path.js", + "test-net-better-error-messages-port-hostname.js", + "TODO:test-net-bind-twice.js", + "TODO:test-net-buffersize.js", + "TODO:test-net-bytes-written-large.js", + "TODO:test-net-can-reset-timeout.js", + "test-net-connect-after-destroy.js", + "TODO:test-net-connect-buffer.js", + "TODO:test-net-connect-buffer2.js", + "TODO:test-net-connect-call-socket-connect.js", + "test-net-connect-destroy.js", + "test-net-connect-immediate-destroy.js", + "test-net-connect-immediate-finish.js", + "test-net-connect-no-arg.js", + "TODO:test-net-connect-options-ipv6.js", + "TODO:test-net-connect-options-port.js", + "TODO:test-net-dns-custom-lookup.js", + "test-net-dns-error.js", + "TODO:test-net-dns-lookup-skip.js", + "TODO:test-net-dns-lookup.js", + "test-net-during-close.js", + "TODO:test-net-eaddrinuse.js", + "test-net-end-close.js", + "TODO:test-net-end-destroyed.js", + "test-net-end-without-connect.js", + "test-net-isip.js", + "test-net-isipv4.js", + "test-net-isipv6.js", + "TODO:test-net-listen-after-destroying-stdin.js", + "test-net-listen-close-server-callback-is-not-function.js", + "test-net-listen-close-server.js", + "TODO:test-net-listen-error.js", + "test-net-listen-invalid-port.js", + "test-net-listening.js", + "TODO:test-net-local-address-port.js", + "test-net-localerror.js", + "test-net-options-lookup.js", + "TODO:test-net-pause-resume-connecting.js", + "TODO:test-net-persistent-ref-unref.js", + "test-net-pipe-connect-errors.js", + "TODO:test-net-remote-address-port.js", + "TODO:test-net-server-call-listen-multiple-times.js", + "TODO:test-net-server-capture-rejection.js", + "TODO:test-net-server-close.js", + "test-net-server-listen-options-signal.js", + "test-net-server-listen-options.js", + "test-net-server-listen-path.js", + "test-net-server-listen-remove-callback.js", + "TODO:test-net-server-max-connections.js", + "test-net-server-options.js", + "TODO:test-net-server-pause-on-connect.js", + "TODO:test-net-server-try-ports.js", + "test-net-server-unref-persistent.js", + "test-net-server-unref.js", + "TODO:test-net-socket-close-after-end.js", + "TODO:test-net-socket-connect-without-cb.js", + "TODO:test-net-socket-connecting.js", + "TODO:test-net-socket-destroy-send.js", + "test-net-socket-destroy-twice.js", + "TODO:test-net-socket-end-before-connect.js", + "TODO:test-net-socket-end-callback.js", + "test-net-socket-no-halfopen-enforcer.js", + "TODO:test-net-socket-ready-without-cb.js", + "TODO:test-net-socket-timeout.js", + "TODO:test-net-socket-write-after-close.js", + "TODO:test-net-socket-write-error.js", + "TODO:test-net-sync-cork.js", + "test-net-timeout-no-handle.js", + "TODO:test-net-writable.js", + "TODO:test-net-write-after-end-nt.js", + "test-net-write-arguments.js", + "TODO:test-net-write-fully-async-buffer.js", + "TODO:test-net-write-fully-async-hex-string.js", + "TODO:test-net-write-slow.js", + "test-next-tick-doesnt-hang.js", + "test-next-tick-fixed-queue-regression.js", + "test-next-tick-intentional-starvation.js", + "test-next-tick-ordering.js", + "test-next-tick-ordering2.js", + "test-next-tick-when-exiting.js", + "test-next-tick.js", + "test-nodeeventtarget.js", + "TODO:test-os.js", + "test-outgoing-message-destroy.js", + "test-outgoing-message-pipe.js", + "test-path-basename.js", + "test-path-dirname.js", + "test-path-extname.js", + "test-path-isabsolute.js", + "test-path-join.js", + "test-path-makelong.js", + "test-path-normalize.js", + "test-path-parse-format.js", + "test-path-posix-exists.js", + "test-path-relative.js", + "test-path-resolve.js", + "test-path-win32-exists.js", + "test-path-zero-length-strings.js", + "test-path.js", + "test-process-beforeexit.js", + "test-process-binding-internalbinding-allowlist.js", + "test-process-env-allowed-flags.js", + "test-process-exit-from-before-exit.js", + "test-process-exit-handler.js", + "test-process-exit-recursive.js", + "test-process-exit.js", + "test-process-kill-pid.js", + "TODO:test-process-uptime.js", + "test-promise-unhandled-silent.js", + "test-promise-unhandled-throw-handler.js", + "TODO:test-punycode.js", + "test-querystring-escape.js", + "test-querystring-maxKeys-non-finite.js", + "test-querystring-multichar-separator.js", + "test-querystring.js", + "TODO:test-readline-csi.js", + "test-readline-emit-keypress-events.js", + "test-readline-interface-escapecodetimeout.js", + "TODO:test-readline-interface.js", + "test-readline-keys.js", + "test-readline-position.js", + "TODO:test-readline-promises-csi.mjs", + "TODO:test-readline-promises-interface.js", + "test-readline-reopen.js", + "test-readline-set-raw-mode.js", + "test-readline-undefined-columns.js", + "test-readline.js", + "test-stdin-from-file-spawn.js", + "test-stream-add-abort-signal.js", + "test-stream-aliases-legacy.js", + "TODO:test-stream-asIndexedPairs.mjs", + "test-stream-auto-destroy.js", + "test-stream-await-drain-writers-in-synchronously-recursion-write.js", + "test-stream-backpressure.js", + "test-stream-big-packet.js", + "test-stream-big-push.js", + "test-stream-buffer-list.js", + "TODO:test-stream-catch-rejections.js", + "test-stream-construct.js", + "test-stream-destroy-event-order.js", + "test-stream-duplex-destroy.js", + "test-stream-duplex-end.js", + "TODO:test-stream-duplex-from.js", + "test-stream-duplex-props.js", + "test-stream-duplex-readable-end.js", + "test-stream-duplex-writable-finished.js", + "test-stream-duplex.js", + "test-stream-end-paused.js", + "test-stream-error-once.js", + "test-stream-events-prepend.js", + "test-stream-inheritance.js", + "test-stream-ispaused.js", + "test-stream-objectmode-undefined.js", + "test-stream-once-readable-pipe.js", + "test-stream-pipe-after-end.js", + "test-stream-pipe-await-drain-manual-resume.js", + "test-stream-pipe-await-drain-push-while-write.js", + "test-stream-pipe-await-drain.js", + "test-stream-pipe-cleanup-pause.js", + "test-stream-pipe-cleanup.js", + "test-stream-pipe-error-handling.js", + "test-stream-pipe-event.js", + "test-stream-pipe-flow-after-unpipe.js", + "test-stream-pipe-flow.js", + "test-stream-pipe-manual-resume.js", + "test-stream-pipe-multiple-pipes.js", + "test-stream-pipe-needDrain.js", + "test-stream-pipe-same-destination-twice.js", + "test-stream-pipe-unpipe-streams.js", + "test-stream-pipe-without-listenerCount.js", + "TODO:test-stream-pipeline-async-iterator.js", + "TODO:test-stream-pipeline-queued-end-in-destroy.js", + "TODO:test-stream-pipeline-with-empty-string.js", + "test-stream-push-strings.js", + "test-stream-readable-aborted.js", + "test-stream-readable-add-chunk-during-data.js", + "test-stream-readable-constructor-set-methods.js", + "test-stream-readable-data.js", + "test-stream-readable-destroy.js", + "test-stream-readable-didRead.js", + "test-stream-readable-emit-readable-short-stream.js", + "test-stream-readable-emittedReadable.js", + "test-stream-readable-end-destroyed.js", + "test-stream-readable-ended.js", + "test-stream-readable-error-end.js", + "test-stream-readable-event.js", + "test-stream-readable-flow-recursion.js", + "test-stream-readable-hwm-0-async.js", + "test-stream-readable-hwm-0-no-flow-data.js", + "test-stream-readable-hwm-0.js", + "test-stream-readable-infinite-read.js", + "test-stream-readable-invalid-chunk.js", + "test-stream-readable-needReadable.js", + "test-stream-readable-next-no-null.js", + "test-stream-readable-no-unneeded-readable.js", + "test-stream-readable-object-multi-push-async.js", + "test-stream-readable-pause-and-resume.js", + "test-stream-readable-readable-then-resume.js", + "test-stream-readable-readable.js", + "test-stream-readable-reading-readingMore.js", + "test-stream-readable-resume-hwm.js", + "test-stream-readable-resumeScheduled.js", + "test-stream-readable-setEncoding-existing-buffers.js", + "test-stream-readable-setEncoding-null.js", + "test-stream-readable-unshift.js", + "test-stream-readable-with-unimplemented-_read.js", + "test-stream-readableListening-state.js", + "TODO:test-stream-some-find-every.mjs", + "test-stream-transform-callback-twice.js", + "test-stream-transform-constructor-set-methods.js", + "test-stream-transform-destroy.js", + "test-stream-transform-final-sync.js", + "test-stream-transform-final.js", + "test-stream-transform-flush-data.js", + "test-stream-transform-objectmode-falsey-value.js", + "test-stream-transform-split-highwatermark.js", + "test-stream-transform-split-objectmode.js", + "test-stream-uint8array.js", + "test-stream-unpipe-event.js", + "test-stream-unshift-empty-chunk.js", + "test-stream-unshift-read-race.js", + "test-stream-writable-change-default-encoding.js", + "test-stream-writable-clear-buffer.js", + "test-stream-writable-constructor-set-methods.js", + "test-stream-writable-decoded-encoding.js", + "test-stream-writable-destroy.js", + "test-stream-writable-end-cb-error.js", + "test-stream-writable-end-multiple.js", + "test-stream-writable-ended-state.js", + "test-stream-writable-finish-destroyed.js", + "test-stream-writable-finished-state.js", + "test-stream-writable-finished.js", + "test-stream-writable-invalid-chunk.js", + "test-stream-writable-needdrain-state.js", + "test-stream-writable-null.js", + "test-stream-writable-properties.js", + "test-stream-writable-writable.js", + "test-stream-writable-write-cb-error.js", + "test-stream-writable-write-cb-twice.js", + "test-stream-writable-write-error.js", + "test-stream-writable-write-writev-finish.js", + "test-stream-writableState-ending.js", + "test-stream-writableState-uncorked-bufferedRequestCount.js", + "test-stream-write-destroy.js", + "test-stream-write-drain.js", + "test-stream-write-final.js", + "test-stream-writev.js", + "test-stream2-base64-single-char-read-end.js", + "test-stream2-basic.js", + "test-stream2-compatibility.js", + "test-stream2-decode-partial.js", + "test-stream2-finish-pipe.js", + "test-stream2-large-read-stall.js", + "test-stream2-objects.js", + "test-stream2-pipe-error-handling.js", + "test-stream2-pipe-error-once-listener.js", + "test-stream2-push.js", + "test-stream2-read-sync-stack.js", + "test-stream2-readable-empty-buffer-no-eof.js", + "test-stream2-readable-from-list.js", + "test-stream2-readable-legacy-drain.js", + "test-stream2-readable-non-empty-end.js", + "test-stream2-readable-wrap-destroy.js", + "test-stream2-readable-wrap-empty.js", + "test-stream2-readable-wrap-error.js", + "test-stream2-readable-wrap.js", + "test-stream2-set-encoding.js", + "test-stream2-transform.js", + "test-stream2-unpipe-drain.js", + "test-stream2-unpipe-leak.js", + "test-stream2-writable.js", + "test-stream3-cork-end.js", + "test-stream3-cork-uncork.js", + "test-stream3-pause-then-read.js", + "test-streams-highwatermark.js", + "test-timers-api-refs.js", + "test-timers-args.js", + "test-timers-clear-null-does-not-throw-error.js", + "test-timers-clear-object-does-not-throw-error.js", + "test-timers-clear-timeout-interval-equivalent.js", + "test-timers-clearImmediate.js", + "test-timers-interval-throw.js", + "test-timers-non-integer-delay.js", + "test-timers-refresh.js", + "test-timers-same-timeout-wrong-list-deleted.js", + "test-timers-timeout-with-non-integer.js", + "test-timers-uncaught-exception.js", + "test-timers-unref-throw-then-ref.js", + "test-timers-user-call.js", + "test-timers-zero-timeout.js", + "TODO:test-tls-connect-simple.js", + "test-url-domain-ascii-unicode.js", + "test-url-fileurltopath.js", + "test-url-format-invalid-input.js", + "test-url-format-whatwg.js", + "test-url-format.js", + "test-url-parse-format.js", + "test-url-parse-invalid-input.js", + "test-url-parse-query.js", + "test-url-pathtofileurl.js", + "test-url-relative.js", + "test-url-urltooptions.js", + "test-util-deprecate-invalid-code.js", + "test-util-deprecate.js", + "test-util-format.js", + "test-util-inherits.js", + "test-util-inspect-long-running.js", + "test-util-inspect-namespace.js", + "test-util-inspect-proxy.js", + "test-util-inspect.js", + "test-util-isDeepStrictEqual.js", + "test-util-promisify.js", + "test-util-types-exists.js", + "TODO:test-util-types.js", + "test-util.js", + "test-vm-static-this.js", + "TODO:test-webcrypto-sign-verify.js", + "test-whatwg-encoding-custom-api-basics.js", + "test-whatwg-encoding-custom-fatal-streaming.js", + "test-whatwg-encoding-custom-textdecoder-fatal.js", + "test-whatwg-encoding-custom-textdecoder-ignorebom.js", + "test-whatwg-encoding-custom-textdecoder-streaming.js", + "test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js", + "test-whatwg-events-add-event-listener-options-passive.js", + "test-whatwg-events-add-event-listener-options-signal.js", + "test-whatwg-events-customevent.js", + "test-whatwg-url-custom-deepequal.js", + "test-whatwg-url-custom-domainto.js", + "test-whatwg-url-custom-global.js", + "test-whatwg-url-custom-href-side-effect.js", + "test-whatwg-url-custom-inspect.js", + "test-whatwg-url-custom-parsing.js", + "test-whatwg-url-custom-setters.js", + "test-whatwg-url-custom-tostringtag.js", + "test-whatwg-url-override-hostname.js", + "test-whatwg-url-properties.js", + "test-whatwg-url-toascii.js", + "test-zlib-close-after-error.js", + "test-zlib-close-after-write.js", + "test-zlib-convenience-methods.js", + "test-zlib-deflate-raw-inherits.js", + "test-zlib-destroy-pipe.js", + "test-zlib-empty-buffer.js", + "test-zlib-from-string.js", + "test-zlib-invalid-input.js", + "test-zlib-no-stream.js", + "test-zlib-random-byte-pipes.js", + "test-zlib-sync-no-event.js", + "test-zlib-truncated.js", + "test-zlib-unzip-one-byte-chunks.js", + "test-zlib-write-after-end.js", + "test-zlib-write-after-flush.js", + "test-zlib-zero-byte.js", + "test-zlib-zero-windowBits.js" + ], + "pseudo-tty": [ + "console-dumb-tty.js", + "console_colors.js", + "no_dropped_stdio.js", + "no_interleaved_stdio.js", + "test-tty-color-support-warning-2.js", + "test-tty-color-support-warning.js", + "test-tty-stdin-end.js", + "test-tty-stdout-end.js" + ], + "pummel": [ + "TODO:test-net-bytes-per-incoming-chunk-overhead.js", + "TODO:test-net-pingpong-delay.js", + "TODO:test-net-write-callbacks.js" + ], + "sequential": [ + "test-child-process-exit.js" + ] + }, + "windowsIgnore": { + "parallel": [ + "test-child-process-exec-abortcontroller-promisified.js", + "test-console-log-throw-primitive.js", + "test-console-no-swallow-stack-overflow.js", + "test-console-sync-write-error.js", + "test-dns.js", + "test-dns-resolveany.js", + "test-fs-access.js", + "test-fs-mkdir.js", + "test-fs-open-mode-mask.js", + "test-fs-readdir-stack-overflow.js", + "test-fs-rm.js", + "test-fs-watchfile.js", + "test-fs-write-file-invalid-path.js", + "test-fs-write-file-sync.js", + "test-fs-write-file.js", + "test-http-client-get-url.js", + "test-http-client-reject-cr-no-lf.js", + "test-net-allow-half-open.js", + "test-net-better-error-messages-listen-path.js", + "test-net-better-error-messages-path.js", + "test-net-pipe-connect-errors.js", + "test-net-server-listen-path.js", + "test-net-socket-close-after-end.js", + "test-util-inspect-long-running.js", + "test-util-inspect.js" + ] + }, + "darwinIgnore": { + "parallel": [ + "test-net-allow-half-open.js", + "test-net-socket-close-after-end.js" ] }, - "windowsIgnore": {}, - "darwinIgnore": {} + "suitesFolder": "test", + "versionsFolder": "versions" } diff --git a/cli/tests/node_compat/test.ts b/cli/tests/node_compat/test.ts index 3af1fb69356e5a..4029596bde36c0 100644 --- a/cli/tests/node_compat/test.ts +++ b/cli/tests/node_compat/test.ts @@ -39,9 +39,10 @@ for await (const path of testPaths) { ) { continue; } + const isTodo = path.includes("TODO"); const ignore = (Deno.build.os === "windows" && windowsIgnorePaths.has(path)) || - (Deno.build.os === "darwin" && darwinIgnorePaths.has(path)); + (Deno.build.os === "darwin" && darwinIgnorePaths.has(path)) || isTodo; Deno.test({ name: `Node.js compatibility "${path}"`, ignore, @@ -60,7 +61,7 @@ for await (const path of testPaths) { "-A", "--quiet", "--unstable", - "--unsafely-ignore-certificate-errors", + //"--unsafely-ignore-certificate-errors", "--v8-flags=" + v8Flags.join(), testCase.endsWith(".mjs") ? "--import-map=" + importMap : "runner.ts", testCase, @@ -94,7 +95,8 @@ for await (const path of testPaths) { } function checkConfigTestFilesOrder(testFileLists: Array) { - for (const testFileList of testFileLists) { + for (let testFileList of testFileLists) { + testFileList = testFileList.filter((name) => !name.startsWith("TODO:")); const sortedTestList = JSON.parse(JSON.stringify(testFileList)); sortedTestList.sort(); if (JSON.stringify(testFileList) !== JSON.stringify(sortedTestList)) { diff --git a/cli/tests/node_compat/test/common/child_process.js b/cli/tests/node_compat/test/common/child_process.js new file mode 100644 index 00000000000000..4b553ea84bf41b --- /dev/null +++ b/cli/tests/node_compat/test/common/child_process.js @@ -0,0 +1,56 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const assert = require('assert'); +const common = require('./'); + +// Workaround for Windows Server 2008R2 +// When CMD is used to launch a process and CMD is killed too quickly, the +// process can stay behind running in suspended state, never completing. +function cleanupStaleProcess(filename) { + if (!common.isWindows) { + return; + } + process.once('beforeExit', () => { + const basename = filename.replace(/.*[/\\]/g, ''); + try { + require('child_process') + .execFileSync(`${process.env.SystemRoot}\\System32\\wbem\\WMIC.exe`, [ + 'process', + 'where', + `commandline like '%${basename}%child'`, + 'delete', + '/nointeractive', + ]); + } catch { + // Ignore failures, there might not be any stale process to clean up. + } + }); +} + +// This should keep the child process running long enough to expire +// the timeout. +const kExpiringChildRunTime = common.platformTimeout(20 * 1000); +const kExpiringParentTimer = 1; +assert(kExpiringChildRunTime > kExpiringParentTimer); + +function logAfterTime(time) { + setTimeout(() => { + // The following console statements are part of the test. + console.log('child stdout'); + console.error('child stderr'); + }, time); +} + +module.exports = { + cleanupStaleProcess, + logAfterTime, + kExpiringChildRunTime, + kExpiringParentTimer +}; diff --git a/cli/tests/node_compat/test/common/countdown.js b/cli/tests/node_compat/test/common/countdown.js new file mode 100644 index 00000000000000..635a24374d3906 --- /dev/null +++ b/cli/tests/node_compat/test/common/countdown.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const assert = require('assert'); +const kLimit = Symbol('limit'); +const kCallback = Symbol('callback'); +const common = require('./'); + +class Countdown { + constructor(limit, cb) { + assert.strictEqual(typeof limit, 'number'); + assert.strictEqual(typeof cb, 'function'); + this[kLimit] = limit; + this[kCallback] = common.mustCall(cb); + } + + dec() { + assert(this[kLimit] > 0, 'Countdown expired'); + if (--this[kLimit] === 0) + this[kCallback](); + return this[kLimit]; + } + + get remaining() { + return this[kLimit]; + } +} + +module.exports = Countdown; diff --git a/cli/tests/node_compat/test/common/dns.js b/cli/tests/node_compat/test/common/dns.js new file mode 100644 index 00000000000000..d89769298b24ea --- /dev/null +++ b/cli/tests/node_compat/test/common/dns.js @@ -0,0 +1,327 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const assert = require('assert'); +const os = require('os'); + +const types = { + A: 1, + AAAA: 28, + NS: 2, + CNAME: 5, + SOA: 6, + PTR: 12, + MX: 15, + TXT: 16, + ANY: 255, + CAA: 257 +}; + +const classes = { + IN: 1 +}; + +// Naïve DNS parser/serializer. + +function readDomainFromPacket(buffer, offset) { + assert.ok(offset < buffer.length); + const length = buffer[offset]; + if (length === 0) { + return { nread: 1, domain: '' }; + } else if ((length & 0xC0) === 0) { + offset += 1; + const chunk = buffer.toString('ascii', offset, offset + length); + // Read the rest of the domain. + const { nread, domain } = readDomainFromPacket(buffer, offset + length); + return { + nread: 1 + length + nread, + domain: domain ? `${chunk}.${domain}` : chunk + }; + } + // Pointer to another part of the packet. + assert.strictEqual(length & 0xC0, 0xC0); + // eslint-disable-next-line space-infix-ops, space-unary-ops + const pointeeOffset = buffer.readUInt16BE(offset) &~ 0xC000; + return { + nread: 2, + domain: readDomainFromPacket(buffer, pointeeOffset) + }; +} + +function parseDNSPacket(buffer) { + assert.ok(buffer.length > 12); + + const parsed = { + id: buffer.readUInt16BE(0), + flags: buffer.readUInt16BE(2), + }; + + const counts = [ + ['questions', buffer.readUInt16BE(4)], + ['answers', buffer.readUInt16BE(6)], + ['authorityAnswers', buffer.readUInt16BE(8)], + ['additionalRecords', buffer.readUInt16BE(10)], + ]; + + let offset = 12; + for (const [ sectionName, count ] of counts) { + parsed[sectionName] = []; + for (let i = 0; i < count; ++i) { + const { nread, domain } = readDomainFromPacket(buffer, offset); + offset += nread; + + const type = buffer.readUInt16BE(offset); + + const rr = { + domain, + cls: buffer.readUInt16BE(offset + 2), + }; + offset += 4; + + for (const name in types) { + if (types[name] === type) + rr.type = name; + } + + if (sectionName !== 'questions') { + rr.ttl = buffer.readInt32BE(offset); + const dataLength = buffer.readUInt16BE(offset); + offset += 6; + + switch (type) { + case types.A: + assert.strictEqual(dataLength, 4); + rr.address = `${buffer[offset + 0]}.${buffer[offset + 1]}.` + + `${buffer[offset + 2]}.${buffer[offset + 3]}`; + break; + case types.AAAA: + assert.strictEqual(dataLength, 16); + rr.address = buffer.toString('hex', offset, offset + 16) + .replace(/(.{4}(?!$))/g, '$1:'); + break; + case types.TXT: + { + let position = offset; + rr.entries = []; + while (position < offset + dataLength) { + const txtLength = buffer[offset]; + rr.entries.push(buffer.toString('utf8', + position + 1, + position + 1 + txtLength)); + position += 1 + txtLength; + } + assert.strictEqual(position, offset + dataLength); + break; + } + case types.MX: + { + rr.priority = buffer.readInt16BE(buffer, offset); + offset += 2; + const { nread, domain } = readDomainFromPacket(buffer, offset); + rr.exchange = domain; + assert.strictEqual(nread, dataLength); + break; + } + case types.NS: + case types.CNAME: + case types.PTR: + { + const { nread, domain } = readDomainFromPacket(buffer, offset); + rr.value = domain; + assert.strictEqual(nread, dataLength); + break; + } + case types.SOA: + { + const mname = readDomainFromPacket(buffer, offset); + const rname = readDomainFromPacket(buffer, offset + mname.nread); + rr.nsname = mname.domain; + rr.hostmaster = rname.domain; + const trailerOffset = offset + mname.nread + rname.nread; + rr.serial = buffer.readUInt32BE(trailerOffset); + rr.refresh = buffer.readUInt32BE(trailerOffset + 4); + rr.retry = buffer.readUInt32BE(trailerOffset + 8); + rr.expire = buffer.readUInt32BE(trailerOffset + 12); + rr.minttl = buffer.readUInt32BE(trailerOffset + 16); + + assert.strictEqual(trailerOffset + 20, dataLength); + break; + } + default: + throw new Error(`Unknown RR type ${rr.type}`); + } + offset += dataLength; + } + + parsed[sectionName].push(rr); + + assert.ok(offset <= buffer.length); + } + } + + assert.strictEqual(offset, buffer.length); + return parsed; +} + +function writeIPv6(ip) { + const parts = ip.replace(/^:|:$/g, '').split(':'); + const buf = Buffer.alloc(16); + + let offset = 0; + for (const part of parts) { + if (part === '') { + offset += 16 - 2 * (parts.length - 1); + } else { + buf.writeUInt16BE(parseInt(part, 16), offset); + offset += 2; + } + } + + return buf; +} + +function writeDomainName(domain) { + return Buffer.concat(domain.split('.').map((label) => { + assert(label.length < 64); + return Buffer.concat([ + Buffer.from([label.length]), + Buffer.from(label, 'ascii'), + ]); + }).concat([Buffer.alloc(1)])); +} + +function writeDNSPacket(parsed) { + const buffers = []; + const kStandardResponseFlags = 0x8180; + + buffers.push(new Uint16Array([ + parsed.id, + parsed.flags === undefined ? kStandardResponseFlags : parsed.flags, + parsed.questions && parsed.questions.length, + parsed.answers && parsed.answers.length, + parsed.authorityAnswers && parsed.authorityAnswers.length, + parsed.additionalRecords && parsed.additionalRecords.length, + ])); + + for (const q of parsed.questions) { + assert(types[q.type]); + buffers.push(writeDomainName(q.domain)); + buffers.push(new Uint16Array([ + types[q.type], + q.cls === undefined ? classes.IN : q.cls, + ])); + } + + for (const rr of [].concat(parsed.answers, + parsed.authorityAnswers, + parsed.additionalRecords)) { + if (!rr) continue; + + assert(types[rr.type]); + buffers.push(writeDomainName(rr.domain)); + buffers.push(new Uint16Array([ + types[rr.type], + rr.cls === undefined ? classes.IN : rr.cls, + ])); + buffers.push(new Int32Array([rr.ttl])); + + const rdLengthBuf = new Uint16Array(1); + buffers.push(rdLengthBuf); + + switch (rr.type) { + case 'A': + rdLengthBuf[0] = 4; + buffers.push(new Uint8Array(rr.address.split('.'))); + break; + case 'AAAA': + rdLengthBuf[0] = 16; + buffers.push(writeIPv6(rr.address)); + break; + case 'TXT': { + const total = rr.entries.map((s) => s.length).reduce((a, b) => a + b); + // Total length of all strings + 1 byte each for their lengths. + rdLengthBuf[0] = rr.entries.length + total; + for (const txt of rr.entries) { + buffers.push(new Uint8Array([Buffer.byteLength(txt)])); + buffers.push(Buffer.from(txt)); + } + break; + } + case 'MX': + rdLengthBuf[0] = 2; + buffers.push(new Uint16Array([rr.priority])); + // fall through + case 'NS': + case 'CNAME': + case 'PTR': + { + const domain = writeDomainName(rr.exchange || rr.value); + rdLengthBuf[0] += domain.length; + buffers.push(domain); + break; + } + case 'SOA': + { + const mname = writeDomainName(rr.nsname); + const rname = writeDomainName(rr.hostmaster); + rdLengthBuf[0] = mname.length + rname.length + 20; + buffers.push(mname, rname); + buffers.push(new Uint32Array([ + rr.serial, rr.refresh, rr.retry, rr.expire, rr.minttl, + ])); + break; + } + case 'CAA': + { + rdLengthBuf[0] = 5 + rr.issue.length + 2; + buffers.push(Buffer.from([Number(rr.critical)])); + buffers.push(Buffer.from([Number(5)])); + buffers.push(Buffer.from('issue' + rr.issue)); + break; + } + default: + throw new Error(`Unknown RR type ${rr.type}`); + } + } + + return Buffer.concat(buffers.map((typedArray) => { + const buf = Buffer.from(typedArray.buffer, + typedArray.byteOffset, + typedArray.byteLength); + if (os.endianness() === 'LE') { + if (typedArray.BYTES_PER_ELEMENT === 2) buf.swap16(); + if (typedArray.BYTES_PER_ELEMENT === 4) buf.swap32(); + } + return buf; + })); +} + +const mockedErrorCode = 'ENOTFOUND'; +const mockedSysCall = 'getaddrinfo'; + +function errorLookupMock(code = mockedErrorCode, syscall = mockedSysCall) { + return function lookupWithError(hostname, dnsopts, cb) { + const err = new Error(`${syscall} ${code} ${hostname}`); + err.code = code; + err.errno = code; + err.syscall = syscall; + err.hostname = hostname; + cb(err); + }; +} + +module.exports = { + types, + classes, + writeDNSPacket, + parseDNSPacket, + errorLookupMock, + mockedErrorCode, + mockedSysCall +}; diff --git a/cli/tests/node_compat/test/common/duplexpair.js b/cli/tests/node_compat/test/common/duplexpair.js new file mode 100644 index 00000000000000..9e5c498c4efb3e --- /dev/null +++ b/cli/tests/node_compat/test/common/duplexpair.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const { Duplex } = require('stream'); +const assert = require('assert'); + +const kCallback = Symbol('Callback'); +const kOtherSide = Symbol('Other'); + +class DuplexSocket extends Duplex { + constructor() { + super(); + this[kCallback] = null; + this[kOtherSide] = null; + } + + _read() { + const callback = this[kCallback]; + if (callback) { + this[kCallback] = null; + callback(); + } + } + + _write(chunk, encoding, callback) { + assert.notStrictEqual(this[kOtherSide], null); + assert.strictEqual(this[kOtherSide][kCallback], null); + if (chunk.length === 0) { + process.nextTick(callback); + } else { + this[kOtherSide].push(chunk); + this[kOtherSide][kCallback] = callback; + } + } + + _final(callback) { + this[kOtherSide].on('end', callback); + this[kOtherSide].push(null); + } +} + +function makeDuplexPair() { + const clientSide = new DuplexSocket(); + const serverSide = new DuplexSocket(); + clientSide[kOtherSide] = serverSide; + serverSide[kOtherSide] = clientSide; + return { clientSide, serverSide }; +} + +module.exports = makeDuplexPair; diff --git a/cli/tests/node_compat/test/common/fixtures.js b/cli/tests/node_compat/test/common/fixtures.js new file mode 100644 index 00000000000000..af4dbc5a5627ab --- /dev/null +++ b/cli/tests/node_compat/test/common/fixtures.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const { pathToFileURL } = require('url'); + +const fixturesDir = path.join(__dirname, '..', 'fixtures'); + +function fixturesPath(...args) { + return path.join(fixturesDir, ...args); +} + +function fixturesFileURL(...args) { + return pathToFileURL(fixturesPath(...args)); +} + +function readFixtureSync(args, enc) { + if (Array.isArray(args)) + return fs.readFileSync(fixturesPath(...args), enc); + return fs.readFileSync(fixturesPath(args), enc); +} + +function readFixtureKey(name, enc) { + return fs.readFileSync(fixturesPath('keys', name), enc); +} + +function readFixtureKeys(enc, ...names) { + return names.map((name) => readFixtureKey(name, enc)); +} + +module.exports = { + fixturesDir, + path: fixturesPath, + fileURL: fixturesFileURL, + readSync: readFixtureSync, + readKey: readFixtureKey, + readKeys: readFixtureKeys, +}; diff --git a/cli/tests/node_compat/test/common/hijackstdio.js b/cli/tests/node_compat/test/common/hijackstdio.js new file mode 100644 index 00000000000000..a746e979168de9 --- /dev/null +++ b/cli/tests/node_compat/test/common/hijackstdio.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Hijack stdout and stderr +const stdWrite = {}; +function hijackStdWritable(name, listener) { + const stream = process[name]; + const _write = stdWrite[name] = stream.write; + + stream.writeTimes = 0; + stream.write = function(data, callback) { + try { + listener(data); + } catch (e) { + process.nextTick(() => { throw e; }); + } + + _write.call(stream, data, callback); + stream.writeTimes++; + }; +} + +function restoreWritable(name) { + process[name].write = stdWrite[name]; + delete process[name].writeTimes; +} + +module.exports = { + hijackStdout: hijackStdWritable.bind(null, 'stdout'), + hijackStderr: hijackStdWritable.bind(null, 'stderr'), + restoreStdout: restoreWritable.bind(null, 'stdout'), + restoreStderr: restoreWritable.bind(null, 'stderr') +}; diff --git a/cli/tests/node_compat/test/common/index.mjs b/cli/tests/node_compat/test/common/index.mjs new file mode 100644 index 00000000000000..d5b9d7dad5efaf --- /dev/null +++ b/cli/tests/node_compat/test/common/index.mjs @@ -0,0 +1,113 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const common = require('./index.js'); + +const { + isMainThread, + isWindows, + isAIX, + isIBMi, + isLinuxPPCBE, + isSunOS, + isDumbTerminal, + isFreeBSD, + isOpenBSD, + isLinux, + isOSX, + enoughTestMem, + buildType, + localIPv6Hosts, + opensslCli, + PIPE, + hasCrypto, + hasIPv6, + childShouldThrowAndAbort, + checkoutEOL, + createZeroFilledFile, + platformTimeout, + allowGlobals, + mustCall, + mustCallAtLeast, + mustSucceed, + hasMultiLocalhost, + skipIfDumbTerminal, + skipIfEslintMissing, + canCreateSymLink, + getCallSite, + mustNotCall, + mustNotMutateObjectDeep, + printSkipMessage, + skip, + nodeProcessAborted, + isAlive, + expectWarning, + expectsError, + skipIfInspectorDisabled, + skipIf32Bits, + getArrayBufferViews, + getBufferSources, + getTTYfd, + runWithInvalidFD, + spawnPromisified, +} = common; + +const getPort = () => common.PORT; + +export { + isMainThread, + isWindows, + isAIX, + isIBMi, + isLinuxPPCBE, + isSunOS, + isDumbTerminal, + isFreeBSD, + isOpenBSD, + isLinux, + isOSX, + enoughTestMem, + buildType, + localIPv6Hosts, + opensslCli, + PIPE, + hasCrypto, + hasIPv6, + childShouldThrowAndAbort, + checkoutEOL, + createZeroFilledFile, + platformTimeout, + allowGlobals, + mustCall, + mustCallAtLeast, + mustSucceed, + hasMultiLocalhost, + skipIfDumbTerminal, + skipIfEslintMissing, + canCreateSymLink, + getCallSite, + mustNotCall, + mustNotMutateObjectDeep, + printSkipMessage, + skip, + nodeProcessAborted, + isAlive, + expectWarning, + expectsError, + skipIfInspectorDisabled, + skipIf32Bits, + getArrayBufferViews, + getBufferSources, + getTTYfd, + runWithInvalidFD, + createRequire, + spawnPromisified, + getPort, +}; diff --git a/cli/tests/node_compat/test/common/internet.js b/cli/tests/node_compat/test/common/internet.js new file mode 100644 index 00000000000000..b42fda66c63dab --- /dev/null +++ b/cli/tests/node_compat/test/common/internet.js @@ -0,0 +1,68 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Utilities for internet-related tests + +const addresses = { + // A generic host that has registered common DNS records, + // supports both IPv4 and IPv6, and provides basic HTTP/HTTPS services + INET_HOST: 'nodejs.org', + // A host that provides IPv4 services + INET4_HOST: 'nodejs.org', + // A host that provides IPv6 services + INET6_HOST: 'nodejs.org', + // An accessible IPv4 IP, + // defaults to the Google Public DNS IPv4 address + INET4_IP: '8.8.8.8', + // An accessible IPv6 IP, + // defaults to the Google Public DNS IPv6 address + INET6_IP: '2001:4860:4860::8888', + // An invalid host that cannot be resolved + // See https://tools.ietf.org/html/rfc2606#section-2 + INVALID_HOST: 'something.invalid', + // A host with MX records registered + MX_HOST: 'nodejs.org', + // On some systems, .invalid returns a server failure/try again rather than + // record not found. Use this to guarantee record not found. + NOT_FOUND: 'come.on.fhqwhgads.test', + // A host with SRV records registered + // TODO(kt3k): Temporarily use _caldav._tcp.google.com instead of + // _jabber._tcp.google.com, which currently doesn't respond + // SRV_HOST: '_jabber._tcp.google.com', + SRV_HOST: '_caldav._tcp.google.com', + // A host with PTR records registered + PTR_HOST: '8.8.8.8.in-addr.arpa', + // A host with NAPTR records registered + NAPTR_HOST: 'sip2sip.info', + // A host with SOA records registered + SOA_HOST: 'nodejs.org', + // A host with CAA record registered + CAA_HOST: 'google.com', + // A host with CNAME records registered + CNAME_HOST: 'blog.nodejs.org', + // A host with NS records registered + NS_HOST: 'nodejs.org', + // A host with TXT records registered + TXT_HOST: 'nodejs.org', + // An accessible IPv4 DNS server + DNS4_SERVER: '8.8.8.8', + // An accessible IPv4 DNS server + DNS6_SERVER: '2001:4860:4860::8888' +}; + +for (const key of Object.keys(addresses)) { + const envName = `NODE_TEST_${key}`; + if (process.env[envName]) { + addresses[key] = process.env[envName]; + } +} + +module.exports = { + addresses +}; diff --git a/cli/tests/node_compat/test/fixtures/GH-1899-output.js b/cli/tests/node_compat/test/fixtures/GH-1899-output.js new file mode 100644 index 00000000000000..013b61ba0510e1 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/GH-1899-output.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +console.log('hello, world!'); + diff --git a/cli/tests/node_compat/test/fixtures/a.js b/cli/tests/node_compat/test/fixtures/a.js new file mode 100644 index 00000000000000..c3960fd92ceeb2 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/a.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +const c = require('./b/c'); + +console.error('load fixtures/a.js'); + +var string = 'A'; + +exports.SomeClass = c.SomeClass; + +exports.A = function() { + return string; +}; + +exports.C = function() { + return c.C(); +}; + +exports.D = function() { + return c.D(); +}; + +exports.number = 42; + +process.on('exit', function() { + string = 'A done'; +}); diff --git a/cli/tests/node_compat/test/fixtures/child-process-spawn-node.js b/cli/tests/node_compat/test/fixtures/child-process-spawn-node.js new file mode 100644 index 00000000000000..589da4dab19f58 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/child-process-spawn-node.js @@ -0,0 +1,14 @@ +const assert = require('assert'); +// TODO(kt3k): Uncomment this when util.debuglog is added +// const debug = require('util').debuglog('test'); +const debug = console.log; + +function onmessage(m) { + debug('CHILD got message:', m); + assert.ok(m.hello); + process.removeListener('message', onmessage); +} + +process.on('message', onmessage); +// TODO(kt3k): Uncomment the below when the ipc features are ready +// process.send({ foo: 'bar' }); diff --git a/cli/tests/node_compat/test/fixtures/child_process_should_emit_error.js b/cli/tests/node_compat/test/fixtures/child_process_should_emit_error.js new file mode 100644 index 00000000000000..7ceaf9209080f2 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/child_process_should_emit_error.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +const exec = require('child_process').exec; + +[0, 1].forEach(function(i) { + exec('ls', function(err, stdout, stderr) { + console.log(i); + throw new Error('hello world'); + }); +}); diff --git a/cli/tests/node_compat/test/fixtures/echo.js b/cli/tests/node_compat/test/fixtures/echo.js new file mode 100644 index 00000000000000..893099e9bd9d4c --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/echo.js @@ -0,0 +1,41 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +const common = require('../common'); +const assert = require('assert'); + +process.stdout.write('hello world\r\n'); + +// TODO(PolarETech): process.openStdin() is not yet implemented. +// Use process.stdin instead. +var stdin = process.stdin; +// var stdin = process.openStdin(); + +stdin.on('data', function(data) { + process.stdout.write(data.toString()); +}); diff --git a/cli/tests/node_compat/test/fixtures/elipses.txt b/cli/tests/node_compat/test/fixtures/elipses.txt new file mode 100644 index 00000000000000..610560050537d6 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/elipses.txt @@ -0,0 +1 @@ +………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………… \ No newline at end of file diff --git a/cli/tests/node_compat/test/fixtures/empty.txt b/cli/tests/node_compat/test/fixtures/empty.txt new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/cli/tests/node_compat/test/fixtures/exit.js b/cli/tests/node_compat/test/fixtures/exit.js new file mode 100644 index 00000000000000..ca80f48286b308 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/exit.js @@ -0,0 +1,31 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The process.argv[3] should be argv[2]. + +process.exit(process.argv[3] || 1); diff --git a/cli/tests/node_compat/test/fixtures/keys/agent1-cert.pem b/cli/tests/node_compat/test/fixtures/keys/agent1-cert.pem new file mode 100644 index 00000000000000..938ec42b721079 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/keys/agent1-cert.pem @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +-----BEGIN CERTIFICATE----- +MIID6DCCAtCgAwIBAgIUFH02wcL3Qgben6tfIibXitsApCYwDQYJKoZIhvcNAQEL +BQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G +A1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe +BgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTIyMDkwMzIxNDAzN1oY +DzIyOTYwNjE3MjE0MDM3WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJ +BgNVBAcMAlNGMQ8wDQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxDzAN +BgNVBAMMBmFnZW50MTEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUVjIK+yDTgnCT3CxChO0E +37q9VuHdrlKeKLeQzUJW2yczSfNzX/0zfHpjY+zKWie39z3HCJqWxtiG2wxiOI8c +3WqWOvzVmdWADlh6EfkIlg+E7VC6JaKDA+zabmhPvnuu3JzogBMnsWl68lCXzuPx +deQAmEwNtqjrh74DtM+Ud0ulb//Ixjxo1q3rYKu+aaexSramuee6qJta2rjrB4l8 +B/bU+j1mDf9XQQfSjo9jRnp4hiTFdBl2k+lZzqE2L/rhu6EMjA2IhAq/7xA2MbLo +9cObVUin6lfoo5+JKRgT9Fp2xEgDOit+2EA/S6oUfPNeLSVUqmXOSWlXlwlb9Nxr +AgMBAAGjYTBfMF0GCCsGAQUFBwEBBFEwTzAjBggrBgEFBQcwAYYXaHR0cDovL29j +c3Aubm9kZWpzLm9yZy8wKAYIKwYBBQUHMAKGHGh0dHA6Ly9jYS5ub2RlanMub3Jn +L2NhLmNlcnQwDQYJKoZIhvcNAQELBQADggEBAMM0mBBjLMt9pYXePtUeNO0VTw9y +FWCM8nAcAO2kRNwkJwcsispNpkcsHZ5o8Xf5mpCotdvziEWG1hyxwU6nAWyNOLcN +G0a0KUfbMO3B6ZYe1GwPDjXaQnv75SkAdxgX5zOzca3xnhITcjUUGjQ0fbDfwFV5 +ix8mnzvfXjDONdEznVa7PFcN6QliFUMwR/h8pCRHtE5+a10OSPeJSrGG+FtrGnRW +G1IJUv6oiGF/MvWCr84REVgc1j78xomGANJIu2hN7bnD1nEMON6em8IfnDOUtynV +9wfWTqiQYD5Zifj6WcGa0aAHMuetyFG4lIfMAHmd3gaKpks7j9l26LwRPvI= +-----END CERTIFICATE----- diff --git a/cli/tests/node_compat/test/fixtures/keys/agent1-key.pem b/cli/tests/node_compat/test/fixtures/keys/agent1-key.pem new file mode 100644 index 00000000000000..b182ac5ed9be1e --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/keys/agent1-key.pem @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1FYyCvsg04Jwk9wsQoTtBN+6vVbh3a5Snii3kM1CVtsnM0nz +c1/9M3x6Y2Psylont/c9xwialsbYhtsMYjiPHN1qljr81ZnVgA5YehH5CJYPhO1Q +uiWigwPs2m5oT757rtyc6IATJ7FpevJQl87j8XXkAJhMDbao64e+A7TPlHdLpW// +yMY8aNat62CrvmmnsUq2prnnuqibWtq46weJfAf21Po9Zg3/V0EH0o6PY0Z6eIYk +xXQZdpPpWc6hNi/64buhDIwNiIQKv+8QNjGy6PXDm1VIp+pX6KOfiSkYE/RadsRI +AzorfthAP0uqFHzzXi0lVKplzklpV5cJW/TcawIDAQABAoIBAAvbtHfAhpjJVBgt +15rvaX04MWmZjIugzKRgib/gdq/7FTlcC+iJl85kSUF7tyGl30n62MxgwqFhAX6m +hQ6HMhbelrFFIhGbwbyhEHfgwROlrcAysKt0pprCgVvBhrnNXYLqdyjU3jz9P3LK +TY3s0/YMK2uNFdI+PTjKH+Z9Foqn9NZUnUonEDepGyuRO7fLeccWJPv2L4CR4a/5 +ku4VbDgVpvVSVRG3PSVzbmxobnpdpl52og+T7tPx1cLnIknPtVljXPWtZdfekh2E +eAp2KxCCHOKzzG3ItBKsVu0woeqEpy8JcoO6LbgmEoVnZpgmtQClbBgef8+i+oGE +BgW9nmECgYEA8gA63QQuZOUC56N1QXURexN2PogF4wChPaCTFbQSJXvSBkQmbqfL +qRSD8P0t7GOioPrQK6pDwFf4BJB01AvkDf8Z6DxxOJ7cqIC7LOwDupXocWX7Q0Qk +O6cwclBVsrDZK00v60uRRpl/a39GW2dx7IiQDkKQndLh3/0TbMIWHNcCgYEA4J6r +yinZbLpKw2+ezhi4B4GT1bMLoKboJwpZVyNZZCzYR6ZHv+lS7HR/02rcYMZGoYbf +n7OHwF4SrnUS7vPhG4g2ZsOhKQnMvFSQqpGmK1ZTuoKGAevyvtouhK/DgtLWzGvX +9fSahiq/UvfXs/z4M11q9Rv9ztPCmG1cwSEHlo0CgYEAogQNZJK8DMhVnYcNpXke +7uskqtCeQE/Xo06xqkIYNAgloBRYNpUYAGa/vsOBz1UVN/kzDUi8ezVp0oRz8tLT +J5u2WIi+tE2HJTiqF3UbOfvK1sCT64DfUSCpip7GAQ/tFNRkVH8PD9kMOYfILsGe +v+DdsO5Xq5HXrwHb02BNNZkCgYBsl8lt33WiPx5OBfS8pu6xkk+qjPkeHhM2bKZs +nkZlS9j0KsudWGwirN/vkkYg8zrKdK5AQ0dqFRDrDuasZ3N5IA1M+V88u+QjWK7o +B6pSYVXxYZDv9OZSpqC+vUrEQLJf+fNakXrzSk9dCT1bYv2Lt6ox/epix7XYg2bI +Z/OHMQKBgQC2FUGhlndGeugTJaoJ8nhT/0VfRUX/h6sCgSerk5qFr/hNCBV4T022 +x0NDR2yLG6MXyqApJpG6rh3QIDElQoQCNlI3/KJ6JfEfmqrLLN2OigTvA5sE4fGU +Dp/ha8OQAx95EwXuaG7LgARduvOIK3x8qi8KsZoUGJcg2ywurUbkWA== +-----END RSA PRIVATE KEY----- diff --git a/cli/tests/node_compat/test/fixtures/loop.js b/cli/tests/node_compat/test/fixtures/loop.js new file mode 100644 index 00000000000000..69bac522c117bd --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/loop.js @@ -0,0 +1,17 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +var t = 1; +var k = 1; +console.log('A message', 5); +while (t > 0) { + if (t++ === 1000) { + t = 0; + console.log(`Outputted message #${k++}`); + } +} +process.exit(55); diff --git a/cli/tests/node_compat/test/fixtures/package.json b/cli/tests/node_compat/test/fixtures/package.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/package.json @@ -0,0 +1 @@ +{} diff --git a/cli/tests/node_compat/test/fixtures/print-chars.js b/cli/tests/node_compat/test/fixtures/print-chars.js new file mode 100644 index 00000000000000..2519c77fd2e6bf --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/print-chars.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The process.argv[3] should be argv[2]. + +const assert = require('assert'); + +var n = parseInt(process.argv[3]); + +process.stdout.write('c'.repeat(n)); diff --git a/cli/tests/node_compat/test/fixtures/x.txt b/cli/tests/node_compat/test/fixtures/x.txt new file mode 100644 index 00000000000000..cd470e619003f5 --- /dev/null +++ b/cli/tests/node_compat/test/fixtures/x.txt @@ -0,0 +1 @@ +xyz diff --git a/cli/tests/node_compat/test/internet/package.json b/cli/tests/node_compat/test/internet/package.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/cli/tests/node_compat/test/internet/package.json @@ -0,0 +1 @@ +{} diff --git a/cli/tests/node_compat/test/internet/test-dgram-connect.js b/cli/tests/node_compat/test/internet/test-dgram-connect.js new file mode 100644 index 00000000000000..17b52472cdc598 --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dgram-connect.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { addresses } = require('../common/internet'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); +client.connect(common.PORT, addresses.INVALID_HOST, common.mustCall((err) => { + assert.ok(err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN'); + + client.once('error', common.mustCall((err) => { + assert.ok(err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN'); + client.once('connect', common.mustCall(() => client.close())); + client.connect(common.PORT); + })); + + client.connect(common.PORT, addresses.INVALID_HOST); +})); diff --git a/cli/tests/node_compat/test/internet/test-dns-any.js b/cli/tests/node_compat/test/internet/test-dns-any.js new file mode 100644 index 00000000000000..cd4450524568e6 --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-any.js @@ -0,0 +1,192 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(cmorten): enable remaining tests once functionality is implemented. + +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const dns = require('dns'); +const net = require('net'); + +let running = false; +const queue = []; + +const dnsPromises = dns.promises; +const isIPv4 = net.isIPv4; +const isIPv6 = net.isIPv6; + +dns.setServers([ '8.8.8.8', '8.8.4.4' ]); + +function checkWrap(req) { + assert.ok(typeof req === 'object'); +} + +const checkers = { + checkA(r) { + assert.ok(isIPv4(r.address)); + // assert.strictEqual(typeof r.ttl, 'number'); + assert.strictEqual(r.type, 'A'); + }, + checkAAAA(r) { + assert.ok(isIPv6(r.address)); + // assert.strictEqual(typeof r.ttl, 'number'); + assert.strictEqual(r.type, 'AAAA'); + }, + checkCNAME(r) { + assert.ok(r.value); + assert.strictEqual(typeof r.value, 'string'); + assert.strictEqual(r.type, 'CNAME'); + }, + checkMX(r) { + assert.strictEqual(typeof r.exchange, 'string'); + assert.strictEqual(typeof r.priority, 'number'); + assert.strictEqual(r.type, 'MX'); + }, + checkNAPTR(r) { + assert.strictEqual(typeof r.flags, 'string'); + assert.strictEqual(typeof r.service, 'string'); + assert.strictEqual(typeof r.regexp, 'string'); + assert.strictEqual(typeof r.replacement, 'string'); + assert.strictEqual(typeof r.order, 'number'); + assert.strictEqual(typeof r.preference, 'number'); + assert.strictEqual(r.type, 'NAPTR'); + }, + checkNS(r) { + assert.strictEqual(typeof r.value, 'string'); + assert.strictEqual(r.type, 'NS'); + }, + checkPTR(r) { + assert.strictEqual(typeof r.value, 'string'); + assert.strictEqual(r.type, 'PTR'); + }, + checkTXT(r) { + assert.ok(Array.isArray(r.entries)); + assert.ok(r.entries.length > 0); + assert.strictEqual(r.type, 'TXT'); + }, + checkSOA(r) { + assert.strictEqual(typeof r.nsname, 'string'); + assert.strictEqual(typeof r.hostmaster, 'string'); + assert.strictEqual(typeof r.serial, 'number'); + assert.strictEqual(typeof r.refresh, 'number'); + assert.strictEqual(typeof r.retry, 'number'); + assert.strictEqual(typeof r.expire, 'number'); + assert.strictEqual(typeof r.minttl, 'number'); + assert.strictEqual(r.type, 'SOA'); + }, + checkSRV(r) { + assert.strictEqual(typeof r.name, 'string'); + assert.strictEqual(typeof r.port, 'number'); + assert.strictEqual(typeof r.priority, 'number'); + assert.strictEqual(typeof r.weight, 'number'); + assert.strictEqual(r.type, 'SRV'); + } +}; + +function TEST(f) { + function next() { + const f = queue.shift(); + if (f) { + running = true; + f(done); + } + } + + function done() { + running = false; + process.nextTick(next); + } + + queue.push(f); + + if (!running) { + next(); + } +} + +function processResult(res) { + assert.ok(Array.isArray(res)); + assert.ok(res.length > 0); + + const types = {}; + res.forEach((obj) => { + types[obj.type] = true; + checkers[`check${obj.type}`](obj); + }); + + return types; +} + +TEST(async function test_sip2sip_for_naptr(done) { + function validateResult(res) { + const types = processResult(res); + assert.ok( + types.A && types.NS && types.NAPTR && types.SOA, + `Missing record type, found ${Object.keys(types)}` + ); + } + + validateResult(await dnsPromises.resolve('sip2sip.info', 'ANY')); + + const req = dns.resolve( + 'sip2sip.info', + 'ANY', + common.mustSucceed((ret) => { + validateResult(ret); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_google_for_cname_and_srv(done) { + function validateResult(res) { + const types = processResult(res); + assert.ok(types.SRV); + } + + // TODO(kt3k): Temporarily use _caldav._tcp.google.com instead of + // _jabber._tcp.google.com, which currently doesn't respond + // validateResult(await dnsPromises.resolve('_jabber._tcp.google.com', 'ANY')); + validateResult(await dnsPromises.resolve('_caldav._tcp.google.com', 'ANY')); + + + // TODO(kt3k): Temporarily use _caldav._tcp.google.com instead of + // _jabber._tcp.google.com, which currently doesn't respond + const req = dns.resolve( + // '_jabber._tcp.google.com', + '_caldav._tcp.google.com', + 'ANY', + common.mustSucceed((ret) => { + validateResult(ret); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_ptr(done) { + function validateResult(res) { + const types = processResult(res); + assert.ok(types.PTR); + } + + validateResult(await dnsPromises.resolve('8.8.8.8.in-addr.arpa', 'ANY')); + + const req = dns.resolve( + '8.8.8.8.in-addr.arpa', + 'ANY', + common.mustSucceed((ret) => { + validateResult(ret); + done(); + })); + + checkWrap(req); +}); diff --git a/cli/tests/node_compat/test/internet/test-dns-idna2008.js b/cli/tests/node_compat/test/internet/test-dns-idna2008.js new file mode 100644 index 00000000000000..d082f75473d612 --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-idna2008.js @@ -0,0 +1,76 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Verify that non-ASCII hostnames are handled correctly as IDNA 2008. +// +// * Tests will fail with NXDOMAIN when UTF-8 leaks through to a getaddrinfo() +// that doesn't support IDNA at all. +// +// * "straße.de" will resolve to the wrong address when the resolver supports +// only IDNA 2003 (e.g., glibc until 2.28) because it encodes it wrong. + +const { mustCall } = require('../common'); +const assert = require('assert'); +const dns = require('dns'); +const { addresses } = require('../common/internet'); + +const fixture = { + hostname: 'straße.de', + expectedAddress: '81.169.145.78', + dnsServer: addresses.DNS4_SERVER, + family: 4, +}; + +// Explicitly use well-behaved DNS servers that are known to be able to resolve +// the query (which is a.k.a xn--strae-oqa.de). +dns.setServers([fixture.dnsServer]); + +dns.lookup( + fixture.hostname, + { family: fixture.family }, + mustCall((err, address) => { + if (err && err.errno === 'ESERVFAIL') { + assert.ok(err.message.includes('queryA ESERVFAIL straße.de')); + return; + } + assert.ifError(err); + assert.strictEqual(address, fixture.expectedAddress); + }) +); + +dns.promises.lookup(fixture.hostname, { family: fixture.family }) + .then(({ address }) => { + assert.strictEqual(address, fixture.expectedAddress); + }, (err) => { + if (err && err.errno === 'ESERVFAIL') { + assert.ok(err.message.includes('queryA ESERVFAIL straße.de')); + } else { + throw err; + } + }).finally(mustCall()); + +dns.resolve4(fixture.hostname, mustCall((err, addresses) => { + if (err && err.errno === 'ESERVFAIL') { + assert.ok(err.message.includes('queryA ESERVFAIL straße.de')); + return; + } + assert.ifError(err); + assert.deepStrictEqual(addresses, [fixture.expectedAddress]); +})); + +const p = new dns.promises.Resolver().resolve4(fixture.hostname); +p.then((addresses) => { + assert.deepStrictEqual(addresses, [fixture.expectedAddress]); +}, (err) => { + if (err && err.errno === 'ESERVFAIL') { + assert.ok(err.message.includes('queryA ESERVFAIL straße.de')); + } else { + throw err; + } +}).finally(mustCall()); diff --git a/cli/tests/node_compat/test/internet/test-dns-ipv4.js b/cli/tests/node_compat/test/internet/test-dns-ipv4.js new file mode 100644 index 00000000000000..43b60950a0b04d --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-ipv4.js @@ -0,0 +1,257 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// TODO: enable remaining tests once functionality is implemented. + +const common = require('../common'); +const { addresses } = require('../common/internet'); +const assert = require('assert'); +const dns = require('dns'); +const net = require('net'); +// const util = require('util'); +const isIPv4 = net.isIPv4; + +const dnsPromises = dns.promises; +let running = false; +const queue = []; + +function TEST(f) { + function next() { + const f = queue.shift(); + if (f) { + running = true; + console.log(f.name); + f(done); + } + } + + function done() { + running = false; + process.nextTick(next); + } + + queue.push(f); + + if (!running) { + next(); + } +} + +function checkWrap(req) { + assert.ok(typeof req === 'object'); +} + +TEST(async function test_resolve4(done) { + function validateResult(res) { + assert.ok(res.length > 0); + + for (let i = 0; i < res.length; i++) { + assert.ok(isIPv4(res[i])); + } + } + + validateResult(await dnsPromises.resolve4(addresses.INET4_HOST)); + + const req = dns.resolve4( + addresses.INET4_HOST, + common.mustSucceed((ips) => { + validateResult(ips); + done(); + })); + + checkWrap(req); +}); + +// TEST(async function test_reverse_ipv4(done) { +// function validateResult(res) { +// assert.ok(res.length > 0); + +// for (let i = 0; i < res.length; i++) { +// assert.ok(res[i]); +// assert.ok(typeof res[i] === 'string'); +// } +// } + +// validateResult(await dnsPromises.reverse(addresses.INET4_IP)); + +// const req = dns.reverse( +// addresses.INET4_IP, +// common.mustSucceed((domains) => { +// validateResult(domains); +// done(); +// })); + +// checkWrap(req); +// }); + +TEST(async function test_lookup_ipv4_explicit(done) { + function validateResult(res) { + assert.ok(net.isIPv4(res.address)); + assert.strictEqual(res.family, 4); + } + + validateResult(await dnsPromises.lookup(addresses.INET4_HOST, 4)); + + const req = dns.lookup( + addresses.INET4_HOST, 4, + common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_ipv4_implicit(done) { + function validateResult(res) { + assert.ok(net.isIPv4(res.address)); + assert.strictEqual(res.family, 4); + } + + validateResult(await dnsPromises.lookup(addresses.INET4_HOST)); + + const req = dns.lookup( + addresses.INET4_HOST, + common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_ipv4_explicit_object(done) { + function validateResult(res) { + assert.ok(net.isIPv4(res.address)); + assert.strictEqual(res.family, 4); + } + + validateResult(await dnsPromises.lookup(addresses.INET4_HOST, { family: 4 })); + + const req = dns.lookup(addresses.INET4_HOST, { + family: 4 + }, common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_ipv4_hint_addrconfig(done) { + function validateResult(res) { + assert.ok(net.isIPv4(res.address)); + assert.strictEqual(res.family, 4); + } + + validateResult(await dnsPromises.lookup(addresses.INET4_HOST, { + hints: dns.ADDRCONFIG + })); + + const req = dns.lookup(addresses.INET4_HOST, { + hints: dns.ADDRCONFIG + }, common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_ip_ipv4(done) { + function validateResult(res) { + assert.strictEqual(res.address, '127.0.0.1'); + assert.strictEqual(res.family, 4); + } + + validateResult(await dnsPromises.lookup('127.0.0.1')); + + const req = dns.lookup('127.0.0.1', + common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_localhost_ipv4(done) { + function validateResult(res) { + assert.strictEqual(res.address, '127.0.0.1'); + assert.strictEqual(res.family, 4); + } + + validateResult(await dnsPromises.lookup('localhost', 4)); + + const req = dns.lookup('localhost', 4, + common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_all_ipv4(done) { + function validateResult(res) { + assert.ok(Array.isArray(res)); + assert.ok(res.length > 0); + + res.forEach((ip) => { + assert.ok(isIPv4(ip.address)); + assert.strictEqual(ip.family, 4); + }); + } + + validateResult(await dnsPromises.lookup(addresses.INET4_HOST, { + all: true, + family: 4 + })); + + const req = dns.lookup( + addresses.INET4_HOST, + { all: true, family: 4 }, + common.mustSucceed((ips) => { + validateResult(ips); + done(); + }) + ); + + checkWrap(req); +}); + +// TEST(async function test_lookupservice_ip_ipv4(done) { +// function validateResult(res) { +// assert.strictEqual(typeof res.hostname, 'string'); +// assert(res.hostname); +// assert(['http', 'www', '80'].includes(res.service)); +// } + +// validateResult(await dnsPromises.lookupService('127.0.0.1', 80)); + +// const req = dns.lookupService( +// '127.0.0.1', 80, +// common.mustSucceed((hostname, service) => { +// validateResult({ hostname, service }); +// done(); +// }) +// ); + +// checkWrap(req); +// }); + +// TEST(function test_lookupservice_ip_ipv4_promise(done) { +// util.promisify(dns.lookupService)('127.0.0.1', 80) +// .then(common.mustCall(({ hostname, service }) => { +// assert.strictEqual(typeof hostname, 'string'); +// assert(hostname.length > 0); +// assert(['http', 'www', '80'].includes(service)); +// done(); +// })); +// }); diff --git a/cli/tests/node_compat/test/internet/test-dns-ipv6.js b/cli/tests/node_compat/test/internet/test-dns-ipv6.js new file mode 100644 index 00000000000000..4b94d60414ecca --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-ipv6.js @@ -0,0 +1,250 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// TODO: enable remaining tests once functionality is implemented. + +const common = require('../common'); +const { addresses } = require('../common/internet'); +if (!common.hasIPv6) + common.skip('this test, no IPv6 support'); + +const assert = require('assert'); +const dns = require('dns'); +const net = require('net'); +const dnsPromises = dns.promises; +const isIPv6 = net.isIPv6; + +let running = false; +const queue = []; + +function TEST(f) { + function next() { + const f = queue.shift(); + if (f) { + running = true; + console.log(f.name); + f(done); + } + } + + function done() { + running = false; + process.nextTick(next); + } + + queue.push(f); + + if (!running) { + next(); + } +} + +function checkWrap(req) { + assert.ok(typeof req === 'object'); +} + +TEST(async function test_resolve6(done) { + function validateResult(res) { + assert.ok(res.length > 0); + + for (let i = 0; i < res.length; i++) { + assert.ok(isIPv6(res[i])); + } + } + + validateResult(await dnsPromises.resolve6(addresses.INET6_HOST)); + + const req = dns.resolve6( + addresses.INET6_HOST, + common.mustSucceed((ips) => { + validateResult(ips); + done(); + })); + + checkWrap(req); +}); + +// TEST(async function test_reverse_ipv6(done) { +// function validateResult(res) { +// assert.ok(res.length > 0); + +// for (let i = 0; i < res.length; i++) { +// assert.ok(typeof res[i] === 'string'); +// } +// } + +// validateResult(await dnsPromises.reverse(addresses.INET6_IP)); + +// const req = dns.reverse( +// addresses.INET6_IP, +// common.mustSucceed((domains) => { +// validateResult(domains); +// done(); +// })); + +// checkWrap(req); +// }); + +TEST(async function test_lookup_ipv6_explicit(done) { + function validateResult(res) { + assert.ok(isIPv6(res.address)); + assert.strictEqual(res.family, 6); + } + + validateResult(await dnsPromises.lookup(addresses.INET6_HOST, 6)); + + const req = dns.lookup( + addresses.INET6_HOST, + 6, + common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +// This ends up just being too problematic to test +// TEST(function test_lookup_ipv6_implicit(done) { +// var req = dns.lookup(addresses.INET6_HOST, function(err, ip, family) { +// assert.ifError(err); +// assert.ok(net.isIPv6(ip)); +// assert.strictEqual(family, 6); + +// done(); +// }); + +// checkWrap(req); +// }); + +TEST(async function test_lookup_ipv6_explicit_object(done) { + function validateResult(res) { + assert.ok(isIPv6(res.address)); + assert.strictEqual(res.family, 6); + } + + validateResult(await dnsPromises.lookup(addresses.INET6_HOST, { family: 6 })); + + const req = dns.lookup(addresses.INET6_HOST, { + family: 6 + }, common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(function test_lookup_ipv6_hint(done) { + const req = dns.lookup(addresses.INET6_HOST, { + family: 6, + hints: dns.V4MAPPED + }, common.mustCall((err, ip, family) => { + if (err) { + // FreeBSD does not support V4MAPPED + if (common.isFreeBSD) { + assert(err instanceof Error); + assert.strictEqual(err.code, 'EAI_BADFLAGS'); + assert.strictEqual(err.hostname, addresses.INET_HOST); + assert.match(err.message, /getaddrinfo EAI_BADFLAGS/); + done(); + return; + } + + assert.ifError(err); + } + + assert.ok(isIPv6(ip)); + assert.strictEqual(family, 6); + + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_ip_ipv6(done) { + function validateResult(res) { + assert.ok(isIPv6(res.address)); + assert.strictEqual(res.family, 6); + } + + validateResult(await dnsPromises.lookup('::1')); + + const req = dns.lookup( + '::1', + common.mustSucceed((ip, family) => { + validateResult({ address: ip, family }); + done(); + })); + + checkWrap(req); +}); + +TEST(async function test_lookup_all_ipv6(done) { + function validateResult(res) { + assert.ok(Array.isArray(res)); + assert.ok(res.length > 0); + + res.forEach((ip) => { + assert.ok(isIPv6(ip.address), + `Invalid IPv6: ${ip.address.toString()}`); + assert.strictEqual(ip.family, 6); + }); + } + + validateResult(await dnsPromises.lookup(addresses.INET6_HOST, { + all: true, + family: 6 + })); + + const req = dns.lookup( + addresses.INET6_HOST, + { all: true, family: 6 }, + common.mustSucceed((ips) => { + validateResult(ips); + done(); + }) + ); + + checkWrap(req); +}); + +// TEST(function test_lookupservice_ip_ipv6(done) { +// const req = dns.lookupService( +// '::1', 80, +// common.mustCall((err, host, service) => { +// if (err) { +// // Not skipping the test, rather checking an alternative result, +// // i.e. that ::1 may not be configured (e.g. in /etc/hosts) +// assert.strictEqual(err.code, 'ENOTFOUND'); +// return done(); +// } +// assert.strictEqual(typeof host, 'string'); +// assert(host); +// assert(['http', 'www', '80'].includes(service)); +// done(); +// }) +// ); + +// checkWrap(req); +// }); + +// Disabled because it appears to be not working on Linux. +// TEST(function test_lookup_localhost_ipv6(done) { +// var req = dns.lookup('localhost', 6, function(err, ip, family) { +// assert.ifError(err); +// assert.ok(net.isIPv6(ip)); +// assert.strictEqual(family, 6); +// +// done(); +// }); +// +// checkWrap(req); +// }); diff --git a/cli/tests/node_compat/test/internet/test-dns-lookup.js b/cli/tests/node_compat/test/internet/test-dns-lookup.js new file mode 100644 index 00000000000000..4ed3716b409f98 --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-lookup.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const common = require('../common'); +const dns = require('dns'); +const dnsPromises = dns.promises; +const { addresses } = require('../common/internet'); +const assert = require('assert'); + +assert.rejects( + dnsPromises.lookup(addresses.NOT_FOUND, { + hints: 0, + family: 0, + all: false + }), + { + code: 'ENOTFOUND', + message: `getaddrinfo ENOTFOUND ${addresses.NOT_FOUND}` + } +); + +assert.rejects( + dnsPromises.lookup(addresses.NOT_FOUND, { + hints: 0, + family: 0, + all: true + }), + { + code: 'ENOTFOUND', + message: `getaddrinfo ENOTFOUND ${addresses.NOT_FOUND}` + } +); + +dns.lookup(addresses.NOT_FOUND, { + hints: 0, + family: 0, + all: true +}, common.mustCall((error) => { + assert.strictEqual(error.code, 'ENOTFOUND'); + assert.strictEqual( + error.message, + `getaddrinfo ENOTFOUND ${addresses.NOT_FOUND}` + ); + assert.strictEqual(error.syscall, 'getaddrinfo'); + assert.strictEqual(error.hostname, addresses.NOT_FOUND); +})); + +assert.throws( + () => dnsPromises.lookup(addresses.NOT_FOUND, { + family: 'ipv4', + all: 'all' + }), + { code: 'ERR_INVALID_ARG_VALUE' } +); diff --git a/cli/tests/node_compat/test/internet/test-dns-promises-resolve.js b/cli/tests/node_compat/test/internet/test-dns-promises-resolve.js new file mode 100644 index 00000000000000..d700ad48ca119a --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-promises-resolve.js @@ -0,0 +1,49 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const dnsPromises = require('dns').promises; + +// Error when rrtype is invalid. +{ + const rrtype = 'DUMMY'; + assert.throws( + () => dnsPromises.resolve('example.org', rrtype), + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: `The argument 'rrtype' is invalid. Received '${rrtype}'` + } + ); +} + +// Error when rrtype is a number. +{ + const rrtype = 0; + assert.throws( + () => dnsPromises.resolve('example.org', rrtype), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "rrtype" argument must be of type string. ' + + `Received type ${typeof rrtype} (${rrtype})` + } + ); +} + +// Setting rrtype to undefined should work like resolve4. +{ + (async function() { + const rrtype = undefined; + const result = await dnsPromises.resolve('example.org', rrtype); + assert.ok(result !== undefined); + assert.ok(result.length > 0); + })().then(common.mustCall()); +} diff --git a/cli/tests/node_compat/test/internet/test-dns-regress-6244.js b/cli/tests/node_compat/test/internet/test-dns-regress-6244.js new file mode 100644 index 00000000000000..db78a6f92a091d --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-regress-6244.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const dns = require('dns'); + +// Should not segfault. +// Ref: https://github.com/nodejs/node-v0.x-archive/issues/6244 +dns.resolve4('127.0.0.1', common.mustCall()); diff --git a/cli/tests/node_compat/test/internet/test-dns-setserver-in-callback-of-resolve4.js b/cli/tests/node_compat/test/internet/test-dns-setserver-in-callback-of-resolve4.js new file mode 100644 index 00000000000000..27356b9ca54c0e --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns-setserver-in-callback-of-resolve4.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// We don't care about `err` in the callback function of `dns.resolve4`. We just +// want to test whether `dns.setServers` that is run after `resolve4` will cause +// a crash or not. If it doesn't crash, the test succeeded. + +const common = require('../common'); +const { addresses } = require('../common/internet'); +const dns = require('dns'); + +dns.resolve4( + addresses.INET4_HOST, + common.mustCall(function(/* err, nameServers */) { + dns.setServers([ addresses.DNS4_SERVER ]); + })); + +// Test https://github.com/nodejs/node/issues/14734 +dns.resolve4(addresses.INET4_HOST, common.mustCall()); diff --git a/cli/tests/node_compat/test/internet/test-dns.js b/cli/tests/node_compat/test/internet/test-dns.js new file mode 100644 index 00000000000000..ae5e46ca95e4b6 --- /dev/null +++ b/cli/tests/node_compat/test/internet/test-dns.js @@ -0,0 +1,761 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// TODO(cmorten): enable remaining tests once functionality is implemented. + +const common = require('../common'); +const { addresses } = require('../common/internet'); +const { internalBinding } = require('internal/test/binding'); +// const { getSystemErrorName } = require('util'); +const assert = require('assert'); +const dns = require('dns'); +const net = require('net'); +const isIPv4 = net.isIPv4; +const isIPv6 = net.isIPv6; +const util = require('util'); +const dnsPromises = dns.promises; + +let expected = 0; +let completed = 0; +let running = false; +const queue = []; + + +function TEST(f) { + function next() { + const f = queue.shift(); + if (f) { + running = true; + console.log(f.name); + f(done); + } + } + + function done() { + running = false; + completed++; + process.nextTick(next); + } + + expected++; + queue.push(f); + + if (!running) { + next(); + } +} + + +function checkWrap(req) { + assert.strictEqual(typeof req, 'object'); +} + + +// TEST(function test_reverse_bogus(done) { +// dnsPromises.reverse('bogus ip') +// .then(common.mustNotCall()) +// .catch(common.mustCall((err) => { +// assert.strictEqual(err.code, 'EINVAL'); +// assert.strictEqual(getSystemErrorName(err.errno), 'EINVAL'); +// })); + +// assert.throws(() => { +// dns.reverse('bogus ip', common.mustNotCall()); +// }, /^Error: getHostByAddr EINVAL bogus ip$/); +// done(); +// }); + +// TEST(async function test_resolve4_ttl(done) { +// function validateResult(result) { +// assert.ok(result.length > 0); + +// for (const item of result) { +// assert.strictEqual(typeof item, 'object'); +// assert.strictEqual(typeof item.ttl, 'number'); +// assert.strictEqual(typeof item.address, 'string'); +// assert.ok(item.ttl >= 0); +// assert.ok(isIPv4(item.address)); +// } +// } + +// validateResult(await dnsPromises.resolve4(addresses.INET4_HOST, { +// ttl: true +// })); + +// const req = dns.resolve4(addresses.INET4_HOST, { +// ttl: true +// }, function(err, result) { +// assert.ifError(err); +// validateResult(result); +// done(); +// }); + +// checkWrap(req); +// }); + +// TEST(async function test_resolve6_ttl(done) { +// function validateResult(result) { +// assert.ok(result.length > 0); + +// for (const item of result) { +// assert.strictEqual(typeof item, 'object'); +// assert.strictEqual(typeof item.ttl, 'number'); +// assert.strictEqual(typeof item.address, 'string'); +// assert.ok(item.ttl >= 0); +// assert.ok(isIPv6(item.address)); +// } +// } + +// validateResult(await dnsPromises.resolve6(addresses.INET6_HOST, { +// ttl: true +// })); + +// const req = dns.resolve6(addresses.INET6_HOST, { +// ttl: true +// }, function(err, result) { +// assert.ifError(err); +// validateResult(result); +// done(); +// }); + +// checkWrap(req); +// }); + +TEST(async function test_resolveMx(done) { + function validateResult(result) { + assert.ok(result.length > 0); + + for (const item of result) { + assert.strictEqual(typeof item, 'object'); + assert.ok(item.exchange); + assert.strictEqual(typeof item.exchange, 'string'); + assert.strictEqual(typeof item.priority, 'number'); + } + } + + validateResult(await dnsPromises.resolveMx(addresses.MX_HOST)); + + const req = dns.resolveMx(addresses.MX_HOST, function(err, result) { + assert.ifError(err); + validateResult(result); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveMx_failure(done) { + dnsPromises.resolveMx(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveMx(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolveNs(done) { + function validateResult(result) { + assert.ok(result.length > 0); + + for (const item of result) { + assert.ok(item); + assert.strictEqual(typeof item, 'string'); + } + } + + validateResult(await dnsPromises.resolveNs(addresses.NS_HOST)); + + const req = dns.resolveNs(addresses.NS_HOST, function(err, names) { + assert.ifError(err); + validateResult(names); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveNs_failure(done) { + dnsPromises.resolveNs(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveNs(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolveSrv(done) { + function validateResult(result) { + assert.ok(result.length > 0); + + for (const item of result) { + assert.strictEqual(typeof item, 'object'); + assert.ok(item.name); + assert.strictEqual(typeof item.name, 'string'); + assert.strictEqual(typeof item.port, 'number'); + assert.strictEqual(typeof item.priority, 'number'); + assert.strictEqual(typeof item.weight, 'number'); + } + } + + validateResult(await dnsPromises.resolveSrv(addresses.SRV_HOST)); + + const req = dns.resolveSrv(addresses.SRV_HOST, function(err, result) { + assert.ifError(err); + validateResult(result); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveSrv_failure(done) { + dnsPromises.resolveSrv(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveSrv(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolvePtr(done) { + function validateResult(result) { + assert.ok(result.length > 0); + + for (const item of result) { + assert.ok(item); + assert.strictEqual(typeof item, 'string'); + } + } + + validateResult(await dnsPromises.resolvePtr(addresses.PTR_HOST)); + + const req = dns.resolvePtr(addresses.PTR_HOST, function(err, result) { + assert.ifError(err); + validateResult(result); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolvePtr_failure(done) { + dnsPromises.resolvePtr(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolvePtr(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolveNaptr(done) { + function validateResult(result) { + assert.ok(result.length > 0); + + for (const item of result) { + assert.strictEqual(typeof item, 'object'); + assert.strictEqual(typeof item.flags, 'string'); + assert.strictEqual(typeof item.service, 'string'); + assert.strictEqual(typeof item.regexp, 'string'); + assert.strictEqual(typeof item.replacement, 'string'); + assert.strictEqual(typeof item.order, 'number'); + assert.strictEqual(typeof item.preference, 'number'); + } + } + + validateResult(await dnsPromises.resolveNaptr(addresses.NAPTR_HOST)); + + const req = dns.resolveNaptr(addresses.NAPTR_HOST, function(err, result) { + assert.ifError(err); + validateResult(result); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveNaptr_failure(done) { + dnsPromises.resolveNaptr(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveNaptr(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolveSoa(done) { + function validateResult(result) { + assert.strictEqual(typeof result, 'object'); + assert.strictEqual(typeof result.nsname, 'string'); + assert.ok(result.nsname.length > 0); + assert.strictEqual(typeof result.hostmaster, 'string'); + assert.ok(result.hostmaster.length > 0); + assert.strictEqual(typeof result.serial, 'number'); + assert.ok((result.serial > 0) && (result.serial < 4294967295)); + assert.strictEqual(typeof result.refresh, 'number'); + assert.ok((result.refresh > 0) && (result.refresh < 2147483647)); + assert.strictEqual(typeof result.retry, 'number'); + assert.ok((result.retry > 0) && (result.retry < 2147483647)); + assert.strictEqual(typeof result.expire, 'number'); + assert.ok((result.expire > 0) && (result.expire < 2147483647)); + assert.strictEqual(typeof result.minttl, 'number'); + assert.ok((result.minttl >= 0) && (result.minttl < 2147483647)); + } + + validateResult(await dnsPromises.resolveSoa(addresses.SOA_HOST)); + + const req = dns.resolveSoa(addresses.SOA_HOST, function(err, result) { + assert.ifError(err); + validateResult(result); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveSoa_failure(done) { + dnsPromises.resolveSoa(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveSoa(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolveCaa(done) { + function validateResult(result) { + assert.ok(Array.isArray(result), + `expected array, got ${util.inspect(result)}`); + assert.strictEqual(result.length, 1); + assert.strictEqual(typeof result[0].critical, 'number'); + assert.strictEqual(result[0].critical, 0); + assert.strictEqual(result[0].issue, 'pki.goog'); + } + + validateResult(await dnsPromises.resolveCaa(addresses.CAA_HOST)); + + const req = dns.resolveCaa(addresses.CAA_HOST, function(err, records) { + assert.ifError(err); + validateResult(records); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveCaa_failure(done) { + dnsPromises.resolveTxt(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveCaa(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + +TEST(async function test_resolveCname(done) { + function validateResult(result) { + assert.ok(result.length > 0); + + for (const item of result) { + assert.ok(item); + assert.strictEqual(typeof item, 'string'); + } + } + + validateResult(await dnsPromises.resolveCname(addresses.CNAME_HOST)); + + const req = dns.resolveCname(addresses.CNAME_HOST, function(err, names) { + assert.ifError(err); + validateResult(names); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveCname_failure(done) { + dnsPromises.resolveCname(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveCname(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + + +TEST(async function test_resolveTxt(done) { + function validateResult(result) { + assert.ok(Array.isArray(result[0])); + assert.strictEqual(result.length, 1); + assert(result[0][0].startsWith('v=spf1')); + } + + validateResult(await dnsPromises.resolveTxt(addresses.TXT_HOST)); + + const req = dns.resolveTxt(addresses.TXT_HOST, function(err, records) { + assert.ifError(err); + validateResult(records); + done(); + }); + + checkWrap(req); +}); + +TEST(function test_resolveTxt_failure(done) { + dnsPromises.resolveTxt(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOTFOUND'); + })); + + const req = dns.resolveTxt(addresses.NOT_FOUND, function(err, result) { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); + + assert.strictEqual(result, undefined); + + done(); + }); + + checkWrap(req); +}); + + +TEST(function test_lookup_failure(done) { + dnsPromises.lookup(addresses.NOT_FOUND, 4) + .then(common.mustNotCall()) + .catch(common.expectsError({ code: dns.NOTFOUND })); + + const req = dns.lookup(addresses.NOT_FOUND, 4, (err) => { + assert.ok(err instanceof Error); + assert.strictEqual(err.code, dns.NOTFOUND); + assert.strictEqual(err.code, 'ENOTFOUND'); + assert.doesNotMatch(err.message, /ENOENT/); + assert.ok(err.message.includes(addresses.NOT_FOUND)); + + done(); + }); + + checkWrap(req); +}); + + +TEST(async function test_lookup_ip_all(done) { + function validateResult(result) { + assert.ok(Array.isArray(result)); + assert.ok(result.length > 0); + assert.strictEqual(result[0].address, '127.0.0.1'); + assert.strictEqual(result[0].family, 4); + } + + validateResult(await dnsPromises.lookup('127.0.0.1', { all: true })); + + const req = dns.lookup( + '127.0.0.1', + { all: true }, + function(err, ips, family) { + assert.ifError(err); + assert.strictEqual(family, undefined); + validateResult(ips); + done(); + } + ); + + checkWrap(req); +}); + + +TEST(function test_lookup_ip_all_promise(done) { + const req = util.promisify(dns.lookup)('127.0.0.1', { all: true }) + .then(function(ips) { + assert.ok(Array.isArray(ips)); + assert.ok(ips.length > 0); + assert.strictEqual(ips[0].address, '127.0.0.1'); + assert.strictEqual(ips[0].family, 4); + + done(); + }); + + checkWrap(req); +}); + + +TEST(function test_lookup_ip_promise(done) { + util.promisify(dns.lookup)('127.0.0.1') + .then(function({ address, family }) { + assert.strictEqual(address, '127.0.0.1'); + assert.strictEqual(family, 4); + + done(); + }); +}); + + +TEST(async function test_lookup_null_all(done) { + assert.deepStrictEqual(await dnsPromises.lookup(null, { all: true }), []); + + const req = dns.lookup(null, { all: true }, (err, ips) => { + assert.ifError(err); + assert.ok(Array.isArray(ips)); + assert.strictEqual(ips.length, 0); + + done(); + }); + + checkWrap(req); +}); + + +TEST(async function test_lookup_all_mixed(done) { + function validateResult(result) { + assert.ok(Array.isArray(result)); + assert.ok(result.length > 0); + + result.forEach(function(ip) { + if (isIPv4(ip.address)) + assert.strictEqual(ip.family, 4); + else if (isIPv6(ip.address)) + assert.strictEqual(ip.family, 6); + else + assert.fail('unexpected IP address'); + }); + } + + validateResult(await dnsPromises.lookup(addresses.INET_HOST, { all: true })); + + const req = dns.lookup(addresses.INET_HOST, { + all: true + }, function(err, ips) { + assert.ifError(err); + validateResult(ips); + done(); + }); + + checkWrap(req); +}); + + +// TEST(function test_lookupservice_invalid(done) { +// dnsPromises.lookupService('1.2.3.4', 80) +// .then(common.mustNotCall()) +// .catch(common.expectsError({ code: 'ENOTFOUND' })); + +// const req = dns.lookupService('1.2.3.4', 80, (err) => { +// assert(err instanceof Error); +// assert.strictEqual(err.code, 'ENOTFOUND'); +// assert.match(err.message, /1\.2\.3\.4/); + +// done(); +// }); + +// checkWrap(req); +// }); + + +// TEST(function test_reverse_failure(done) { +// dnsPromises.reverse('203.0.113.0') +// .then(common.mustNotCall()) +// .catch(common.expectsError({ +// code: 'ENOTFOUND', +// hostname: '203.0.113.0' +// })); + +// // 203.0.113.0/24 are addresses reserved for (RFC) documentation use only +// const req = dns.reverse('203.0.113.0', function(err) { +// assert(err instanceof Error); +// assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code... +// assert.strictEqual(err.hostname, '203.0.113.0'); +// assert.match(err.message, /203\.0\.113\.0/); + +// done(); +// }); + +// checkWrap(req); +// }); + + +TEST(function test_lookup_failure(done) { + dnsPromises.lookup(addresses.NOT_FOUND) + .then(common.mustNotCall()) + .catch(common.expectsError({ + code: 'ENOTFOUND', + hostname: addresses.NOT_FOUND + })); + + const req = dns.lookup(addresses.NOT_FOUND, (err) => { + assert(err instanceof Error); + assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code... + assert.strictEqual(err.hostname, addresses.NOT_FOUND); + assert.ok(err.message.includes(addresses.NOT_FOUND)); + + done(); + }); + + checkWrap(req); +}); + + +TEST(function test_resolve_failure(done) { + const req = dns.resolve4(addresses.NOT_FOUND, (err) => { + assert(err instanceof Error); + + switch (err.code) { + case 'ENOTFOUND': + case 'ESERVFAIL': + break; + default: + assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code... + break; + } + + assert.strictEqual(err.hostname, addresses.NOT_FOUND); + assert.ok(err.message.includes(addresses.NOT_FOUND)); + + done(); + }); + + checkWrap(req); +}); + + +let getaddrinfoCallbackCalled = false; + +console.log(`looking up ${addresses.INET4_HOST}..`); + +const cares = internalBinding('cares_wrap'); +const req = new cares.GetAddrInfoReqWrap(); +cares.getaddrinfo(req, addresses.INET4_HOST, 4, + /* hints */ 0, /* verbatim */ true); + +req.oncomplete = function(err, domains) { + assert.strictEqual(err, 0); + console.log(`${addresses.INET4_HOST} = ${domains}`); + assert.ok(Array.isArray(domains)); + assert.ok(domains.length >= 1); + assert.strictEqual(typeof domains[0], 'string'); + getaddrinfoCallbackCalled = true; +}; + +process.on('exit', function() { + console.log(`${completed} tests completed`); + assert.strictEqual(running, false); + assert.strictEqual(completed, expected); + assert.ok(getaddrinfoCallbackCalled); +}); + +// Should not throw. +dns.lookup(addresses.INET6_HOST, 6, common.mustCall()); +dns.lookup(addresses.INET_HOST, {}, common.mustCall()); +// dns.lookupService('0.0.0.0', '0', common.mustCall()); +// dns.lookupService('0.0.0.0', 0, common.mustCall()); +(async function() { + await dnsPromises.lookup(addresses.INET6_HOST, 6); + await dnsPromises.lookup(addresses.INET_HOST, {}); +})().then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-assert-async.js b/cli/tests/node_compat/test/parallel/test-assert-async.js new file mode 100644 index 00000000000000..4bee92a1ae6dcf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-assert-async.js @@ -0,0 +1,244 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Run all tests in parallel and check their outcome at the end. +const promises = []; + +// Thenable object without `catch` method, +// shouldn't be considered as a valid Thenable +const invalidThenable = { + then: (fulfill, reject) => { + fulfill(); + }, +}; + +// Function that returns a Thenable function, +// a function with `catch` and `then` methods attached, +// shouldn't be considered as a valid Thenable. +const invalidThenableFunc = () => { + function f() {} + + f.then = (fulfill, reject) => { + fulfill(); + }; + f.catch = () => {}; + + return f; +}; + +// Test assert.rejects() and assert.doesNotReject() by checking their +// expected output and by verifying that they do not work sync + +// Check `assert.rejects`. +{ + const rejectingFn = async () => assert.fail(); + const errObj = { + code: 'ERR_ASSERTION', + name: 'AssertionError', + message: 'Failed' + }; + + // `assert.rejects` accepts a function or a promise + // or a thenable as first argument. + promises.push(assert.rejects(rejectingFn, errObj)); + promises.push(assert.rejects(rejectingFn(), errObj)); + + const validRejectingThenable = { + then: (fulfill, reject) => { + reject({ code: 'FAIL' }); + }, + catch: () => {} + }; + promises.push(assert.rejects(validRejectingThenable, { code: 'FAIL' })); + + // `assert.rejects` should not accept thenables that + // use a function as `obj` and that have no `catch` handler. + promises.push(assert.rejects( + assert.rejects(invalidThenable, {}), + { + code: 'ERR_INVALID_ARG_TYPE' + }) + ); + promises.push(assert.rejects( + assert.rejects(invalidThenableFunc, {}), + { + code: 'ERR_INVALID_RETURN_VALUE' + }) + ); + + const err = new Error('foobar'); + const validate = () => { return 'baz'; }; + promises.push(assert.rejects( + () => assert.rejects(Promise.reject(err), validate), + { + message: 'The "validate" validation function is expected to ' + + "return \"true\". Received 'baz'\n\nCaught error:\n\n" + + 'Error: foobar', + code: 'ERR_ASSERTION', + actual: err, + expected: validate, + name: 'AssertionError', + operator: 'rejects', + } + )); +} + +{ + const handler = (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.message, + 'Missing expected rejection (mustNotCall).'); + assert.strictEqual(err.operator, 'rejects'); + assert.ok(!err.stack.includes('at Function.rejects')); + return true; + }; + + let promise = assert.rejects(async () => {}, common.mustNotCall()); + promises.push(assert.rejects(promise, common.mustCall(handler))); + + promise = assert.rejects(() => {}, common.mustNotCall()); + promises.push(assert.rejects(promise, { + name: 'TypeError', + code: 'ERR_INVALID_RETURN_VALUE', + // FIXME(JakobJingleheimer): This should match on key words, like /Promise/ and /undefined/. + message: 'Expected instance of Promise to be returned ' + + 'from the "promiseFn" function but got undefined.' + })); + + promise = assert.rejects(Promise.resolve(), common.mustNotCall()); + promises.push(assert.rejects(promise, common.mustCall(handler))); +} + +{ + const THROWN_ERROR = new Error(); + + promises.push(assert.rejects(() => { + throw THROWN_ERROR; + }, {}).catch(common.mustCall((err) => { + assert.strictEqual(err, THROWN_ERROR); + }))); +} + +promises.push(assert.rejects( + assert.rejects('fail', {}), + { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "promiseFn" argument must be of type function or an ' + + "instance of Promise. Received type string ('fail')" + } +)); + +{ + const handler = (generated, actual, err) => { + assert.strictEqual(err.generatedMessage, generated); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.actual, actual); + assert.strictEqual(err.operator, 'rejects'); + assert.match(err.stack, /rejects/); + return true; + }; + const err = new Error(); + promises.push(assert.rejects( + assert.rejects(Promise.reject(null), { code: 'FOO' }), + handler.bind(null, true, null) + )); + promises.push(assert.rejects( + assert.rejects(Promise.reject(5), { code: 'FOO' }, 'AAAAA'), + handler.bind(null, false, 5) + )); + promises.push(assert.rejects( + assert.rejects(Promise.reject(err), { code: 'FOO' }, 'AAAAA'), + handler.bind(null, false, err) + )); +} + +// Check `assert.doesNotReject`. +{ + // `assert.doesNotReject` accepts a function or a promise + // or a thenable as first argument. + /* eslint-disable no-restricted-syntax */ + let promise = assert.doesNotReject(() => new Map(), common.mustNotCall()); + promises.push(assert.rejects(promise, { + message: 'Expected instance of Promise to be returned ' + + 'from the "promiseFn" function but got an instance of Map.', + code: 'ERR_INVALID_RETURN_VALUE', + name: 'TypeError' + })); + promises.push(assert.doesNotReject(async () => {})); + promises.push(assert.doesNotReject(Promise.resolve())); + + // `assert.doesNotReject` should not accept thenables that + // use a function as `obj` and that have no `catch` handler. + const validFulfillingThenable = { + then: (fulfill, reject) => { + fulfill(); + }, + catch: () => {} + }; + promises.push(assert.doesNotReject(validFulfillingThenable)); + promises.push(assert.rejects( + assert.doesNotReject(invalidThenable), + { + code: 'ERR_INVALID_ARG_TYPE' + }) + ); + promises.push(assert.rejects( + assert.doesNotReject(invalidThenableFunc), + { + code: 'ERR_INVALID_RETURN_VALUE' + }) + ); + + const handler1 = (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.message, 'Failed'); + return true; + }; + const handler2 = (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.message, + 'Got unwanted rejection.\nActual message: "Failed"'); + assert.strictEqual(err.operator, 'doesNotReject'); + assert.ok(err.stack); + assert.ok(!err.stack.includes('at Function.doesNotReject')); + return true; + }; + + const rejectingFn = async () => assert.fail(); + + promise = assert.doesNotReject(rejectingFn, common.mustCall(handler1)); + promises.push(assert.rejects(promise, common.mustCall(handler2))); + + promise = assert.doesNotReject(rejectingFn(), common.mustCall(handler1)); + promises.push(assert.rejects(promise, common.mustCall(handler2))); + + promise = assert.doesNotReject(() => assert.fail(), common.mustNotCall()); + promises.push(assert.rejects(promise, common.mustCall(handler1))); + + promises.push(assert.rejects( + assert.doesNotReject(123), + { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "promiseFn" argument must be of type ' + + 'function or an instance of Promise. Received type number (123)' + } + )); + /* eslint-enable no-restricted-syntax */ +} + +// Make sure all async code gets properly executed. +Promise.all(promises).then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-assert.js b/cli/tests/node_compat/test/parallel/test-assert.js new file mode 100644 index 00000000000000..58b95068c94c3f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-assert.js @@ -0,0 +1,1615 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 15.5.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { inspect } = require('util'); +// TODO(kt3k): Enable these when "vm" is ready. +// const vm = require('vm'); +// TODO(kt3k): Enable these when "internal/test/binding" is ready. +// const { internalBinding } = require('internal/test/binding'); +const a = assert; + +// Disable colored output to prevent color codes from breaking assertion +// message comparisons. This should only be an issue when process.stdout +// is a TTY. +if (process.stdout.isTTY) + process.env.NODE_DISABLE_COLORS = '1'; + +const strictEqualMessageStart = 'Expected values to be strictly equal:\n'; +const start = 'Expected values to be strictly deep-equal:'; +const actExp = '+ actual - expected'; + +assert.ok(a.AssertionError.prototype instanceof Error, + 'a.AssertionError instanceof Error'); + +assert.throws(() => a(false), a.AssertionError, 'ok(false)'); +assert.throws(() => a.ok(false), a.AssertionError, 'ok(false)'); + +// Throw message if the message is instanceof Error. +{ + let threw = false; + try { + assert.ok(false, new Error('ok(false)')); + } catch (e) { + threw = true; + assert.ok(e instanceof Error); + } + assert.ok(threw, 'Error: ok(false)'); +} + + +a(true); +a('test', 'ok(\'test\')'); +a.ok(true); +a.ok('test'); + +assert.throws(() => a.equal(true, false), + a.AssertionError, 'equal(true, false)'); + +a.equal(null, null); +a.equal(undefined, undefined); +a.equal(null, undefined); +a.equal(true, true); +a.equal(2, '2'); +a.notEqual(true, false); + +assert.throws(() => a.notEqual(true, true), + a.AssertionError, 'notEqual(true, true)'); + +assert.throws(() => a.strictEqual(2, '2'), + a.AssertionError, 'strictEqual(2, \'2\')'); + +assert.throws(() => a.strictEqual(null, undefined), + a.AssertionError, 'strictEqual(null, undefined)'); + +assert.throws( + () => a.notStrictEqual(2, 2), + { + message: 'Expected "actual" to be strictly unequal to: 2\n', + name: 'AssertionError' + } +); + +assert.throws( + () => a.notStrictEqual('a '.repeat(30), 'a '.repeat(30)), + { + message: 'Expected "actual" to be strictly unequal to: ' + + `"${'a '.repeat(30)}"\n`, + name: 'AssertionError' + } +); + +assert.throws( + () => a.notEqual(1, 1), + { + message: '1 != 1', + operator: '!=' + } +); + +a.notStrictEqual(2, '2'); + +// Testing the throwing. +function thrower(errorConstructor) { + throw new errorConstructor({}); +} + +// The basic calls work. +assert.throws(() => thrower(a.AssertionError), a.AssertionError, 'message'); +assert.throws(() => thrower(a.AssertionError), a.AssertionError); +assert.throws(() => thrower(a.AssertionError)); + +// If not passing an error, catch all. +assert.throws(() => thrower(TypeError)); + +// When passing a type, only catch errors of the appropriate type. +assert.throws( + () => a.throws(() => thrower(TypeError), a.AssertionError), + { + // generatedMessage: true, + // actual: new TypeError({}), + expected: a.AssertionError, + code: 'ERR_ASSERTION', + name: 'AssertionError', + operator: 'throws', + message: 'The error is expected to be an instance of "AssertionError". ' + + 'Received "TypeError"\n\nError message:\n\n[object Object]' + } +); + +// doesNotThrow should pass through all errors. +{ + let threw = false; + try { + a.doesNotThrow(() => thrower(TypeError), a.AssertionError); + } catch (e) { + threw = true; + assert.ok(e instanceof TypeError); + } + assert(threw, 'a.doesNotThrow with an explicit error is eating extra errors'); +} + +// Key difference is that throwing our correct error makes an assertion error. +{ + let threw = false; + try { + a.doesNotThrow(() => thrower(TypeError), TypeError); + } catch (e) { + threw = true; + assert.ok(e instanceof a.AssertionError); + // Commented out the following assertion + // assert.ok(!e.stack.includes('at Function.doesNotThrow')); + } + assert.ok(threw, 'a.doesNotThrow is not catching type matching errors'); +} + +assert.throws( + () => a.doesNotThrow(() => thrower(Error), 'user message'), + { + name: 'AssertionError', + code: 'ERR_ASSERTION', + operator: 'doesNotThrow', + message: 'Got unwanted exception: user message\n' + + 'Actual message: "[object Object]"' + } +); + +assert.throws( + () => a.doesNotThrow(() => thrower(Error)), + { + code: 'ERR_ASSERTION', + message: 'Got unwanted exception.\nActual message: "[object Object]"' + } +); + +assert.throws( + () => a.doesNotThrow(() => thrower(Error), /\[[a-z]{6}\s[A-z]{6}\]/g, 'user message'), + { + name: 'AssertionError', + code: 'ERR_ASSERTION', + operator: 'doesNotThrow', + message: 'Got unwanted exception: user message\n' + + 'Actual message: "[object Object]"' + } +); + +// Make sure that validating using constructor really works. +{ + let threw = false; + try { + assert.throws( + () => { + throw ({}); + }, + Array + ); + } catch { + threw = true; + } + assert.ok(threw, 'wrong constructor validation'); +} + +// Use a RegExp to validate the error message. +{ + a.throws(() => thrower(TypeError), /\[object Object\]/); + + const symbol = Symbol('foo'); + a.throws(() => { + throw symbol; + }, /foo/); + + a.throws(() => { + a.throws(() => { + throw symbol; + }, /abc/); + }, { + message: 'The input did not match the regular expression /abc/. ' + + "Input:\n\n'Symbol(foo)'\n", + code: 'ERR_ASSERTION', + operator: 'throws', + actual: symbol, + expected: /abc/ + }); +} + +// Use a fn to validate the error object. +a.throws(() => thrower(TypeError), (err) => { + if ((err instanceof TypeError) && /\[object Object\]/.test(err)) { + return true; + } +}); + +// https://github.com/nodejs/node/issues/3188 +{ + let actual; + assert.throws( + () => { + const ES6Error = class extends Error {}; + const AnotherErrorType = class extends Error {}; + + assert.throws(() => { + actual = new AnotherErrorType('foo'); + throw actual; + }, ES6Error); + }, + (err) => { + assert.strictEqual( + err.message, + 'The error is expected to be an instance of "ES6Error". ' + + 'Received "AnotherErrorType"\n\nError message:\n\nfoo' + ); + assert.strictEqual(err.actual, actual); + return true; + } + ); +} + +// Check messages from assert.throws(). +{ + const noop = () => {}; + assert.throws( + () => { a.throws((noop)); }, + { + code: 'ERR_ASSERTION', + message: 'Missing expected exception.', + operator: 'throws', + actual: undefined, + expected: undefined + }); + + assert.throws( + () => { a.throws(noop, TypeError); }, + { + code: 'ERR_ASSERTION', + message: 'Missing expected exception (TypeError).', + actual: undefined, + expected: TypeError + }); + + assert.throws( + () => { a.throws(noop, 'fhqwhgads'); }, + { + code: 'ERR_ASSERTION', + message: 'Missing expected exception: fhqwhgads', + actual: undefined, + expected: undefined + }); + + assert.throws( + () => { a.throws(noop, TypeError, 'fhqwhgads'); }, + { + code: 'ERR_ASSERTION', + message: 'Missing expected exception (TypeError): fhqwhgads', + actual: undefined, + expected: TypeError + }); + + let threw = false; + try { + a.throws(noop); + } catch (e) { + threw = true; + assert.ok(e instanceof a.AssertionError); + // TODO(kt3k): enable this assertion + // assert.ok(!e.stack.includes('at Function.throws')); + } + assert.ok(threw); +} + +const circular = { y: 1 }; +circular.x = circular; + +function testAssertionMessage(actual, expected, msg) { + assert.throws( + () => assert.strictEqual(actual, ''), + { + generatedMessage: true, + message: msg || strictEqualMessageStart + + `+ actual - expected\n\n+ ${expected}\n- ''` + } + ); +} + +function testShortAssertionMessage(actual, expected) { + testAssertionMessage(actual, expected, strictEqualMessageStart + + `\n${inspect(actual)} !== ''\n`); +} + +// TODO(kt3k): Do we need completely simulate +// Node.js assertion error messages? +/* +testShortAssertionMessage(null, 'null'); +testShortAssertionMessage(true, 'true'); +testShortAssertionMessage(false, 'false'); +testShortAssertionMessage(100, '100'); +testShortAssertionMessage(NaN, 'NaN'); +testShortAssertionMessage(Infinity, 'Infinity'); +testShortAssertionMessage('a', '"a"'); +testShortAssertionMessage('foo', '\'foo\''); +testShortAssertionMessage(0, '0'); +testShortAssertionMessage(Symbol(), 'Symbol()'); +testShortAssertionMessage(undefined, 'undefined'); +testShortAssertionMessage(-Infinity, '-Infinity'); +testAssertionMessage([], '[]'); +testAssertionMessage(/a/, '/a/'); +testAssertionMessage(/abc/gim, '/abc/gim'); +testAssertionMessage({}, '{}'); +testAssertionMessage([1, 2, 3], '[\n+ 1,\n+ 2,\n+ 3\n+ ]'); +testAssertionMessage(function f() {}, '[Function: f]'); +testAssertionMessage(function() {}, '[Function (anonymous)]'); +testAssertionMessage(circular, + ' {\n+ x: [Circular *1],\n+ y: 1\n+ }'); +testAssertionMessage({ a: undefined, b: null }, + '{\n+ a: undefined,\n+ b: null\n+ }'); +testAssertionMessage({ a: NaN, b: Infinity, c: -Infinity }, + '{\n+ a: NaN,\n+ b: Infinity,\n+ c: -Infinity\n+ }'); +*/ + +// https://github.com/nodejs/node-v0.x-archive/issues/5292 +assert.throws( + () => assert.strictEqual(1, 2), +/* Memo: Disabled this object assertion + { + message: `${strictEqualMessageStart}\n1 !== 2\n`, + generatedMessage: true + } +*/ +); + +assert.throws( + () => assert.strictEqual(1, 2, 'oh no'), + { + message: 'oh no', + generatedMessage: false + } +); + +{ + let threw = false; + const rangeError = new RangeError('my range'); + + // Verify custom errors. + try { + assert.strictEqual(1, 2, rangeError); + } catch (e) { + assert.strictEqual(e, rangeError); + threw = true; + assert.ok(e instanceof RangeError, 'Incorrect error type thrown'); + } + assert.ok(threw); + threw = false; + + // Verify AssertionError is the result from doesNotThrow with custom Error. + try { + a.doesNotThrow(() => { + throw new TypeError('wrong type'); + }, TypeError, rangeError); + } catch (e) { + threw = true; + assert.ok(e.message.includes(rangeError.message)); + assert.ok(e instanceof assert.AssertionError); + // TODO(kt3k): Enable this assertion + // assert.ok(!e.stack.includes('doesNotThrow'), e); + } + assert.ok(threw); +} + +{ + // Verify that throws() and doesNotThrow() throw on non-functions. + const testBlockTypeError = (method, fn) => { + assert.throws( + () => method(fn), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "fn" argument must be of type function.' + + common.invalidArgTypeHelper(fn) + } + ); + }; + + testBlockTypeError(assert.throws, 'string'); + testBlockTypeError(assert.doesNotThrow, 'string'); + testBlockTypeError(assert.throws, 1); + testBlockTypeError(assert.doesNotThrow, 1); + testBlockTypeError(assert.throws, true); + testBlockTypeError(assert.doesNotThrow, true); + testBlockTypeError(assert.throws, false); + testBlockTypeError(assert.doesNotThrow, false); + testBlockTypeError(assert.throws, []); + testBlockTypeError(assert.doesNotThrow, []); + testBlockTypeError(assert.throws, {}); + testBlockTypeError(assert.doesNotThrow, {}); + testBlockTypeError(assert.throws, /foo/); + testBlockTypeError(assert.doesNotThrow, /foo/); + testBlockTypeError(assert.throws, null); + testBlockTypeError(assert.doesNotThrow, null); + testBlockTypeError(assert.throws, undefined); + testBlockTypeError(assert.doesNotThrow, undefined); +} + +// https://github.com/nodejs/node/issues/3275 +assert.throws(() => { throw 'error'; }, (err) => err === 'error'); +assert.throws(() => { throw new Error(); }, (err) => err instanceof Error); + +// Long values should be truncated for display. +assert.throws(() => { + assert.strictEqual('A'.repeat(1000), ''); +}, (err) => { + assert.strictEqual(err.code, 'ERR_ASSERTION'); + /* TODO(kt3k): Enable this assertion + assert.strictEqual(err.message, + `${strictEqualMessageStart}+ actual - expected\n\n` + + `+ '${'A'.repeat(1000)}'\n- ''`); + */ + assert.strictEqual(err.actual.length, 1000); + // TODO(kt3k): Enable this after fixing 'inspect' + // assert.ok(inspect(err).includes(`actual: '${'A'.repeat(488)}...'`)); + return true; +}); + +// Output that extends beyond 10 lines should also be truncated for display. +{ + const multilineString = 'fhqwhgads\n'.repeat(15); + assert.throws(() => { + assert.strictEqual(multilineString, ''); + }, (err) => { + assert.strictEqual(err.code, 'ERR_ASSERTION'); + // TODO(kt3k): Enable these assertion when the strictEqual message is aligned + // to Node.js API. + // assert.strictEqual(err.message.split('\n').length, 19); + assert.strictEqual(err.actual.split('\n').length, 16); + // TODO(kt3k): inspect(err) causes Maximum call stack error. + /* + assert.ok(inspect(err).includes( + "actual: 'fhqwhgads\\n' +\n" + + " 'fhqwhgads\\n' +\n".repeat(9) + + " '...'")); + */ + return true; + }); +} + +{ + // Bad args to AssertionError constructor should throw TypeError. + const args = [1, true, false, '', null, Infinity, Symbol('test'), undefined]; + args.forEach((input) => { + assert.throws( + () => new assert.AssertionError(input), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options" argument must be of type object.' + + common.invalidArgTypeHelper(input) + }); + }); +} + +assert.throws( + () => assert.strictEqual(new Error('foo'), new Error('foobar')), + { + code: 'ERR_ASSERTION', + name: 'AssertionError', + /* TODO(kt3k): Enable this assertion when the assertion error message is fixed. + message: 'Expected "actual" to be reference-equal to "expected":\n' + + '+ actual - expected\n\n' + + '+ [Error: foo]\n- [Error: foobar]' + */ + } +); + +a.equal(NaN, NaN); +a.throws( + () => a.notEqual(NaN, NaN), + a.AssertionError +); + +// Test strict assert. +{ + const a = require('assert'); + const assert = require('assert').strict; + assert.throws(() => assert.equal(1, true), assert.AssertionError); + assert.notEqual(0, false); + assert.throws(() => assert.deepEqual(1, true), assert.AssertionError); + assert.notDeepEqual(0, false); + assert.equal(assert.strict, assert.strict.strict); + assert.equal(assert.equal, assert.strictEqual); + assert.equal(assert.deepEqual, assert.deepStrictEqual); + assert.equal(assert.notEqual, assert.notStrictEqual); + assert.equal(assert.notDeepEqual, assert.notDeepStrictEqual); + assert.equal(Object.keys(assert).length, Object.keys(a).length); + assert(7); + assert.throws( + () => assert(...[]), + { + message: 'No value argument passed to `assert.ok()`', + name: 'AssertionError', + // TODO(kt3k): Enable this + // generatedMessage: true + } + ); + assert.throws( + () => a(), + { + message: 'No value argument passed to `assert.ok()`', + name: 'AssertionError' + } + ); + + // Test setting the limit to zero and that assert.strict works properly. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + assert.throws( + () => { + assert.ok( + typeof 123 === 'string' + ); + }, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n ' + + "assert.ok(\n typeof 123 === 'string'\n )\n" + */ + } + ); + Error.stackTraceLimit = tmpLimit; + + // Test error diffs. + let message = [ + start, + `${actExp} ... Lines skipped`, + '', + ' [', + ' [', + ' [', + ' 1,', + ' 2,', + '+ 3', + "- '3'", + ' ]', + '...', + ' 4,', + ' 5', + ' ]'].join('\n'); + assert.throws( + () => assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); + + message = [ + start, + `${actExp} ... Lines skipped`, + '', + ' [', + ' 1,', + '...', + ' 1,', + ' 0,', + '- 1,', + ' 1,', + '...', + ' 1,', + ' 1', + ' ]' + ].join('\n'); + assert.throws( + () => assert.deepEqual( + [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); + + message = [ + start, + `${actExp} ... Lines skipped`, + '', + ' [', + ' 1,', + '...', + ' 1,', + ' 0,', + '+ 1,', + ' 1,', + ' 1,', + ' 1', + ' ]' + ].join('\n'); + assert.throws( + () => assert.deepEqual( + [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1]), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); + + message = [ + start, + actExp, + '', + ' [', + ' 1,', + '+ 2,', + '- 1,', + ' 1,', + ' 1,', + ' 0,', + '+ 1,', + ' 1', + ' ]' + ].join('\n'); + assert.throws( + () => assert.deepEqual( + [1, 2, 1, 1, 0, 1, 1], + [1, 1, 1, 1, 0, 1]), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); + + message = [ + start, + actExp, + '', + '+ [', + '+ 1,', + '+ 2,', + '+ 1', + '+ ]', + '- undefined', + ].join('\n'); + assert.throws( + () => assert.deepEqual([1, 2, 1], undefined), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); + + message = [ + start, + actExp, + '', + ' [', + '+ 1,', + ' 2,', + ' 1', + ' ]' + ].join('\n'); + assert.throws( + () => assert.deepEqual([1, 2, 1], [2, 1]), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); + + message = `${start}\n` + + `${actExp} ... Lines skipped\n` + + '\n' + + ' [\n' + + '+ 1,\n'.repeat(25) + + '...\n' + + '- 2,\n'.repeat(25) + + '...'; + assert.throws( + () => assert.deepEqual(Array(28).fill(1), Array(28).fill(2)), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); + + const obj1 = {}; + const obj2 = { loop: 'forever' }; + obj2[inspect.custom] = () => '{}'; + // No infinite loop and no custom inspect. + assert.throws(() => assert.deepEqual(obj1, obj2), { + code: "ERR_ASSERTION", + /* TODO(kt3k): Enable this assertion + message: `${start}\n` + + `${actExp}\n` + + '\n' + + '+ {}\n' + + '- {\n' + + '- [Symbol(nodejs.util.inspect.custom)]: [Function (anonymous)],\n' + + "- loop: 'forever'\n" + + '- }' + */ + }); + + // notDeepEqual tests + assert.throws( + () => assert.notDeepEqual([1], [1]), + { + code: "ERR_ASSERTION", + /* TODO(kt3k): Enable this assertion + message: 'Expected "actual" not to be strictly deep-equal to:\n\n' + + '[\n 1\n]\n' + */ + } + ); + + message = 'Expected "actual" not to be strictly deep-equal to:' + + `\n\n[${'\n 1,'.repeat(45)}\n...\n`; + const data = Array(51).fill(1); + assert.throws( + () => assert.notDeepEqual(data, data), + /* TODO(kt3k): Enable this assertion + { message } + */ + ); +} + +assert.throws( + () => assert.ok(null), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + generatedMessage: true, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert.ok(null)\n' + */ + } +); +assert.throws( + () => assert(typeof 123n === 'string'), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + generatedMessage: true, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n ' + + "assert(typeof 123n === 'string')\n" + */ + } +); + +assert.throws( + () => assert(false, Symbol('foo')), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + generatedMessage: false, + message: 'Symbol(foo)' + */ + } +); + +// TODO(kt3k): Enable these when "internal/test/binding" is ready. +/* +{ + // Test caching. + const fs = internalBinding('fs'); + const tmp = fs.close; + fs.close = common.mustCall(tmp, 1); + function throwErr() { + assert( + (Buffer.from('test') instanceof Error) + ); + } + assert.throws( + () => throwErr(), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + message: 'The expression evaluated to a falsy value:\n\n ' + + "assert(\n (Buffer.from('test') instanceof Error)\n )\n" + } + ); + assert.throws( + () => throwErr(), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + message: 'The expression evaluated to a falsy value:\n\n ' + + "assert(\n (Buffer.from('test') instanceof Error)\n )\n" + } + ); + fs.close = tmp; +} +*/ + +assert.throws( + () => { + a( + (() => 'string')() + === + 123 instanceof + Buffer + ); + }, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n' + + ' a(\n' + + ' (() => \'string\')()\n' + + ' ===\n' + + ' 123 instanceof\n' + + ' Buffer\n' + + ' )\n' + */ + } +); + +assert.throws( + () => { + a( + (() => 'string')() + === + 123 instanceof + Buffer + ); + }, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n' + + ' a(\n' + + ' (() => \'string\')()\n' + + ' ===\n' + + ' 123 instanceof\n' + + ' Buffer\n' + + ' )\n' + */ + } +); + +assert.throws(() => { +a(( + () => 'string')() === +123 instanceof +Buffer +); +}, { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n' + + ' a((\n' + + ' () => \'string\')() ===\n' + + ' 123 instanceof\n' + + ' Buffer\n' + + ' )\n' + */ + } +); + +assert.throws( + () => { + assert(true); assert(null, undefined); + }, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert(null, undefined)\n' + */ + } +); + +assert.throws( + () => { + assert + .ok(null, undefined); + }, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n ' + + 'ok(null, undefined)\n' + */ + } +); + +assert.throws( + () => assert['ok']["apply"](null, [0]), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert[\'ok\']["apply"](null, [0])\n' + */ + } +); + +assert.throws( + () => { + const wrapper = (fn, value) => fn(value); + wrapper(assert, false); + }, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n fn(value)\n' + */ + } +); + +assert.throws( + () => assert.ok.call(null, 0), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert.ok.call(null, 0)\n', + generatedMessage: true + */ + } +); + +assert.throws( + () => assert.ok.call(null, 0, 'test'), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + message: 'test', + generatedMessage: false + } +); + +// Works in eval. +assert.throws( + () => new Function('assert', 'assert(1 === 2);')(assert), + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n assert(1 === 2)\n' + */ + } +); + +assert.throws( + () => eval('console.log("FOO");\nassert.ok(1 === 2);'), + { + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + message: 'false == true' + */ + } +); + +assert.throws( + () => assert.throws(() => {}, 'Error message', 'message'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + /* TODO(kt3k): Enable this assertion + message: 'The "error" argument must be of type function or ' + + 'an instance of Error, RegExp, or Object. Received type string ' + + "('Error message')" + */ + } +); + +[ + 1, + false, + Symbol() +].forEach((input) => { + assert.throws( + () => assert.throws(() => {}, input), + { + code: 'ERR_INVALID_ARG_TYPE', + /* TODO(kt3k): Enable this assertion + message: 'The "error" argument must be of type function or ' + + 'an instance of Error, RegExp, or Object.' + + common.invalidArgTypeHelper(input) + */ + } + ); +}); + +{ + + assert.throws(() => { + assert.ok((() => Boolean('' === false))()); + }, { + code: "ERR_ASSERTION", + /* TODO(kt3k): Enable this assertion + message: 'The expression evaluated to a falsy value:\n\n' + + " assert.ok((() => Boolean('\\u0001' === false))())\n" + */ + }); + + const errFn = () => { + const err = new TypeError('Wrong value'); + err.code = 404; + throw err; + }; + const errObj = { + name: 'TypeError', + message: 'Wrong value' + }; + assert.throws(errFn, errObj); + + errObj.code = 404; + assert.throws(errFn, errObj); + + // Fail in case a expected property is undefined and not existent on the + // error. + errObj.foo = undefined; + assert.throws( + () => assert.throws(errFn, errObj), + { + code: 'ERR_ASSERTION', + name: 'AssertionError', + /* TODO(kt3k): Enable this assertion + message: `${start}\n${actExp}\n\n` + + ' Comparison {\n' + + ' code: 404,\n' + + '- foo: undefined,\n' + + " message: 'Wrong value',\n" + + " name: 'TypeError'\n" + + ' }' + */ + } + ); + + // Show multiple wrong properties at the same time. + errObj.code = '404'; + assert.throws( + () => assert.throws(errFn, errObj), + { + code: 'ERR_ASSERTION', + name: 'AssertionError', + /* TODO(kt3k): Enable this assertion + message: `${start}\n${actExp}\n\n` + + ' Comparison {\n' + + '+ code: 404,\n' + + "- code: '404',\n" + + '- foo: undefined,\n' + + " message: 'Wrong value',\n" + + " name: 'TypeError'\n" + + ' }' + */ + } + ); + + assert.throws( + () => assert.throws(() => { throw new Error(); }, { foo: 'bar' }, 'foobar'), + { + constructor: assert.AssertionError, + code: 'ERR_ASSERTION', + message: 'foobar' + } + ); + + assert.throws( + () => a.doesNotThrow(() => { throw new Error(); }, { foo: 'bar' }), + { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + /* TODO(kt3k): Enable this assertion + message: 'The "expected" argument must be of type function or an ' + + 'instance of RegExp. Received an instance of Object' + */ + } + ); + + assert.throws(() => { throw new Error('e'); }, new Error('e')); + assert.throws( + () => assert.throws(() => { throw new TypeError('e'); }, new Error('e')), + { + name: 'AssertionError', + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + message: `${start}\n${actExp}\n\n` + + ' Comparison {\n' + + " message: 'e',\n" + + "+ name: 'TypeError'\n" + + "- name: 'Error'\n" + + ' }' + */ + } + ); + assert.throws( + () => assert.throws(() => { throw new Error('foo'); }, new Error('')), + { + name: 'AssertionError', + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + generatedMessage: true, + message: `${start}\n${actExp}\n\n` + + ' Comparison {\n' + + "+ message: 'foo',\n" + + "- message: '',\n" + + " name: 'Error'\n" + + ' }' + */ + } + ); + + assert.throws(() => { throw undefined; }, /undefined/); + assert.throws( + () => a.doesNotThrow(() => { throw undefined; }), + { + name: 'AssertionError', + code: 'ERR_ASSERTION', + message: 'Got unwanted exception.\nActual message: "undefined"' + } + ); +} + +assert.throws( + () => assert.throws(() => { throw new Error(); }, {}), + { + message: "The argument 'error' may not be an empty object. Received {}", + code: 'ERR_INVALID_ARG_VALUE' + } +); + +assert.throws( + () => a.throws( + () => { throw 'foo'; }, + 'foo' + ), + { + code: 'ERR_AMBIGUOUS_ARGUMENT', + message: 'The "error/message" argument is ambiguous. ' + + 'The error "foo" is identical to the message.' + } +); + +assert.throws( + () => a.throws( + () => { throw new TypeError('foo'); }, + 'foo' + ), + { + code: 'ERR_AMBIGUOUS_ARGUMENT', + message: 'The "error/message" argument is ambiguous. ' + + 'The error message "foo" is identical to the message.' + } +); + +// Should not throw. +assert.throws(() => { throw null; }, 'foo'); + +assert.throws( + () => assert.strictEqual([], []), + { + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + message: 'Values have same structure but are not reference-equal:\n\n[]\n' + */ + } +); + +{ + const args = (function() { return arguments; })('a'); + assert.throws( + () => assert.strictEqual(args, { 0: 'a' }), + { + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + message: 'Expected "actual" to be reference-equal to "expected":\n' + + '+ actual - expected\n\n' + + "+ [Arguments] {\n- {\n '0': 'a'\n }" + */ + } + ); +} + +assert.throws( + () => { throw new TypeError('foobar'); }, + { + message: /foo/, + name: /^TypeError$/ + } +); + +assert.throws( + () => assert.throws( + () => { throw new TypeError('foobar'); }, + { + message: /fooa/, + name: /^TypeError$/ + } + ), + { + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + message: `${start}\n${actExp}\n\n` + + ' Comparison {\n' + + "+ message: 'foobar',\n" + + '- message: /fooa/,\n' + + " name: 'TypeError'\n" + + ' }' + */ + } +); + +{ + let actual = null; + const expected = { message: 'foo' }; + assert.throws( + () => assert.throws( + () => { throw actual; }, + expected + ), + { + operator: 'throws', + actual, + expected, + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + generatedMessage: true, + message: `${start}\n${actExp}\n\n` + + '+ null\n' + + '- {\n' + + "- message: 'foo'\n" + + '- }' + */ + } + ); + + actual = 'foobar'; + const message = 'message'; + assert.throws( + () => assert.throws( + () => { throw actual; }, + { message: 'foobar' }, + message + ), + { + actual, + message, + operator: 'throws', + generatedMessage: false + } + ); +} + +// Indicate where the strings diverge. +assert.throws( + () => assert.strictEqual('test test', 'test foobar'), + { + code: 'ERR_ASSERTION', + name: 'AssertionError', + /* TODO(kt3k): Enable this assertion + message: strictEqualMessageStart + + '+ actual - expected\n\n' + + "+ 'test test'\n" + + "- 'test foobar'\n" + + ' ^' + */ + } +); + +// Check for reference-equal objects in `notStrictEqual()` +assert.throws( + () => { + const obj = {}; + assert.notStrictEqual(obj, obj); + }, + { + code: 'ERR_ASSERTION', + name: 'AssertionError', + /* TODO(kt3k): Enable this assertion + message: 'Expected "actual" not to be reference-equal to "expected": {}' + */ + } +); + +assert.throws( + () => { + const obj = { a: true }; + assert.notStrictEqual(obj, obj); + }, + { + code: 'ERR_ASSERTION', + name: 'AssertionError', + /* TODO(kt3k): Enable this assertion + message: 'Expected "actual" not to be reference-equal to "expected":\n\n' + + '{\n a: true\n}\n' + */ + } +); + +{ + let threw = false; + try { + assert.deepStrictEqual(Array(100).fill(1), 'foobar'); + } catch (err) { + threw = true; + /* TODO(kt3k): Enable this assertion + assert(/actual: \[Array],\n expected: 'foobar',/.test(inspect(err))); + */ + } + assert(threw); +} + +assert.throws( + () => a.equal(1), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.deepEqual(/a/), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.notEqual(null), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.notDeepEqual('test'), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.strictEqual({}), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.deepStrictEqual(Symbol()), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.notStrictEqual(5n), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.notDeepStrictEqual(undefined), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.strictEqual(), + { code: 'ERR_MISSING_ARGS' } +); + +assert.throws( + () => a.deepStrictEqual(), + { code: 'ERR_MISSING_ARGS' } +); + +// Verify that `stackStartFunction` works as alternative to `stackStartFn`. +{ + (function hidden() { + const err = new assert.AssertionError({ + actual: 'foo', + operator: 'strictEqual', + stackStartFunction: hidden + }); + const err2 = new assert.AssertionError({ + actual: 'foo', + operator: 'strictEqual', + stackStartFn: hidden + }); + assert(!err.stack.includes('hidden')); + assert(!err2.stack.includes('hidden')); + })(); +} + +assert.throws( + () => assert.throws(() => { throw Symbol('foo'); }, RangeError), + { + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + message: 'The error is expected to be an instance of "RangeError". ' + + 'Received "Symbol(foo)"' + */ + } +); + +assert.throws( + () => assert.throws(() => { throw [1, 2]; }, RangeError), + { + code: 'ERR_ASSERTION', + /* TODO(kt3k): Enable this assertion + message: 'The error is expected to be an instance of "RangeError". ' + + 'Received "[Array]"' + */ + } +); + +{ + const err = new TypeError('foo'); + const validate = (() => () => ({ a: true, b: [ 1, 2, 3 ] }))(); + assert.throws( + () => assert.throws(() => { throw err; }, validate), + { + message: 'The validation function is expected to ' + + `return "true". Received ${inspect(validate())}\n\nCaught ` + + `error:\n\n${err}`, + code: 'ERR_ASSERTION', + actual: err, + expected: validate, + name: 'AssertionError', + operator: 'throws', + } + ); +} + +// TODO(kt3k): Enable these when "vm" is ready. +/* +assert.throws( + () => { + const script = new vm.Script('new RangeError("foobar");'); + const context = vm.createContext(); + const err = script.runInContext(context); + assert.throws(() => { throw err; }, RangeError); + }, + { + message: 'The error is expected to be an instance of "RangeError". ' + + 'Received an error with identical name but a different ' + + 'prototype.\n\nError message:\n\nfoobar' + } +); +*/ + +// Multiple assert.match() tests. +{ + assert.throws( + () => assert.match(/abc/, 'string'), + { + code: 'ERR_INVALID_ARG_TYPE', + /* TODO(kt3k): Enable this assertion + message: 'The "regexp" argument must be an instance of RegExp. ' + + "Received type string ('string')" + */ + } + ); + assert.throws( + () => assert.match('string', /abc/), + { + actual: 'string', + expected: /abc/, + operator: 'match', + /* TODO(kt3k): Enable this assertion + message: 'The input did not match the regular expression /abc/. ' + + "Input:\n\n'string'\n", + generatedMessage: true + */ + } + ); + assert.throws( + () => assert.match('string', /abc/, 'foobar'), + { + actual: 'string', + expected: /abc/, + operator: 'match', + message: 'foobar', + generatedMessage: false + } + ); + const errorMessage = new RangeError('foobar'); + assert.throws( + () => assert.match('string', /abc/, errorMessage), + errorMessage + ); + assert.throws( + () => assert.match({ abc: 123 }, /abc/), + { + actual: { abc: 123 }, + expected: /abc/, + operator: 'match', + /* TODO(kt3k): Enable this assertion + message: 'The "string" argument must be of type string. ' + + 'Received type object ({ abc: 123 })', + generatedMessage: true + */ + } + ); + assert.match('I will pass', /pass$/); +} + +// Multiple assert.doesNotMatch() tests. +{ + assert.throws( + () => assert.doesNotMatch(/abc/, 'string'), + { + code: 'ERR_INVALID_ARG_TYPE', + /* TODO(kt3k): Enable this assertion + message: 'The "regexp" argument must be an instance of RegExp. ' + + "Received type string ('string')" + */ + } + ); + assert.throws( + () => assert.doesNotMatch('string', /string/), + { + actual: 'string', + expected: /string/, + operator: 'doesNotMatch', + /* TODO(kt3k): Enable this assertion + message: 'The input was expected to not match the regular expression ' + + "/string/. Input:\n\n'string'\n", + generatedMessage: true + */ + } + ); + assert.throws( + () => assert.doesNotMatch('string', /string/, 'foobar'), + { + actual: 'string', + expected: /string/, + operator: 'doesNotMatch', + message: 'foobar', + generatedMessage: false + } + ); + const errorMessage = new RangeError('foobar'); + assert.throws( + () => assert.doesNotMatch('string', /string/, errorMessage), + errorMessage + ); + assert.throws( + () => assert.doesNotMatch({ abc: 123 }, /abc/), + { + actual: { abc: 123 }, + expected: /abc/, + operator: 'doesNotMatch', + message: 'The "string" argument must be of type string. ' + + 'Received type object ({ abc: 123 })', + /* TODO(kt3k): Enable this assertion + generatedMessage: true + */ + } + ); + assert.doesNotMatch('I will pass', /different$/); +} + +{ + const tempColor = inspect.defaultOptions.colors; + assert.throws(() => { + inspect.defaultOptions.colors = true; + // Guarantee the position indicator is placed correctly. + assert.strictEqual(111554n, 11111115); + }, (err) => { + // TODO(kt3k): Enable this assertion + // assert.strictEqual(inspect(err).split('\n')[5], ' ^'); + inspect.defaultOptions.colors = tempColor; + return true; + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-bad-unicode.js b/cli/tests/node_compat/test/parallel/test-bad-unicode.js new file mode 100644 index 00000000000000..416e1a351bff54 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-bad-unicode.js @@ -0,0 +1,40 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +let exception = null; + +try { + eval('"\\uc/ef"'); +} catch (e) { + exception = e; +} + +assert(exception instanceof SyntaxError); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-alloc.js b/cli/tests/node_compat/test/parallel/test-buffer-alloc.js new file mode 100644 index 00000000000000..c93e80f638ebbc --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-alloc.js @@ -0,0 +1,1181 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const vm = require('vm'); + +const SlowBuffer = require('buffer').SlowBuffer; + +// Verify the maximum Uint8Array size. There is no concrete limit by spec. The +// internal limits should be updated if this fails. +assert.throws( + () => new Uint8Array(2 ** 32 + 1), + { message: 'Invalid typed array length: 4294967297' } +); + +const b = Buffer.allocUnsafe(1024); +assert.strictEqual(b.length, 1024); + +b[0] = -1; +assert.strictEqual(b[0], 255); + +for (let i = 0; i < 1024; i++) { + b[i] = i % 256; +} + +for (let i = 0; i < 1024; i++) { + assert.strictEqual(i % 256, b[i]); +} + +const c = Buffer.allocUnsafe(512); +assert.strictEqual(c.length, 512); + +const d = Buffer.from([]); +assert.strictEqual(d.length, 0); + +// Test offset properties +{ + const b = Buffer.alloc(128); + assert.strictEqual(b.length, 128); + assert.strictEqual(b.byteOffset, 0); + assert.strictEqual(b.offset, 0); +} + +// Test creating a Buffer from a Uint32Array +{ + const ui32 = new Uint32Array(4).fill(42); + const e = Buffer.from(ui32); + for (const [index, value] of e.entries()) { + assert.strictEqual(value, ui32[index]); + } +} +// Test creating a Buffer from a Uint32Array (old constructor) +{ + const ui32 = new Uint32Array(4).fill(42); + const e = Buffer(ui32); + for (const [key, value] of e.entries()) { + assert.deepStrictEqual(value, ui32[key]); + } +} + +// Test invalid encoding for Buffer.toString +assert.throws(() => b.toString('invalid'), + /Unknown encoding: invalid/); +// Invalid encoding for Buffer.write +assert.throws(() => b.write('test string', 0, 5, 'invalid'), + /Unknown encoding: invalid/); +// Unsupported arguments for Buffer.write +assert.throws(() => b.write('test', 'utf8', 0), + { code: 'ERR_INVALID_ARG_TYPE' }); + +// Try to create 0-length buffers. Should not throw. +Buffer.from(''); +Buffer.from('', 'ascii'); +Buffer.from('', 'latin1'); +Buffer.alloc(0); +Buffer.allocUnsafe(0); +new Buffer(''); +new Buffer('', 'ascii'); +new Buffer('', 'latin1'); +new Buffer('', 'binary'); +Buffer(0); + +const outOfRangeError = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError' +}; + +// Try to write a 0-length string beyond the end of b +assert.throws(() => b.write('', 2048), outOfRangeError); + +// Throw when writing to negative offset +assert.throws(() => b.write('a', -1), outOfRangeError); + +// Throw when writing past bounds from the pool +assert.throws(() => b.write('a', 2048), outOfRangeError); + +// Throw when writing to negative offset +assert.throws(() => b.write('a', -1), outOfRangeError); + +// Try to copy 0 bytes worth of data into an empty buffer +b.copy(Buffer.alloc(0), 0, 0, 0); + +// Try to copy 0 bytes past the end of the target buffer +b.copy(Buffer.alloc(0), 1, 1, 1); +b.copy(Buffer.alloc(1), 1, 1, 1); + +// Try to copy 0 bytes from past the end of the source buffer +b.copy(Buffer.alloc(1), 0, 2048, 2048); + +// Testing for smart defaults and ability to pass string values as offset +{ + const writeTest = Buffer.from('abcdes'); + writeTest.write('n', 'ascii'); + assert.throws( + () => writeTest.write('o', '1', 'ascii'), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + writeTest.write('o', 1, 'ascii'); + writeTest.write('d', 2, 'ascii'); + writeTest.write('e', 3, 'ascii'); + writeTest.write('j', 4, 'ascii'); + assert.strictEqual(writeTest.toString(), 'nodejs'); +} + +// Offset points to the end of the buffer and does not throw. +// (see https://github.com/nodejs/node/issues/8127). +Buffer.alloc(1).write('', 1, 0); + +// ASCII slice test +{ + const asciiString = 'hello world'; + + for (let i = 0; i < asciiString.length; i++) { + b[i] = asciiString.charCodeAt(i); + } + const asciiSlice = b.toString('ascii', 0, asciiString.length); + assert.strictEqual(asciiString, asciiSlice); +} + +{ + const asciiString = 'hello world'; + const offset = 100; + + assert.strictEqual(asciiString.length, b.write(asciiString, offset, 'ascii')); + const asciiSlice = b.toString('ascii', offset, offset + asciiString.length); + assert.strictEqual(asciiString, asciiSlice); +} + +{ + const asciiString = 'hello world'; + const offset = 100; + + const sliceA = b.slice(offset, offset + asciiString.length); + const sliceB = b.slice(offset, offset + asciiString.length); + for (let i = 0; i < asciiString.length; i++) { + assert.strictEqual(sliceA[i], sliceB[i]); + } +} + +// UTF-8 slice test +{ + const utf8String = '¡hέlló wôrld!'; + const offset = 100; + + b.write(utf8String, 0, Buffer.byteLength(utf8String), 'utf8'); + let utf8Slice = b.toString('utf8', 0, Buffer.byteLength(utf8String)); + assert.strictEqual(utf8String, utf8Slice); + + assert.strictEqual(Buffer.byteLength(utf8String), + b.write(utf8String, offset, 'utf8')); + utf8Slice = b.toString('utf8', offset, + offset + Buffer.byteLength(utf8String)); + assert.strictEqual(utf8String, utf8Slice); + + const sliceA = b.slice(offset, offset + Buffer.byteLength(utf8String)); + const sliceB = b.slice(offset, offset + Buffer.byteLength(utf8String)); + for (let i = 0; i < Buffer.byteLength(utf8String); i++) { + assert.strictEqual(sliceA[i], sliceB[i]); + } +} + +{ + const slice = b.slice(100, 150); + assert.strictEqual(slice.length, 50); + for (let i = 0; i < 50; i++) { + assert.strictEqual(b[100 + i], slice[i]); + } +} + +{ + // Make sure only top level parent propagates from allocPool + const b = Buffer.allocUnsafe(5); + const c = b.slice(0, 4); + const d = c.slice(0, 2); + assert.strictEqual(b.parent, c.parent); + assert.strictEqual(b.parent, d.parent); +} + +{ + // Also from a non-pooled instance + const b = Buffer.allocUnsafeSlow(5); + const c = b.slice(0, 4); + const d = c.slice(0, 2); + assert.strictEqual(c.parent, d.parent); +} + +{ + // Bug regression test + const testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語 + const buffer = Buffer.allocUnsafe(32); + const size = buffer.write(testValue, 0, 'utf8'); + const slice = buffer.toString('utf8', 0, size); + assert.strictEqual(slice, testValue); +} + +{ + // Test triple slice + const a = Buffer.allocUnsafe(8); + for (let i = 0; i < 8; i++) a[i] = i; + const b = a.slice(4, 8); + assert.strictEqual(b[0], 4); + assert.strictEqual(b[1], 5); + assert.strictEqual(b[2], 6); + assert.strictEqual(b[3], 7); + const c = b.slice(2, 4); + assert.strictEqual(c[0], 6); + assert.strictEqual(c[1], 7); +} + +{ + const d = Buffer.from([23, 42, 255]); + assert.strictEqual(d.length, 3); + assert.strictEqual(d[0], 23); + assert.strictEqual(d[1], 42); + assert.strictEqual(d[2], 255); + assert.deepStrictEqual(d, Buffer.from(d)); +} + +{ + // Test for proper UTF-8 Encoding + const e = Buffer.from('über'); + assert.deepStrictEqual(e, Buffer.from([195, 188, 98, 101, 114])); +} + +{ + // Test for proper ascii Encoding, length should be 4 + const f = Buffer.from('über', 'ascii'); + assert.deepStrictEqual(f, Buffer.from([252, 98, 101, 114])); +} + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + { + // Test for proper UTF16LE encoding, length should be 8 + const f = Buffer.from('über', encoding); + assert.deepStrictEqual(f, Buffer.from([252, 0, 98, 0, 101, 0, 114, 0])); + } + + { + // Length should be 12 + const f = Buffer.from('привет', encoding); + assert.deepStrictEqual( + f, Buffer.from([63, 4, 64, 4, 56, 4, 50, 4, 53, 4, 66, 4]) + ); + assert.strictEqual(f.toString(encoding), 'привет'); + } + + { + const f = Buffer.from([0, 0, 0, 0, 0]); + assert.strictEqual(f.length, 5); + const size = f.write('あいうえお', encoding); + assert.strictEqual(size, 4); + assert.deepStrictEqual(f, Buffer.from([0x42, 0x30, 0x44, 0x30, 0x00])); + } +}); + +{ + const f = Buffer.from('\uD83D\uDC4D', 'utf-16le'); // THUMBS UP SIGN (U+1F44D) + assert.strictEqual(f.length, 4); + assert.deepStrictEqual(f, Buffer.from('3DD84DDC', 'hex')); +} + +// Test construction from arrayish object +{ + const arrayIsh = { 0: 0, 1: 1, 2: 2, 3: 3, length: 4 }; + let g = Buffer.from(arrayIsh); + assert.deepStrictEqual(g, Buffer.from([0, 1, 2, 3])); + const strArrayIsh = { 0: '0', 1: '1', 2: '2', 3: '3', length: 4 }; + g = Buffer.from(strArrayIsh); + assert.deepStrictEqual(g, Buffer.from([0, 1, 2, 3])); +} + +// +// Test toString('base64') +// +assert.strictEqual((Buffer.from('Man')).toString('base64'), 'TWFu'); +assert.strictEqual((Buffer.from('Woman')).toString('base64'), 'V29tYW4='); + +// +// Test toString('base64url') +// +assert.strictEqual((Buffer.from('Man')).toString('base64url'), 'TWFu'); +assert.strictEqual((Buffer.from('Woman')).toString('base64url'), 'V29tYW4'); + +{ + // Test that regular and URL-safe base64 both work both ways + const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff]; + assert.deepStrictEqual(Buffer.from('//++/++/++//', 'base64'), + Buffer.from(expected)); + assert.deepStrictEqual(Buffer.from('__--_--_--__', 'base64'), + Buffer.from(expected)); + assert.deepStrictEqual(Buffer.from('//++/++/++//', 'base64url'), + Buffer.from(expected)); + assert.deepStrictEqual(Buffer.from('__--_--_--__', 'base64url'), + Buffer.from(expected)); +} + +const base64flavors = ['base64', 'base64url']; + +{ + // Test that regular and URL-safe base64 both work both ways with padding + const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff, 0xfb]; + assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64'), + Buffer.from(expected)); + assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64'), + Buffer.from(expected)); + assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64url'), + Buffer.from(expected)); + assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64url'), + Buffer.from(expected)); +} + +{ + // big example + const quote = 'Man is distinguished, not only by his reason, but by this ' + + 'singular passion from other animals, which is a lust ' + + 'of the mind, that by a perseverance of delight in the ' + + 'continued and indefatigable generation of knowledge, ' + + 'exceeds the short vehemence of any carnal pleasure.'; + const expected = 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb' + + '24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlci' + + 'BhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQ' + + 'gYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu' + + 'dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZ' + + 'GdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm' + + '5hbCBwbGVhc3VyZS4='; + assert.strictEqual(Buffer.from(quote).toString('base64'), expected); + assert.strictEqual( + Buffer.from(quote).toString('base64url'), + expected.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '') + ); + + base64flavors.forEach((encoding) => { + let b = Buffer.allocUnsafe(1024); + let bytesWritten = b.write(expected, 0, encoding); + assert.strictEqual(quote.length, bytesWritten); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); + + // Check that the base64 decoder ignores whitespace + const expectedWhite = `${expected.slice(0, 60)} \n` + + `${expected.slice(60, 120)} \n` + + `${expected.slice(120, 180)} \n` + + `${expected.slice(180, 240)} \n` + + `${expected.slice(240, 300)}\n` + + `${expected.slice(300, 360)}\n`; + b = Buffer.allocUnsafe(1024); + bytesWritten = b.write(expectedWhite, 0, encoding); + assert.strictEqual(quote.length, bytesWritten); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); + + // Check that the base64 decoder on the constructor works + // even in the presence of whitespace. + b = Buffer.from(expectedWhite, encoding); + assert.strictEqual(quote.length, b.length); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); + + // Check that the base64 decoder ignores illegal chars + const expectedIllegal = expected.slice(0, 60) + ' \x80' + + expected.slice(60, 120) + ' \xff' + + expected.slice(120, 180) + ' \x00' + + expected.slice(180, 240) + ' \x98' + + expected.slice(240, 300) + '\x03' + + expected.slice(300, 360); + b = Buffer.from(expectedIllegal, encoding); + assert.strictEqual(quote.length, b.length); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); + }); +} + +base64flavors.forEach((encoding) => { + assert.strictEqual(Buffer.from('', encoding).toString(), ''); + assert.strictEqual(Buffer.from('K', encoding).toString(), ''); + + // multiple-of-4 with padding + assert.strictEqual(Buffer.from('Kg==', encoding).toString(), '*'); + assert.strictEqual(Buffer.from('Kio=', encoding).toString(), '*'.repeat(2)); + assert.strictEqual(Buffer.from('Kioq', encoding).toString(), '*'.repeat(3)); + assert.strictEqual( + Buffer.from('KioqKg==', encoding).toString(), '*'.repeat(4)); + assert.strictEqual( + Buffer.from('KioqKio=', encoding).toString(), '*'.repeat(5)); + assert.strictEqual( + Buffer.from('KioqKioq', encoding).toString(), '*'.repeat(6)); + assert.strictEqual(Buffer.from('KioqKioqKg==', encoding).toString(), + '*'.repeat(7)); + assert.strictEqual(Buffer.from('KioqKioqKio=', encoding).toString(), + '*'.repeat(8)); + assert.strictEqual(Buffer.from('KioqKioqKioq', encoding).toString(), + '*'.repeat(9)); + assert.strictEqual(Buffer.from('KioqKioqKioqKg==', encoding).toString(), + '*'.repeat(10)); + assert.strictEqual(Buffer.from('KioqKioqKioqKio=', encoding).toString(), + '*'.repeat(11)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioq', encoding).toString(), + '*'.repeat(12)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg==', encoding).toString(), + '*'.repeat(13)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio=', encoding).toString(), + '*'.repeat(14)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioq', encoding).toString(), + '*'.repeat(15)); + assert.strictEqual( + Buffer.from('KioqKioqKioqKioqKioqKg==', encoding).toString(), + '*'.repeat(16)); + assert.strictEqual( + Buffer.from('KioqKioqKioqKioqKioqKio=', encoding).toString(), + '*'.repeat(17)); + assert.strictEqual( + Buffer.from('KioqKioqKioqKioqKioqKioq', encoding).toString(), + '*'.repeat(18)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKg==', + encoding).toString(), + '*'.repeat(19)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKio=', + encoding).toString(), + '*'.repeat(20)); + + // No padding, not a multiple of 4 + assert.strictEqual(Buffer.from('Kg', encoding).toString(), '*'); + assert.strictEqual(Buffer.from('Kio', encoding).toString(), '*'.repeat(2)); + assert.strictEqual(Buffer.from('KioqKg', encoding).toString(), '*'.repeat(4)); + assert.strictEqual( + Buffer.from('KioqKio', encoding).toString(), '*'.repeat(5)); + assert.strictEqual(Buffer.from('KioqKioqKg', encoding).toString(), + '*'.repeat(7)); + assert.strictEqual(Buffer.from('KioqKioqKio', encoding).toString(), + '*'.repeat(8)); + assert.strictEqual(Buffer.from('KioqKioqKioqKg', encoding).toString(), + '*'.repeat(10)); + assert.strictEqual(Buffer.from('KioqKioqKioqKio', encoding).toString(), + '*'.repeat(11)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg', encoding).toString(), + '*'.repeat(13)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio', encoding).toString(), + '*'.repeat(14)); + assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKg', encoding).toString(), + '*'.repeat(16)); + assert.strictEqual( + Buffer.from('KioqKioqKioqKioqKioqKio', encoding).toString(), + '*'.repeat(17)); + assert.strictEqual( + Buffer.from('KioqKioqKioqKioqKioqKioqKg', encoding).toString(), + '*'.repeat(19)); + assert.strictEqual( + Buffer.from('KioqKioqKioqKioqKioqKioqKio', encoding).toString(), + '*'.repeat(20)); +}); + +// Handle padding graciously, multiple-of-4 or not +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw==', 'base64').length, + 32 +); +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw==', 'base64url') + .length, + 32 +); +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw=', 'base64').length, + 32 +); +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw=', 'base64url') + .length, + 32 +); +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw', 'base64').length, + 32 +); +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw', 'base64url') + .length, + 32 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==', 'base64').length, + 31 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==', 'base64url') + .length, + 31 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=', 'base64').length, + 31 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=', 'base64url') + .length, + 31 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg', 'base64').length, + 31 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg', 'base64url').length, + 31 +); + +{ +// This string encodes single '.' character in UTF-16 + const dot = Buffer.from('//4uAA==', 'base64'); + assert.strictEqual(dot[0], 0xff); + assert.strictEqual(dot[1], 0xfe); + assert.strictEqual(dot[2], 0x2e); + assert.strictEqual(dot[3], 0x00); + assert.strictEqual(dot.toString('base64'), '//4uAA=='); +} + +{ +// This string encodes single '.' character in UTF-16 + const dot = Buffer.from('//4uAA', 'base64url'); + assert.strictEqual(dot[0], 0xff); + assert.strictEqual(dot[1], 0xfe); + assert.strictEqual(dot[2], 0x2e); + assert.strictEqual(dot[3], 0x00); + assert.strictEqual(dot.toString('base64url'), '__4uAA'); +} + +{ + // Writing base64 at a position > 0 should not mangle the result. + // + // https://github.com/joyent/node/issues/402 + const segments = ['TWFkbmVzcz8h', 'IFRoaXM=', 'IGlz', 'IG5vZGUuanMh']; + const b = Buffer.allocUnsafe(64); + let pos = 0; + + for (let i = 0; i < segments.length; ++i) { + pos += b.write(segments[i], pos, 'base64'); + } + assert.strictEqual(b.toString('latin1', 0, pos), + 'Madness?! This is node.js!'); +} + +{ + // Writing base64url at a position > 0 should not mangle the result. + // + // https://github.com/joyent/node/issues/402 + const segments = ['TWFkbmVzcz8h', 'IFRoaXM', 'IGlz', 'IG5vZGUuanMh']; + const b = Buffer.allocUnsafe(64); + let pos = 0; + + for (let i = 0; i < segments.length; ++i) { + pos += b.write(segments[i], pos, 'base64url'); + } + assert.strictEqual(b.toString('latin1', 0, pos), + 'Madness?! This is node.js!'); +} + +// Regression test for https://github.com/nodejs/node/issues/3496. +assert.strictEqual(Buffer.from('=bad'.repeat(1e4), 'base64').length, 0); + +// Regression test for https://github.com/nodejs/node/issues/11987. +assert.deepStrictEqual(Buffer.from('w0 ', 'base64'), + Buffer.from('w0', 'base64')); + +// Regression test for https://github.com/nodejs/node/issues/13657. +assert.deepStrictEqual(Buffer.from(' YWJvcnVtLg', 'base64'), + Buffer.from('YWJvcnVtLg', 'base64')); + +{ + // Creating buffers larger than pool size. + const l = Buffer.poolSize + 5; + const s = 'h'.repeat(l); + const b = Buffer.from(s); + + for (let i = 0; i < l; i++) { + assert.strictEqual(b[i], 'h'.charCodeAt(0)); + } + + const sb = b.toString(); + assert.strictEqual(sb.length, s.length); + assert.strictEqual(sb, s); +} + +{ + // test hex toString + const hexb = Buffer.allocUnsafe(256); + for (let i = 0; i < 256; i++) { + hexb[i] = i; + } + const hexStr = hexb.toString('hex'); + assert.strictEqual(hexStr, + '000102030405060708090a0b0c0d0e0f' + + '101112131415161718191a1b1c1d1e1f' + + '202122232425262728292a2b2c2d2e2f' + + '303132333435363738393a3b3c3d3e3f' + + '404142434445464748494a4b4c4d4e4f' + + '505152535455565758595a5b5c5d5e5f' + + '606162636465666768696a6b6c6d6e6f' + + '707172737475767778797a7b7c7d7e7f' + + '808182838485868788898a8b8c8d8e8f' + + '909192939495969798999a9b9c9d9e9f' + + 'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf' + + 'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf' + + 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf' + + 'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf' + + 'e0e1e2e3e4e5e6e7e8e9eaebecedeeef' + + 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); + + const hexb2 = Buffer.from(hexStr, 'hex'); + for (let i = 0; i < 256; i++) { + assert.strictEqual(hexb2[i], hexb[i]); + } +} + +// Test single hex character is discarded. +assert.strictEqual(Buffer.from('A', 'hex').length, 0); + +// Test that if a trailing character is discarded, rest of string is processed. +assert.deepStrictEqual(Buffer.from('Abx', 'hex'), Buffer.from('Ab', 'hex')); + +// Test single base64 char encodes as 0. +assert.strictEqual(Buffer.from('A', 'base64').length, 0); + + +{ + // Test an invalid slice end. + const b = Buffer.from([1, 2, 3, 4, 5]); + const b2 = b.toString('hex', 1, 10000); + const b3 = b.toString('hex', 1, 5); + const b4 = b.toString('hex', 1); + assert.strictEqual(b2, b3); + assert.strictEqual(b2, b4); +} + +function buildBuffer(data) { + if (Array.isArray(data)) { + const buffer = Buffer.allocUnsafe(data.length); + data.forEach((v, k) => buffer[k] = v); + return buffer; + } + return null; +} + +const x = buildBuffer([0x81, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72]); + +assert.strictEqual(x.inspect(), ''); + +{ + const z = x.slice(4); + assert.strictEqual(z.length, 5); + assert.strictEqual(z[0], 0x6f); + assert.strictEqual(z[1], 0xa3); + assert.strictEqual(z[2], 0x62); + assert.strictEqual(z[3], 0x61); + assert.strictEqual(z[4], 0x72); +} + +{ + const z = x.slice(0); + assert.strictEqual(z.length, x.length); +} + +{ + const z = x.slice(0, 4); + assert.strictEqual(z.length, 4); + assert.strictEqual(z[0], 0x81); + assert.strictEqual(z[1], 0xa3); +} + +{ + const z = x.slice(0, 9); + assert.strictEqual(z.length, 9); +} + +{ + const z = x.slice(1, 4); + assert.strictEqual(z.length, 3); + assert.strictEqual(z[0], 0xa3); +} + +{ + const z = x.slice(2, 4); + assert.strictEqual(z.length, 2); + assert.strictEqual(z[0], 0x66); + assert.strictEqual(z[1], 0x6f); +} + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + const b = Buffer.allocUnsafe(10); + b.write('あいうえお', encoding); + assert.strictEqual(b.toString(encoding), 'あいうえお'); +}); + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + const b = Buffer.allocUnsafe(11); + b.write('あいうえお', 1, encoding); + assert.strictEqual(b.toString(encoding, 1), 'あいうえお'); +}); + +{ + // latin1 encoding should write only one byte per character. + const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); + let s = String.fromCharCode(0xffff); + b.write(s, 0, 'latin1'); + assert.strictEqual(b[0], 0xff); + assert.strictEqual(b[1], 0xad); + assert.strictEqual(b[2], 0xbe); + assert.strictEqual(b[3], 0xef); + s = String.fromCharCode(0xaaee); + b.write(s, 0, 'latin1'); + assert.strictEqual(b[0], 0xee); + assert.strictEqual(b[1], 0xad); + assert.strictEqual(b[2], 0xbe); + assert.strictEqual(b[3], 0xef); +} + +{ + // Binary encoding should write only one byte per character. + const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); + let s = String.fromCharCode(0xffff); + b.write(s, 0, 'latin1'); + assert.strictEqual(b[0], 0xff); + assert.strictEqual(b[1], 0xad); + assert.strictEqual(b[2], 0xbe); + assert.strictEqual(b[3], 0xef); + s = String.fromCharCode(0xaaee); + b.write(s, 0, 'latin1'); + assert.strictEqual(b[0], 0xee); + assert.strictEqual(b[1], 0xad); + assert.strictEqual(b[2], 0xbe); + assert.strictEqual(b[3], 0xef); +} + +{ + // https://github.com/nodejs/node-v0.x-archive/pull/1210 + // Test UTF-8 string includes null character + let buf = Buffer.from('\0'); + assert.strictEqual(buf.length, 1); + buf = Buffer.from('\0\0'); + assert.strictEqual(buf.length, 2); +} + +{ + const buf = Buffer.allocUnsafe(2); + assert.strictEqual(buf.write(''), 0); // 0bytes + assert.strictEqual(buf.write('\0'), 1); // 1byte (v8 adds null terminator) + assert.strictEqual(buf.write('a\0'), 2); // 1byte * 2 + assert.strictEqual(buf.write('あ'), 0); // 3bytes + assert.strictEqual(buf.write('\0あ'), 1); // 1byte + 3bytes + assert.strictEqual(buf.write('\0\0あ'), 2); // 1byte * 2 + 3bytes +} + +{ + const buf = Buffer.allocUnsafe(10); + assert.strictEqual(buf.write('あいう'), 9); // 3bytes * 3 (v8 adds null term.) + assert.strictEqual(buf.write('あいう\0'), 10); // 3bytes * 3 + 1byte +} + +{ + // https://github.com/nodejs/node-v0.x-archive/issues/243 + // Test write() with maxLength + const buf = Buffer.allocUnsafe(4); + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 1, 2, 'utf8'), 2); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0x61); + assert.strictEqual(buf[2], 0x62); + assert.strictEqual(buf[3], 0xFF); + + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 1, 4), 3); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0x61); + assert.strictEqual(buf[2], 0x62); + assert.strictEqual(buf[3], 0x63); + + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 1, 2, 'utf8'), 2); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0x61); + assert.strictEqual(buf[2], 0x62); + assert.strictEqual(buf[3], 0xFF); + + buf.fill(0xFF); + assert.strictEqual(buf.write('abcdef', 1, 2, 'hex'), 2); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0xAB); + assert.strictEqual(buf[2], 0xCD); + assert.strictEqual(buf[3], 0xFF); + + ['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 0, 2, encoding), 2); + assert.strictEqual(buf[0], 0x61); + assert.strictEqual(buf[1], 0x00); + assert.strictEqual(buf[2], 0xFF); + assert.strictEqual(buf[3], 0xFF); + }); +} + +{ + // Test offset returns are correct + const b = Buffer.allocUnsafe(16); + assert.strictEqual(b.writeUInt32LE(0, 0), 4); + assert.strictEqual(b.writeUInt16LE(0, 4), 6); + assert.strictEqual(b.writeUInt8(0, 6), 7); + assert.strictEqual(b.writeInt8(0, 7), 8); + assert.strictEqual(b.writeDoubleLE(0, 8), 16); +} + +{ + // Test unmatched surrogates not producing invalid utf8 output + // ef bf bd = utf-8 representation of unicode replacement character + // see https://codereview.chromium.org/121173009/ + const buf = Buffer.from('ab\ud800cd', 'utf8'); + assert.strictEqual(buf[0], 0x61); + assert.strictEqual(buf[1], 0x62); + assert.strictEqual(buf[2], 0xef); + assert.strictEqual(buf[3], 0xbf); + assert.strictEqual(buf[4], 0xbd); + assert.strictEqual(buf[5], 0x63); + assert.strictEqual(buf[6], 0x64); +} + +{ + // Test for buffer overrun + const buf = Buffer.from([0, 0, 0, 0, 0]); // length: 5 + const sub = buf.slice(0, 4); // length: 4 + assert.strictEqual(sub.write('12345', 'latin1'), 4); + assert.strictEqual(buf[4], 0); + assert.strictEqual(sub.write('12345', 'binary'), 4); + assert.strictEqual(buf[4], 0); +} + +{ + // Test alloc with fill option + const buf = Buffer.alloc(5, '800A', 'hex'); + assert.strictEqual(buf[0], 128); + assert.strictEqual(buf[1], 10); + assert.strictEqual(buf[2], 128); + assert.strictEqual(buf[3], 10); + assert.strictEqual(buf[4], 128); +} + + +// Check for fractional length args, junk length args, etc. +// https://github.com/joyent/node/issues/1758 + +// Call .fill() first, stops valgrind warning about uninitialized memory reads. +Buffer.allocUnsafe(3.3).fill().toString(); +// Throws bad argument error in commit 43cb4ec +Buffer.alloc(3.3).fill().toString(); +assert.strictEqual(Buffer.allocUnsafe(3.3).length, 3); +assert.strictEqual(Buffer.from({ length: 3.3 }).length, 3); +assert.strictEqual(Buffer.from({ length: 'BAM' }).length, 0); + +// Make sure that strings are not coerced to numbers. +assert.strictEqual(Buffer.from('99').length, 2); +assert.strictEqual(Buffer.from('13.37').length, 5); + +// Ensure that the length argument is respected. +['ascii', 'utf8', 'hex', 'base64', 'latin1', 'binary'].forEach((enc) => { + assert.strictEqual(Buffer.allocUnsafe(1).write('aaaaaa', 0, 1, enc), 1); +}); + +{ + // Regression test, guard against buffer overrun in the base64 decoder. + const a = Buffer.allocUnsafe(3); + const b = Buffer.from('xxx'); + a.write('aaaaaaaa', 'base64'); + assert.strictEqual(b.toString(), 'xxx'); +} + +// issue GH-3416 +Buffer.from(Buffer.allocUnsafe(0), 0, 0); + +// issue GH-5587 +assert.throws( + () => Buffer.alloc(8).writeFloatLE(0, 5), + outOfRangeError +); +assert.throws( + () => Buffer.alloc(16).writeDoubleLE(0, 9), + outOfRangeError +); + +// Attempt to overflow buffers, similar to previous bug in array buffers +assert.throws( + () => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff), + outOfRangeError +); +assert.throws( + () => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff), + outOfRangeError +); + +// Ensure negative values can't get past offset +assert.throws( + () => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1), + outOfRangeError +); +assert.throws( + () => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1), + outOfRangeError +); + +// Test for common write(U)IntLE/BE +{ + let buf = Buffer.allocUnsafe(3); + buf.writeUIntLE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); + assert.strictEqual(buf.readUIntLE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeUIntBE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); + assert.strictEqual(buf.readUIntBE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeIntLE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); + assert.strictEqual(buf.readIntLE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeIntBE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); + assert.strictEqual(buf.readIntBE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeIntLE(-0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xaa, 0xcb, 0xed]); + assert.strictEqual(buf.readIntLE(0, 3), -0x123456); + + buf.fill(0xFF); + buf.writeIntBE(-0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xaa]); + assert.strictEqual(buf.readIntBE(0, 3), -0x123456); + + buf.fill(0xFF); + buf.writeIntLE(-0x123400, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0xcc, 0xed]); + assert.strictEqual(buf.readIntLE(0, 3), -0x123400); + + buf.fill(0xFF); + buf.writeIntBE(-0x123400, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcc, 0x00]); + assert.strictEqual(buf.readIntBE(0, 3), -0x123400); + + buf.fill(0xFF); + buf.writeIntLE(-0x120000, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0xee]); + assert.strictEqual(buf.readIntLE(0, 3), -0x120000); + + buf.fill(0xFF); + buf.writeIntBE(-0x120000, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xee, 0x00, 0x00]); + assert.strictEqual(buf.readIntBE(0, 3), -0x120000); + + buf = Buffer.allocUnsafe(5); + buf.writeUIntLE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); + assert.strictEqual(buf.readUIntLE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeUIntBE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); + assert.strictEqual(buf.readUIntBE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeIntLE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); + assert.strictEqual(buf.readIntLE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeIntBE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); + assert.strictEqual(buf.readIntBE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeIntLE(-0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x70, 0x87, 0xa9, 0xcb, 0xed]); + assert.strictEqual(buf.readIntLE(0, 5), -0x1234567890); + + buf.fill(0xFF); + buf.writeIntBE(-0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xa9, 0x87, 0x70]); + assert.strictEqual(buf.readIntBE(0, 5), -0x1234567890); + + buf.fill(0xFF); + buf.writeIntLE(-0x0012000000, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0x00, 0xee, 0xff]); + assert.strictEqual(buf.readIntLE(0, 5), -0x0012000000); + + buf.fill(0xFF); + buf.writeIntBE(-0x0012000000, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0xff, 0xee, 0x00, 0x00, 0x00]); + assert.strictEqual(buf.readIntBE(0, 5), -0x0012000000); +} + +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/5482: +// should throw but not assert in C++ land. +assert.throws( + () => Buffer.from('', 'buffer'), + { + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: 'Unknown encoding: buffer' + } +); + +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/6111. +// Constructing a buffer from another buffer should a) work, and b) not corrupt +// the source buffer. +{ + const a = [...Array(128).keys()]; // [0, 1, 2, 3, ... 126, 127] + const b = Buffer.from(a); + const c = Buffer.from(b); + assert.strictEqual(b.length, a.length); + assert.strictEqual(c.length, a.length); + for (let i = 0, k = a.length; i < k; ++i) { + assert.strictEqual(a[i], i); + assert.strictEqual(b[i], i); + assert.strictEqual(c[i], i); + } +} + +if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check + // Test truncation after decode + const crypto = require('crypto'); + + const b1 = Buffer.from('YW55=======', 'base64'); + const b2 = Buffer.from('YW55', 'base64'); + + assert.strictEqual( + crypto.createHash('sha1').update(b1).digest('hex'), + crypto.createHash('sha1').update(b2).digest('hex') + ); +} else { + common.printSkipMessage('missing crypto'); +} + +const ps = Buffer.poolSize; +Buffer.poolSize = 0; +assert(Buffer.allocUnsafe(1).parent instanceof ArrayBuffer); +Buffer.poolSize = ps; + +assert.throws( + () => Buffer.allocUnsafe(10).copy(), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "target" argument must be an instance of Buffer or ' + + 'Uint8Array. Received undefined' + }); + +assert.throws(() => Buffer.from(), { + name: 'TypeError', + message: 'The first argument must be of type string or an instance of ' + + 'Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined' +}); +assert.throws(() => Buffer.from(null), { + name: 'TypeError', + message: 'The first argument must be of type string or an instance of ' + + 'Buffer, ArrayBuffer, or Array or an Array-like Object. Received null' +}); + +// Test prototype getters don't throw +assert.strictEqual(Buffer.prototype.parent, undefined); +assert.strictEqual(Buffer.prototype.offset, undefined); +assert.strictEqual(SlowBuffer.prototype.parent, undefined); +assert.strictEqual(SlowBuffer.prototype.offset, undefined); + + +{ + // Test that large negative Buffer length inputs don't affect the pool offset. + // Use the fromArrayLike() variant here because it's more lenient + // about its input and passes the length directly to allocate(). + assert.deepStrictEqual(Buffer.from({ length: -Buffer.poolSize }), + Buffer.from('')); + assert.deepStrictEqual(Buffer.from({ length: -100 }), + Buffer.from('')); + + // Check pool offset after that by trying to write string into the pool. + Buffer.from('abc'); +} + + +// Test that ParseArrayIndex handles full uint32 +{ + const errMsg = common.expectsError({ + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"offset" is outside of buffer bounds' + }); + assert.throws(() => Buffer.from(new ArrayBuffer(0), -1 >>> 0), errMsg); +} + +// ParseArrayIndex() should reject values that don't fit in a 32 bits size_t. +assert.throws(() => { + const a = Buffer.alloc(1); + const b = Buffer.alloc(1); + a.copy(b, 0, 0x100000000, 0x100000001); +}, outOfRangeError); + +// Unpooled buffer (replaces SlowBuffer) +{ + const ubuf = Buffer.allocUnsafeSlow(10); + assert(ubuf); + assert(ubuf.buffer); + assert.strictEqual(ubuf.buffer.byteLength, 10); +} + +// Regression test to verify that an empty ArrayBuffer does not throw. +Buffer.from(new ArrayBuffer()); + +// TODO(kt3k): Enable this test when vm.runInNewContext is available +// Test that ArrayBuffer from a different context is detected correctly. +// const arrayBuf = vm.runInNewContext('new ArrayBuffer()'); +// Buffer.from(arrayBuf); +// Buffer.from({ buffer: arrayBuf }); + +assert.throws(() => Buffer.alloc({ valueOf: () => 1 }), + /"size" argument must be of type number/); +assert.throws(() => Buffer.alloc({ valueOf: () => -1 }), + /"size" argument must be of type number/); + +assert.strictEqual(Buffer.prototype.toLocaleString, Buffer.prototype.toString); +{ + const buf = Buffer.from('test'); + assert.strictEqual(buf.toLocaleString(), buf.toString()); +} + +assert.throws(() => { + Buffer.alloc(0x1000, 'This is not correctly encoded', 'hex'); +}, { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError' +}); + +assert.throws(() => { + Buffer.alloc(0x1000, 'c', 'hex'); +}, { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError' +}); + +assert.throws(() => { + Buffer.alloc(1, Buffer.alloc(0)); +}, { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError' +}); + +assert.throws(() => { + Buffer.alloc(40, 'x', 20); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-arraybuffer.js b/cli/tests/node_compat/test/parallel/test-buffer-arraybuffer.js new file mode 100644 index 00000000000000..9f515736ec4e2c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-arraybuffer.js @@ -0,0 +1,162 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const LENGTH = 16; + +const ab = new ArrayBuffer(LENGTH); +const dv = new DataView(ab); +const ui = new Uint8Array(ab); +const buf = Buffer.from(ab); + + +assert.ok(buf instanceof Buffer); +assert.strictEqual(buf.parent, buf.buffer); +assert.strictEqual(buf.buffer, ab); +assert.strictEqual(buf.length, ab.byteLength); + + +buf.fill(0xC); +for (let i = 0; i < LENGTH; i++) { + assert.strictEqual(ui[i], 0xC); + ui[i] = 0xF; + assert.strictEqual(buf[i], 0xF); +} + +buf.writeUInt32LE(0xF00, 0); +buf.writeUInt32BE(0xB47, 4); +buf.writeDoubleLE(3.1415, 8); + +assert.strictEqual(dv.getUint32(0, true), 0xF00); +assert.strictEqual(dv.getUint32(4), 0xB47); +assert.strictEqual(dv.getFloat64(8, true), 3.1415); + + +// Now test protecting users from doing stupid things + +// TODO(Soremwar) +// There is an inconsistency on feross implementation on how buffers are checked +// Enable once it's sorted out +// assert.throws(function() { +// function AB() { } +// Object.setPrototypeOf(AB, ArrayBuffer); +// Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype); +// Buffer.from(new AB()); +// }, { +// code: 'ERR_INVALID_ARG_TYPE', +// name: 'TypeError', +// message: 'The first argument must be of type string or an instance of ' + +// 'Buffer, ArrayBuffer, or Array or an Array-like Object. Received ' + +// 'an instance of AB' +// }); + +// Test the byteOffset and length arguments +{ + const ab = new Uint8Array(5); + ab[0] = 1; + ab[1] = 2; + ab[2] = 3; + ab[3] = 4; + ab[4] = 5; + const buf = Buffer.from(ab.buffer, 1, 3); + assert.strictEqual(buf.length, 3); + assert.strictEqual(buf[0], 2); + assert.strictEqual(buf[1], 3); + assert.strictEqual(buf[2], 4); + buf[0] = 9; + assert.strictEqual(ab[1], 9); + + assert.throws(() => Buffer.from(ab.buffer, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"offset" is outside of buffer bounds' + }); + assert.throws(() => Buffer.from(ab.buffer, 3, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"length" is outside of buffer bounds' + }); +} + +// Test the deprecated Buffer() version also +{ + const ab = new Uint8Array(5); + ab[0] = 1; + ab[1] = 2; + ab[2] = 3; + ab[3] = 4; + ab[4] = 5; + const buf = Buffer(ab.buffer, 1, 3); + assert.strictEqual(buf.length, 3); + assert.strictEqual(buf[0], 2); + assert.strictEqual(buf[1], 3); + assert.strictEqual(buf[2], 4); + buf[0] = 9; + assert.strictEqual(ab[1], 9); + + assert.throws(() => Buffer(ab.buffer, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"offset" is outside of buffer bounds' + }); + assert.throws(() => Buffer(ab.buffer, 3, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"length" is outside of buffer bounds' + }); +} + +{ + // If byteOffset is not numeric, it defaults to 0. + const ab = new ArrayBuffer(10); + const expected = Buffer.from(ab, 0); + assert.deepStrictEqual(Buffer.from(ab, 'fhqwhgads'), expected); + assert.deepStrictEqual(Buffer.from(ab, NaN), expected); + assert.deepStrictEqual(Buffer.from(ab, {}), expected); + assert.deepStrictEqual(Buffer.from(ab, []), expected); + + // If byteOffset can be converted to a number, it will be. + assert.deepStrictEqual(Buffer.from(ab, [1]), Buffer.from(ab, 1)); + + // If byteOffset is Infinity, throw. + assert.throws(() => { + Buffer.from(ab, Infinity); + }, { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"offset" is outside of buffer bounds' + }); +} + +{ + // If length is not numeric, it defaults to 0. + const ab = new ArrayBuffer(10); + const expected = Buffer.from(ab, 0, 0); + assert.deepStrictEqual(Buffer.from(ab, 0, 'fhqwhgads'), expected); + assert.deepStrictEqual(Buffer.from(ab, 0, NaN), expected); + assert.deepStrictEqual(Buffer.from(ab, 0, {}), expected); + assert.deepStrictEqual(Buffer.from(ab, 0, []), expected); + + // If length can be converted to a number, it will be. + assert.deepStrictEqual(Buffer.from(ab, 0, [1]), Buffer.from(ab, 0, 1)); + + // If length is Infinity, throw. + assert.throws(() => { + Buffer.from(ab, 0, Infinity); + }, { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"length" is outside of buffer bounds' + }); +} + +// Test an array like entry with the length set to NaN. +assert.deepStrictEqual(Buffer.from({ length: NaN }), Buffer.alloc(0)); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-ascii.js b/cli/tests/node_compat/test/parallel/test-buffer-ascii.js new file mode 100644 index 00000000000000..9b03ddafb29831 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-ascii.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// ASCII conversion in node.js simply masks off the high bits, +// it doesn't do transliteration. +assert.strictEqual(Buffer.from('hérité').toString('ascii'), 'hC)ritC)'); + +// 71 characters, 78 bytes. The ’ character is a triple-byte sequence. +const input = 'C’est, graphiquement, la réunion d’un accent aigu ' + + 'et d’un accent grave.'; + +const expected = 'Cb\u0000\u0019est, graphiquement, la rC)union ' + + 'db\u0000\u0019un accent aigu et db\u0000\u0019un ' + + 'accent grave.'; + +const buf = Buffer.from(input); + +for (let i = 0; i < expected.length; ++i) { + assert.strictEqual(buf.slice(i).toString('ascii'), expected.slice(i)); + + // Skip remainder of multi-byte sequence. + if (input.charCodeAt(i) > 65535) ++i; + if (input.charCodeAt(i) > 127) ++i; +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-badhex.js b/cli/tests/node_compat/test/parallel/test-buffer-badhex.js new file mode 100644 index 00000000000000..abbc7f0b33aaec --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-badhex.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +// Test hex strings and bad hex strings +{ + const buf = Buffer.alloc(4); + assert.strictEqual(buf.length, 4); + assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0])); + assert.strictEqual(buf.write('abcdxx', 0, 'hex'), 2); + assert.deepStrictEqual(buf, Buffer.from([0xab, 0xcd, 0x00, 0x00])); + assert.strictEqual(buf.toString('hex'), 'abcd0000'); + assert.strictEqual(buf.write('abcdef01', 0, 'hex'), 4); + assert.deepStrictEqual(buf, Buffer.from([0xab, 0xcd, 0xef, 0x01])); + assert.strictEqual(buf.toString('hex'), 'abcdef01'); + + const copy = Buffer.from(buf.toString('hex'), 'hex'); + assert.strictEqual(buf.toString('hex'), copy.toString('hex')); +} + +{ + const buf = Buffer.alloc(5); + assert.strictEqual(buf.write('abcdxx', 1, 'hex'), 2); + assert.strictEqual(buf.toString('hex'), '00abcd0000'); +} + +{ + const buf = Buffer.alloc(4); + assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0])); + assert.strictEqual(buf.write('xxabcd', 0, 'hex'), 0); + assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0])); + assert.strictEqual(buf.write('xxab', 1, 'hex'), 0); + assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0])); + assert.strictEqual(buf.write('cdxxab', 0, 'hex'), 1); + assert.deepStrictEqual(buf, Buffer.from([0xcd, 0, 0, 0])); +} + +{ + const buf = Buffer.alloc(256); + for (let i = 0; i < 256; i++) + buf[i] = i; + + const hex = buf.toString('hex'); + assert.deepStrictEqual(Buffer.from(hex, 'hex'), buf); + + const badHex = `${hex.slice(0, 256)}xx${hex.slice(256, 510)}`; + assert.deepStrictEqual(Buffer.from(badHex, 'hex'), buf.slice(0, 128)); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-bigint64.js b/cli/tests/node_compat/test/parallel/test-buffer-bigint64.js new file mode 100644 index 00000000000000..918c7b91d1b2cb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-bigint64.js @@ -0,0 +1,62 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +const buf = Buffer.allocUnsafe(8); + +['LE', 'BE'].forEach(function(endianness) { + // Should allow simple BigInts to be written and read + let val = 123456789n; + buf[`writeBigInt64${endianness}`](val, 0); + let rtn = buf[`readBigInt64${endianness}`](0); + assert.strictEqual(val, rtn); + + // Should allow INT64_MAX to be written and read + val = 0x7fffffffffffffffn; + buf[`writeBigInt64${endianness}`](val, 0); + rtn = buf[`readBigInt64${endianness}`](0); + assert.strictEqual(val, rtn); + + // Should read and write a negative signed 64-bit integer + val = -123456789n; + buf[`writeBigInt64${endianness}`](val, 0); + assert.strictEqual(val, buf[`readBigInt64${endianness}`](0)); + + // Should read and write an unsigned 64-bit integer + val = 123456789n; + buf[`writeBigUInt64${endianness}`](val, 0); + assert.strictEqual(val, buf[`readBigUInt64${endianness}`](0)); + + // Should throw a RangeError upon INT64_MAX+1 being written + assert.throws(function() { + const val = 0x8000000000000000n; + buf[`writeBigInt64${endianness}`](val, 0); + }, RangeError); + + // Should throw a RangeError upon UINT64_MAX+1 being written + assert.throws(function() { + const val = 0x10000000000000000n; + buf[`writeBigUInt64${endianness}`](val, 0); + }, { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "value" is out of range. It must be ' + + '>= 0n and < 2n ** 64n. Received 18_446_744_073_709_551_616n' + }); + + // Should throw a TypeError upon invalid input + assert.throws(function() { + buf[`writeBigInt64${endianness}`]('bad', 0); + }, TypeError); + + // Should throw a TypeError upon invalid input + assert.throws(function() { + buf[`writeBigUInt64${endianness}`]('bad', 0); + }, TypeError); +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-bytelength.js b/cli/tests/node_compat/test/parallel/test-buffer-bytelength.js new file mode 100644 index 00000000000000..e23b3c3c2846a4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-bytelength.js @@ -0,0 +1,142 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const SlowBuffer = require('buffer').SlowBuffer; +const vm = require('vm'); + +[ + [32, 'latin1'], + [NaN, 'utf8'], + [{}, 'latin1'], + [], +].forEach((args) => { + assert.throws( + () => Buffer.byteLength(...args), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "string" argument must be of type string or an instance ' + + 'of Buffer or ArrayBuffer.' + + common.invalidArgTypeHelper(args[0]) + } + ); +}); + +assert.strictEqual(Buffer.byteLength('', undefined, true), -1); + +assert(ArrayBuffer.isView(new Buffer(10))); +assert(ArrayBuffer.isView(new SlowBuffer(10))); +assert(ArrayBuffer.isView(Buffer.alloc(10))); +assert(ArrayBuffer.isView(Buffer.allocUnsafe(10))); +assert(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10))); +assert(ArrayBuffer.isView(Buffer.from(''))); + +// buffer +const incomplete = Buffer.from([0xe4, 0xb8, 0xad, 0xe6, 0x96]); +assert.strictEqual(Buffer.byteLength(incomplete), 5); +const ascii = Buffer.from('abc'); +assert.strictEqual(Buffer.byteLength(ascii), 3); + +// ArrayBuffer +const buffer = new ArrayBuffer(8); +assert.strictEqual(Buffer.byteLength(buffer), 8); + +// TypedArray +const int8 = new Int8Array(8); +assert.strictEqual(Buffer.byteLength(int8), 8); +const uint8 = new Uint8Array(8); +assert.strictEqual(Buffer.byteLength(uint8), 8); +const uintc8 = new Uint8ClampedArray(2); +assert.strictEqual(Buffer.byteLength(uintc8), 2); +const int16 = new Int16Array(8); +assert.strictEqual(Buffer.byteLength(int16), 16); +const uint16 = new Uint16Array(8); +assert.strictEqual(Buffer.byteLength(uint16), 16); +const int32 = new Int32Array(8); +assert.strictEqual(Buffer.byteLength(int32), 32); +const uint32 = new Uint32Array(8); +assert.strictEqual(Buffer.byteLength(uint32), 32); +const float32 = new Float32Array(8); +assert.strictEqual(Buffer.byteLength(float32), 32); +const float64 = new Float64Array(8); +assert.strictEqual(Buffer.byteLength(float64), 64); + +// DataView +const dv = new DataView(new ArrayBuffer(2)); +assert.strictEqual(Buffer.byteLength(dv), 2); + +// Special case: zero length string +assert.strictEqual(Buffer.byteLength('', 'ascii'), 0); +assert.strictEqual(Buffer.byteLength('', 'HeX'), 0); + +// utf8 +assert.strictEqual(Buffer.byteLength('∑éllö wørl∂!', 'utf-8'), 19); +assert.strictEqual(Buffer.byteLength('κλμνξο', 'utf8'), 12); +assert.strictEqual(Buffer.byteLength('挵挶挷挸挹', 'utf-8'), 15); +assert.strictEqual(Buffer.byteLength('𠝹𠱓𠱸', 'UTF8'), 12); +// Without an encoding, utf8 should be assumed +assert.strictEqual(Buffer.byteLength('hey there'), 9); +assert.strictEqual(Buffer.byteLength('𠱸挶νξ#xx :)'), 17); +assert.strictEqual(Buffer.byteLength('hello world', ''), 11); +// It should also be assumed with unrecognized encoding +assert.strictEqual(Buffer.byteLength('hello world', 'abc'), 11); +assert.strictEqual(Buffer.byteLength('ßœ∑≈', 'unkn0wn enc0ding'), 10); + +// base64 +assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'base64'), 11); +assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'BASE64'), 11); +assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE=', 'base64'), 14); +assert.strictEqual(Buffer.byteLength('aGkk', 'base64'), 3); +assert.strictEqual( + Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==', 'base64'), 25 +); +assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'base64url'), 11); +assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'BASE64URL'), 11); +assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE', 'base64url'), 14); +assert.strictEqual(Buffer.byteLength('aGkk', 'base64url'), 3); +assert.strictEqual( + Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw', 'base64url'), 25 +); +// special padding +assert.strictEqual(Buffer.byteLength('aaa=', 'base64'), 2); +assert.strictEqual(Buffer.byteLength('aaaa==', 'base64'), 3); +assert.strictEqual(Buffer.byteLength('aaa=', 'base64url'), 2); +assert.strictEqual(Buffer.byteLength('aaaa==', 'base64url'), 3); + +assert.strictEqual(Buffer.byteLength('Il était tué'), 14); +assert.strictEqual(Buffer.byteLength('Il était tué', 'utf8'), 14); + +['ascii', 'latin1', 'binary'] + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 12); + }); + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'] + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 24); + }); + +// TODO(Soremwar) +// Enable once vm module is available +// // Test that ArrayBuffer from a different context is detected correctly +// const arrayBuf = vm.runInNewContext('new ArrayBuffer()'); +// assert.strictEqual(Buffer.byteLength(arrayBuf), 0); + +// Verify that invalid encodings are treated as utf8 +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + + assert.ok(!Buffer.isEncoding(encoding)); + assert.strictEqual(Buffer.byteLength('foo', encoding), + Buffer.byteLength('foo', 'utf8')); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-compare-offset.js b/cli/tests/node_compat/test/parallel/test-buffer-compare-offset.js new file mode 100644 index 00000000000000..fa4487ee013ba7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-compare-offset.js @@ -0,0 +1,101 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); +const b = Buffer.from([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]); + +assert.strictEqual(a.compare(b), -1); + +// Equivalent to a.compare(b). +assert.strictEqual(a.compare(b, 0), -1); +assert.throws(() => a.compare(b, '0'), { code: 'ERR_INVALID_ARG_TYPE' }); +assert.strictEqual(a.compare(b, undefined), -1); + +// Equivalent to a.compare(b). +assert.strictEqual(a.compare(b, 0, undefined, 0), -1); + +// Zero-length target, return 1 +assert.strictEqual(a.compare(b, 0, 0, 0), 1); +assert.throws( + () => a.compare(b, 0, '0', '0'), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Equivalent to Buffer.compare(a, b.slice(6, 10)) +assert.strictEqual(a.compare(b, 6, 10), 1); + +// Zero-length source, return -1 +assert.strictEqual(a.compare(b, 6, 10, 0, 0), -1); + +// Zero-length source and target, return 0 +assert.strictEqual(a.compare(b, 0, 0, 0, 0), 0); +assert.strictEqual(a.compare(b, 1, 1, 2, 2), 0); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 5)) +assert.strictEqual(a.compare(b, 0, 5, 4), 1); + +// Equivalent to Buffer.compare(a.slice(1), b.slice(5)) +assert.strictEqual(a.compare(b, 5, undefined, 1), 1); + +// Equivalent to Buffer.compare(a.slice(2), b.slice(2, 4)) +assert.strictEqual(a.compare(b, 2, 4, 2), -1); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 7)) +assert.strictEqual(a.compare(b, 0, 7, 4), -1); + +// Equivalent to Buffer.compare(a.slice(4, 6), b.slice(0, 7)); +assert.strictEqual(a.compare(b, 0, 7, 4, 6), -1); + +// Null is ambiguous. +assert.throws( + () => a.compare(b, 0, null), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Values do not get coerced. +assert.throws( + () => a.compare(b, 0, { valueOf: () => 5 }), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Infinity should not be coerced. +assert.throws( + () => a.compare(b, Infinity, -Infinity), + { code: 'ERR_OUT_OF_RANGE' } +); + +// Zero length target because default for targetEnd <= targetSource +assert.strictEqual(a.compare(b, 0xff), 1); + +assert.throws( + () => a.compare(b, '0xff'), + { code: 'ERR_INVALID_ARG_TYPE' } +); +assert.throws( + () => a.compare(b, 0, '0xff'), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +const oor = { code: 'ERR_OUT_OF_RANGE' }; + +assert.throws(() => a.compare(b, 0, 100, 0), oor); +assert.throws(() => a.compare(b, 0, 1, 0, 100), oor); +assert.throws(() => a.compare(b, -1), oor); +assert.throws(() => a.compare(b, 0, Infinity), oor); +assert.throws(() => a.compare(b, 0, 1, -1), oor); +assert.throws(() => a.compare(b, -Infinity, Infinity), oor); +assert.throws(() => a.compare(), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "target" argument must be an instance of ' + + 'Buffer or Uint8Array. Received undefined' +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-concat.js b/cli/tests/node_compat/test/parallel/test-buffer-concat.js new file mode 100644 index 00000000000000..d44b3e1b969dac --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-concat.js @@ -0,0 +1,107 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const zero = []; +const one = [ Buffer.from('asdf') ]; +const long = []; +for (let i = 0; i < 10; i++) long.push(Buffer.from('asdf')); + +const flatZero = Buffer.concat(zero); +const flatOne = Buffer.concat(one); +const flatLong = Buffer.concat(long); +const flatLongLen = Buffer.concat(long, 40); + +assert.strictEqual(flatZero.length, 0); +assert.strictEqual(flatOne.toString(), 'asdf'); + +const check = 'asdf'.repeat(10); + +// A special case where concat used to return the first item, +// if the length is one. This check is to make sure that we don't do that. +assert.notStrictEqual(flatOne, one[0]); +assert.strictEqual(flatLong.toString(), check); +assert.strictEqual(flatLongLen.toString(), check); + +[undefined, null, Buffer.from('hello')].forEach((value) => { + assert.throws(() => { + Buffer.concat(value); + }, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "list" argument must be an instance of Array.' + + `${common.invalidArgTypeHelper(value)}` + }); +}); + +[[42], ['hello', Buffer.from('world')]].forEach((value) => { + assert.throws(() => { + Buffer.concat(value); + }, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "list[0]" argument must be an instance of Buffer ' + + `or Uint8Array.${common.invalidArgTypeHelper(value[0])}` + }); +}); + +assert.throws(() => { + Buffer.concat([Buffer.from('hello'), 3]); +}, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "list[1]" argument must be an instance of Buffer ' + + 'or Uint8Array. Received type number (3)' +}); + +// eslint-disable-next-line node-core/crypto-check +const random10 = common.hasCrypto ? + require('crypto').randomBytes(10) : + Buffer.alloc(10, 1); +const empty = Buffer.alloc(0); + +assert.notDeepStrictEqual(random10, empty); +assert.notDeepStrictEqual(random10, Buffer.alloc(10)); + +assert.deepStrictEqual(Buffer.concat([], 100), empty); +assert.deepStrictEqual(Buffer.concat([random10], 0), empty); +assert.deepStrictEqual(Buffer.concat([random10], 10), random10); +assert.deepStrictEqual(Buffer.concat([random10, random10], 10), random10); +assert.deepStrictEqual(Buffer.concat([empty, random10]), random10); +assert.deepStrictEqual(Buffer.concat([random10, empty, empty]), random10); + +// The tail should be zero-filled +assert.deepStrictEqual(Buffer.concat([empty], 100), Buffer.alloc(100)); +assert.deepStrictEqual(Buffer.concat([empty], 4096), Buffer.alloc(4096)); +assert.deepStrictEqual( + Buffer.concat([random10], 40), + Buffer.concat([random10, Buffer.alloc(30)])); + +assert.deepStrictEqual(Buffer.concat([new Uint8Array([0x41, 0x42]), + new Uint8Array([0x43, 0x44])]), + Buffer.from('ABCD')); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-constants.js b/cli/tests/node_compat/test/parallel/test-buffer-constants.js new file mode 100644 index 00000000000000..a537f318bd10d8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-constants.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +const { kMaxLength, kStringMaxLength } = require('buffer'); +const { MAX_LENGTH, MAX_STRING_LENGTH } = require('buffer').constants; + +assert.strictEqual(typeof MAX_LENGTH, 'number'); +assert.strictEqual(typeof MAX_STRING_LENGTH, 'number'); +assert(MAX_STRING_LENGTH <= MAX_LENGTH); +assert.throws(() => ' '.repeat(MAX_STRING_LENGTH + 1), + /^RangeError: Invalid string length$/); + +' '.repeat(MAX_STRING_LENGTH); // Should not throw. + +// Legacy values match: +assert.strictEqual(kMaxLength, MAX_LENGTH); +assert.strictEqual(kStringMaxLength, MAX_STRING_LENGTH); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-copy.js b/cli/tests/node_compat/test/parallel/test-buffer-copy.js new file mode 100644 index 00000000000000..b51664e78eea98 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-copy.js @@ -0,0 +1,236 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const b = Buffer.allocUnsafe(1024); +const c = Buffer.allocUnsafe(512); + +let cntr = 0; + +{ + // copy 512 bytes, from 0 to 512. + b.fill(++cntr); + c.fill(++cntr); + const copied = b.copy(c, 0, 0, 512); + assert.strictEqual(copied, 512); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +{ + // Current behavior is to coerce values to integers. + b.fill(++cntr); + c.fill(++cntr); + const copied = b.copy(c, '0', '0', '512'); + assert.strictEqual(copied, 512); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +{ + // Floats will be converted to integers via `Math.floor` + b.fill(++cntr); + c.fill(++cntr); + const copied = b.copy(c, 0, 0, 512.5); + assert.strictEqual(copied, 512); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +{ + // Copy c into b, without specifying sourceEnd + b.fill(++cntr); + c.fill(++cntr); + const copied = c.copy(b, 0, 0); + assert.strictEqual(copied, c.length); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(b[i], c[i]); + } +} + +{ + // Copy c into b, without specifying sourceStart + b.fill(++cntr); + c.fill(++cntr); + const copied = c.copy(b, 0); + assert.strictEqual(copied, c.length); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(b[i], c[i]); + } +} + +{ + // Copied source range greater than source length + b.fill(++cntr); + c.fill(++cntr); + const copied = c.copy(b, 0, 0, c.length + 1); + assert.strictEqual(copied, c.length); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(b[i], c[i]); + } +} + +{ + // Copy longer buffer b to shorter c without targetStart + b.fill(++cntr); + c.fill(++cntr); + const copied = b.copy(c); + assert.strictEqual(copied, c.length); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +{ + // Copy starting near end of b to c + b.fill(++cntr); + c.fill(++cntr); + const copied = b.copy(c, 0, b.length - Math.floor(c.length / 2)); + assert.strictEqual(copied, Math.floor(c.length / 2)); + for (let i = 0; i < Math.floor(c.length / 2); i++) { + assert.strictEqual(c[i], b[b.length - Math.floor(c.length / 2) + i]); + } + for (let i = Math.floor(c.length / 2) + 1; i < c.length; i++) { + assert.strictEqual(c[c.length - 1], c[i]); + } +} + +{ + // Try to copy 513 bytes, and check we don't overrun c + b.fill(++cntr); + c.fill(++cntr); + const copied = b.copy(c, 0, 0, 513); + assert.strictEqual(copied, c.length); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +{ + // copy 768 bytes from b into b + b.fill(++cntr); + b.fill(++cntr, 256); + const copied = b.copy(b, 0, 256, 1024); + assert.strictEqual(copied, 768); + for (let i = 0; i < b.length; i++) { + assert.strictEqual(b[i], cntr); + } +} + +// Copy string longer than buffer length (failure will segfault) +const bb = Buffer.allocUnsafe(10); +bb.fill('hello crazy world'); + + +// Try to copy from before the beginning of b. Should not throw. +b.copy(c, 0, 100, 10); + +// Throw with invalid source type +assert.throws( + () => Buffer.prototype.copy.call(0), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); + +// Copy throws at negative targetStart +assert.throws( + () => Buffer.allocUnsafe(5).copy(Buffer.allocUnsafe(5), -1, 0), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "targetStart" is out of range. ' + + 'It must be >= 0. Received -1' + } +); + +// Copy throws at negative sourceStart +assert.throws( + () => Buffer.allocUnsafe(5).copy(Buffer.allocUnsafe(5), 0, -1), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "sourceStart" is out of range. ' + + 'It must be >= 0. Received -1' + } +); + +{ + // Check sourceEnd resets to targetEnd if former is greater than the latter + b.fill(++cntr); + c.fill(++cntr); + b.copy(c, 0, 0, 1025); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +// Throw with negative sourceEnd +assert.throws( + () => b.copy(c, 0, 0, -1), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "sourceEnd" is out of range. ' + + 'It must be >= 0. Received -1' + } +); + +// When sourceStart is greater than sourceEnd, zero copied +assert.strictEqual(b.copy(c, 0, 100, 10), 0); + +// When targetStart > targetLength, zero copied +assert.strictEqual(b.copy(c, 512, 0, 10), 0); + +// Test that the `target` can be a Uint8Array. +{ + const d = new Uint8Array(c); + // copy 512 bytes, from 0 to 512. + b.fill(++cntr); + d.fill(++cntr); + const copied = b.copy(d, 0, 0, 512); + assert.strictEqual(copied, 512); + for (let i = 0; i < d.length; i++) { + assert.strictEqual(d[i], b[i]); + } +} + +// Test that the source can be a Uint8Array, too. +{ + const e = new Uint8Array(b); + // copy 512 bytes, from 0 to 512. + e.fill(++cntr); + c.fill(++cntr); + const copied = Buffer.prototype.copy.call(e, c, 0, 0, 512); + assert.strictEqual(copied, 512); + for (let i = 0; i < c.length; i++) { + assert.strictEqual(c[i], e[i]); + } +} + +// https://github.com/nodejs/node/issues/23668: Do not crash for invalid input. +c.fill('c'); +b.copy(c, 'not a valid offset'); +// Make sure this acted like a regular copy with `0` offset. +assert.deepStrictEqual(c, b.slice(0, c.length)); + +{ + c.fill('C'); + assert.throws(() => { + b.copy(c, { [Symbol.toPrimitive]() { throw new Error('foo'); } }); + }, /foo/); + // No copying took place: + assert.deepStrictEqual(c.toString(), 'C'.repeat(c.length)); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-equals.js b/cli/tests/node_compat/test/parallel/test-buffer-equals.js new file mode 100644 index 00000000000000..4c82006a71e4f3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-equals.js @@ -0,0 +1,32 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const b = Buffer.from('abcdf'); +const c = Buffer.from('abcdf'); +const d = Buffer.from('abcde'); +const e = Buffer.from('abcdef'); + +assert.ok(b.equals(c)); +assert.ok(!c.equals(d)); +assert.ok(!d.equals(e)); +assert.ok(d.equals(d)); +assert.ok(d.equals(new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]))); + +assert.throws( + () => Buffer.alloc(1).equals('abc'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "otherBuffer" argument must be an instance of ' + + "Buffer or Uint8Array. Received type string ('abc')" + } +); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-failed-alloc-typed-arrays.js b/cli/tests/node_compat/test/parallel/test-buffer-failed-alloc-typed-arrays.js new file mode 100644 index 00000000000000..265652c09b84cf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-failed-alloc-typed-arrays.js @@ -0,0 +1,40 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const SlowBuffer = require('buffer').SlowBuffer; + +// Test failed or zero-sized Buffer allocations not affecting typed arrays. +// This test exists because of a regression that occurred. Because Buffer +// instances are allocated with the same underlying allocator as TypedArrays, +// but Buffer's can optional be non-zero filled, there was a regression that +// occurred when a Buffer allocated failed, the internal flag specifying +// whether or not to zero-fill was not being reset, causing TypedArrays to +// allocate incorrectly. +const zeroArray = new Uint32Array(10).fill(0); +const sizes = [1e10, 0, 0.1, -1, 'a', undefined, null, NaN]; +const allocators = [ + Buffer, + SlowBuffer, + Buffer.alloc, + Buffer.allocUnsafe, + Buffer.allocUnsafeSlow, +]; +for (const allocator of allocators) { + for (const size of sizes) { + try { + // Some of these allocations are known to fail. If they do, + // Uint32Array should still produce a zeroed out result. + allocator(size); + } catch { + assert.deepStrictEqual(zeroArray, new Uint32Array(10)); + } + } +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-fakes.js b/cli/tests/node_compat/test/parallel/test-buffer-fakes.js new file mode 100644 index 00000000000000..5f088b2acb960a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-fakes.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +function FakeBuffer() { } +Object.setPrototypeOf(FakeBuffer, Buffer); +Object.setPrototypeOf(FakeBuffer.prototype, Buffer.prototype); + +const fb = new FakeBuffer(); + +assert.throws(function() { + Buffer.from(fb); +}, TypeError); + +assert.throws(function() { + +Buffer.prototype; // eslint-disable-line no-unused-expressions +}, TypeError); + +assert.throws(function() { + Buffer.compare(fb, Buffer.alloc(0)); +}, TypeError); + +assert.throws(function() { + fb.write('foo'); +}, TypeError); + +assert.throws(function() { + Buffer.concat([fb, fb]); +}, TypeError); + +assert.throws(function() { + fb.toString(); +}, TypeError); + +assert.throws(function() { + fb.equals(Buffer.alloc(0)); +}, TypeError); + +assert.throws(function() { + fb.indexOf(5); +}, TypeError); + +assert.throws(function() { + fb.readFloatLE(0); +}, TypeError); + +assert.throws(function() { + fb.writeFloatLE(0); +}, TypeError); + +assert.throws(function() { + fb.fill(0); +}, TypeError); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-from.js b/cli/tests/node_compat/test/parallel/test-buffer-from.js new file mode 100644 index 00000000000000..ef023cf0b7a064 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-from.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { deepStrictEqual, throws } = require('assert'); +const { runInNewContext } = require('vm'); + +const checkString = 'test'; + +const check = Buffer.from(checkString); + +class MyString extends String { + constructor() { + super(checkString); + } +} + +class MyPrimitive { + [Symbol.toPrimitive]() { + return checkString; + } +} + +class MyBadPrimitive { + [Symbol.toPrimitive]() { + return 1; + } +} + +deepStrictEqual(Buffer.from(new String(checkString)), check); +deepStrictEqual(Buffer.from(new MyString()), check); +deepStrictEqual(Buffer.from(new MyPrimitive()), check); + +// TODO(Soremwar) +// Enable once again when vm works correctly +// deepStrictEqual( +// Buffer.from(runInNewContext('new String(checkString)', { checkString })), +// check +// ); + +[ + {}, + new Boolean(true), + { valueOf() { return null; } }, + { valueOf() { return undefined; } }, + { valueOf: null }, + Object.create(null), + new Number(true), + new MyBadPrimitive(), + Symbol(), + 5n, + (one, two, three) => {}, + undefined, + null, +].forEach((input) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The first argument must be of type string or an instance of ' + + 'Buffer, ArrayBuffer, or Array or an Array-like Object.' + + common.invalidArgTypeHelper(input) + }; + throws(() => Buffer.from(input), errObj); + throws(() => Buffer.from(input, 'hex'), errObj); +}); + +Buffer.allocUnsafe(10); // Should not throw. +Buffer.from('deadbeaf', 'hex'); // Should not throw. diff --git a/cli/tests/node_compat/test/parallel/test-buffer-includes.js b/cli/tests/node_compat/test/parallel/test-buffer-includes.js new file mode 100644 index 00000000000000..66da7bfd30fdab --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-includes.js @@ -0,0 +1,317 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const b = Buffer.from('abcdef'); +const buf_a = Buffer.from('a'); +const buf_bc = Buffer.from('bc'); +const buf_f = Buffer.from('f'); +const buf_z = Buffer.from('z'); +const buf_empty = Buffer.from(''); + +assert(b.includes('a')); +assert(!b.includes('a', 1)); +assert(!b.includes('a', -1)); +assert(!b.includes('a', -4)); +assert(b.includes('a', -b.length)); +assert(b.includes('a', NaN)); +assert(b.includes('a', -Infinity)); +assert(!b.includes('a', Infinity)); +assert(b.includes('bc')); +assert(!b.includes('bc', 2)); +assert(!b.includes('bc', -1)); +assert(!b.includes('bc', -3)); +assert(b.includes('bc', -5)); +assert(b.includes('bc', NaN)); +assert(b.includes('bc', -Infinity)); +assert(!b.includes('bc', Infinity)); +assert(b.includes('f'), b.length - 1); +assert(!b.includes('z')); +assert(b.includes('')); +assert(b.includes('', 1)); +assert(b.includes('', b.length + 1)); +assert(b.includes('', Infinity)); +assert(b.includes(buf_a)); +assert(!b.includes(buf_a, 1)); +assert(!b.includes(buf_a, -1)); +assert(!b.includes(buf_a, -4)); +assert(b.includes(buf_a, -b.length)); +assert(b.includes(buf_a, NaN)); +assert(b.includes(buf_a, -Infinity)); +assert(!b.includes(buf_a, Infinity)); +assert(b.includes(buf_bc)); +assert(!b.includes(buf_bc, 2)); +assert(!b.includes(buf_bc, -1)); +assert(!b.includes(buf_bc, -3)); +assert(b.includes(buf_bc, -5)); +assert(b.includes(buf_bc, NaN)); +assert(b.includes(buf_bc, -Infinity)); +assert(!b.includes(buf_bc, Infinity)); +assert(b.includes(buf_f), b.length - 1); +assert(!b.includes(buf_z)); +assert(b.includes(buf_empty)); +assert(b.includes(buf_empty, 1)); +assert(b.includes(buf_empty, b.length + 1)); +assert(b.includes(buf_empty, Infinity)); +assert(b.includes(0x61)); +assert(!b.includes(0x61, 1)); +assert(!b.includes(0x61, -1)); +assert(!b.includes(0x61, -4)); +assert(b.includes(0x61, -b.length)); +assert(b.includes(0x61, NaN)); +assert(b.includes(0x61, -Infinity)); +assert(!b.includes(0x61, Infinity)); +assert(!b.includes(0x0)); + +// test offsets +assert(b.includes('d', 2)); +assert(b.includes('f', 5)); +assert(b.includes('f', -1)); +assert(!b.includes('f', 6)); + +assert(b.includes(Buffer.from('d'), 2)); +assert(b.includes(Buffer.from('f'), 5)); +assert(b.includes(Buffer.from('f'), -1)); +assert(!b.includes(Buffer.from('f'), 6)); + +// TODO(Soremwar) +// Enable again once encoding is taking into account when evaluating indexOf +// assert(!Buffer.from('ff').includes(Buffer.from('f'), 1, 'ucs2')); + +// test hex encoding +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .includes('64', 0, 'hex'), + true +); +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .includes(Buffer.from('64', 'hex'), 0, 'hex'), + true +); + +// Test base64 encoding +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .includes('ZA==', 0, 'base64'), + true +); +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .includes(Buffer.from('ZA==', 'base64'), 0, 'base64'), + true +); + +// test ascii encoding +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .includes('d', 0, 'ascii'), + true +); +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .includes(Buffer.from('d', 'ascii'), 0, 'ascii'), + true +); + +// Test latin1 encoding +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .includes('d', 0, 'latin1'), + true +); +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .includes(Buffer.from('d', 'latin1'), 0, 'latin1'), + true +); + +// Test binary encoding +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .includes('d', 0, 'binary'), + true +); +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .includes(Buffer.from('d', 'binary'), 0, 'binary'), + true +); + + +// test ucs2 encoding +let twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); + +assert(twoByteString.includes('\u0395', 4, 'ucs2')); +assert(twoByteString.includes('\u03a3', -4, 'ucs2')); +assert(twoByteString.includes('\u03a3', -6, 'ucs2')); +assert(twoByteString.includes( + Buffer.from('\u03a3', 'ucs2'), -6, 'ucs2')); +assert(!twoByteString.includes('\u03a3', -2, 'ucs2')); + +const mixedByteStringUcs2 = + Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2'); +assert(mixedByteStringUcs2.includes('bc', 0, 'ucs2')); +assert(mixedByteStringUcs2.includes('\u03a3', 0, 'ucs2')); +assert(!mixedByteStringUcs2.includes('\u0396', 0, 'ucs2')); + +assert.ok( + mixedByteStringUcs2.includes(Buffer.from('bc', 'ucs2'), 0, 'ucs2')); +assert.ok( + mixedByteStringUcs2.includes(Buffer.from('\u03a3', 'ucs2'), 0, 'ucs2')); +assert.ok( + !mixedByteStringUcs2.includes(Buffer.from('\u0396', 'ucs2'), 0, 'ucs2')); + +twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); + +// Test single char pattern +assert(twoByteString.includes('\u039a', 0, 'ucs2')); +assert(twoByteString.includes('\u0391', 0, 'ucs2'), 'Alpha'); +assert(twoByteString.includes('\u03a3', 0, 'ucs2'), 'First Sigma'); +assert(twoByteString.includes('\u03a3', 6, 'ucs2'), 'Second Sigma'); +assert(twoByteString.includes('\u0395', 0, 'ucs2'), 'Epsilon'); +assert(!twoByteString.includes('\u0392', 0, 'ucs2'), 'Not beta'); + +// Test multi-char pattern +assert(twoByteString.includes('\u039a\u0391', 0, 'ucs2'), 'Lambda Alpha'); +assert(twoByteString.includes('\u0391\u03a3', 0, 'ucs2'), 'Alpha Sigma'); +assert(twoByteString.includes('\u03a3\u03a3', 0, 'ucs2'), 'Sigma Sigma'); +assert(twoByteString.includes('\u03a3\u0395', 0, 'ucs2'), 'Sigma Epsilon'); + +const mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395'); +assert(mixedByteStringUtf8.includes('bc')); +assert(mixedByteStringUtf8.includes('bc', 5)); +assert(mixedByteStringUtf8.includes('bc', -8)); +assert(mixedByteStringUtf8.includes('\u03a3')); +assert(!mixedByteStringUtf8.includes('\u0396')); + + +// Test complex string includes algorithms. Only trigger for long strings. +// Long string that isn't a simple repeat of a shorter string. +let longString = 'A'; +for (let i = 66; i < 76; i++) { // from 'B' to 'K' + longString = longString + String.fromCharCode(i) + longString; +} + +const longBufferString = Buffer.from(longString); + +// Pattern of 15 chars, repeated every 16 chars in long +let pattern = 'ABACABADABACABA'; +for (let i = 0; i < longBufferString.length - pattern.length; i += 7) { + const includes = longBufferString.includes(pattern, i); + assert(includes, `Long ABACABA...-string at index ${i}`); +} +assert(longBufferString.includes('AJABACA'), 'Long AJABACA, First J'); +assert(longBufferString.includes('AJABACA', 511), 'Long AJABACA, Second J'); + +pattern = 'JABACABADABACABA'; +assert(longBufferString.includes(pattern), 'Long JABACABA..., First J'); +assert(longBufferString.includes(pattern, 512), 'Long JABACABA..., Second J'); + +// Search for a non-ASCII string in a pure ASCII string. +const asciiString = Buffer.from( + 'arglebargleglopglyfarglebargleglopglyfarglebargleglopglyf'); +assert(!asciiString.includes('\x2061')); +assert(asciiString.includes('leb', 0)); + +// Search in string containing many non-ASCII chars. +const allCodePoints = []; +for (let i = 0; i < 65534; i++) allCodePoints[i] = i; +const allCharsString = String.fromCharCode.apply(String, allCodePoints) + + String.fromCharCode(65534, 65535); +const allCharsBufferUtf8 = Buffer.from(allCharsString); +const allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2'); + +// Search for string long enough to trigger complex search with ASCII pattern +// and UC16 subject. +assert(!allCharsBufferUtf8.includes('notfound')); +assert(!allCharsBufferUcs2.includes('notfound')); + +// Find substrings in Utf8. +let lengths = [1, 3, 15]; // Single char, simple and complex. +let indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b]; +for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { + for (let i = 0; i < indices.length; i++) { + const index = indices[i]; + let length = lengths[lengthIndex]; + + if (index + length > 0x7F) { + length = 2 * length; + } + + if (index + length > 0x7FF) { + length = 3 * length; + } + + if (index + length > 0xFFFF) { + length = 4 * length; + } + + const patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length); + assert(index, allCharsBufferUtf8.includes(patternBufferUtf8)); + + const patternStringUtf8 = patternBufferUtf8.toString(); + assert(index, allCharsBufferUtf8.includes(patternStringUtf8)); + } +} + +// Find substrings in Usc2. +lengths = [2, 4, 16]; // Single char, simple and complex. +indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0]; +for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { + for (let i = 0; i < indices.length; i++) { + const index = indices[i] * 2; + const length = lengths[lengthIndex]; + + const patternBufferUcs2 = + allCharsBufferUcs2.slice(index, index + length); + assert.ok( + allCharsBufferUcs2.includes(patternBufferUcs2, 0, 'ucs2')); + + const patternStringUcs2 = patternBufferUcs2.toString('ucs2'); + assert.ok( + allCharsBufferUcs2.includes(patternStringUcs2, 0, 'ucs2')); + } +} + +[ + () => { }, + {}, + [], +].forEach((val) => { + assert.throws( + () => b.includes(val), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "value" argument must be one of type number or string ' + + 'or an instance of Buffer or Uint8Array.' + + common.invalidArgTypeHelper(val) + } + ); +}); + +// Test truncation of Number arguments to uint8 +// TODO(Soremwar) +// Enable once multi byte number search is available +// { +// const buf = Buffer.from('this is a test'); +// assert.ok(buf.includes(0x6973)); +// assert.ok(buf.includes(0x697320)); +// assert.ok(buf.includes(0x69732069)); +// assert.ok(buf.includes(0x697374657374)); +// assert.ok(buf.includes(0x69737374)); +// assert.ok(buf.includes(0x69737465)); +// assert.ok(buf.includes(0x69737465)); +// assert.ok(buf.includes(-140)); +// assert.ok(buf.includes(-152)); +// assert.ok(!buf.includes(0xff)); +// assert.ok(!buf.includes(0xffff)); +// } diff --git a/cli/tests/node_compat/test/parallel/test-buffer-indexof.js b/cli/tests/node_compat/test/parallel/test-buffer-indexof.js new file mode 100644 index 00000000000000..e98e343492e130 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-indexof.js @@ -0,0 +1,646 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const b = Buffer.from('abcdef'); +const buf_a = Buffer.from('a'); +const buf_bc = Buffer.from('bc'); +const buf_f = Buffer.from('f'); +const buf_z = Buffer.from('z'); +const buf_empty = Buffer.from(''); + +const s = 'abcdef'; + +assert.strictEqual(b.indexOf('a'), 0); +assert.strictEqual(b.indexOf('a', 1), -1); +assert.strictEqual(b.indexOf('a', -1), -1); +assert.strictEqual(b.indexOf('a', -4), -1); +assert.strictEqual(b.indexOf('a', -b.length), 0); +assert.strictEqual(b.indexOf('a', NaN), 0); +assert.strictEqual(b.indexOf('a', -Infinity), 0); +assert.strictEqual(b.indexOf('a', Infinity), -1); +assert.strictEqual(b.indexOf('bc'), 1); +assert.strictEqual(b.indexOf('bc', 2), -1); +assert.strictEqual(b.indexOf('bc', -1), -1); +assert.strictEqual(b.indexOf('bc', -3), -1); +assert.strictEqual(b.indexOf('bc', -5), 1); +assert.strictEqual(b.indexOf('bc', NaN), 1); +assert.strictEqual(b.indexOf('bc', -Infinity), 1); +assert.strictEqual(b.indexOf('bc', Infinity), -1); +assert.strictEqual(b.indexOf('f'), b.length - 1); +assert.strictEqual(b.indexOf('z'), -1); +assert.strictEqual(b.indexOf(''), 0); +assert.strictEqual(b.indexOf('', 1), 1); +assert.strictEqual(b.indexOf('', b.length + 1), b.length); +assert.strictEqual(b.indexOf('', Infinity), b.length); +assert.strictEqual(b.indexOf(buf_a), 0); +assert.strictEqual(b.indexOf(buf_a, 1), -1); +assert.strictEqual(b.indexOf(buf_a, -1), -1); +assert.strictEqual(b.indexOf(buf_a, -4), -1); +assert.strictEqual(b.indexOf(buf_a, -b.length), 0); +assert.strictEqual(b.indexOf(buf_a, NaN), 0); +assert.strictEqual(b.indexOf(buf_a, -Infinity), 0); +assert.strictEqual(b.indexOf(buf_a, Infinity), -1); +assert.strictEqual(b.indexOf(buf_bc), 1); +assert.strictEqual(b.indexOf(buf_bc, 2), -1); +assert.strictEqual(b.indexOf(buf_bc, -1), -1); +assert.strictEqual(b.indexOf(buf_bc, -3), -1); +assert.strictEqual(b.indexOf(buf_bc, -5), 1); +assert.strictEqual(b.indexOf(buf_bc, NaN), 1); +assert.strictEqual(b.indexOf(buf_bc, -Infinity), 1); +assert.strictEqual(b.indexOf(buf_bc, Infinity), -1); +assert.strictEqual(b.indexOf(buf_f), b.length - 1); +assert.strictEqual(b.indexOf(buf_z), -1); +assert.strictEqual(b.indexOf(buf_empty), 0); +assert.strictEqual(b.indexOf(buf_empty, 1), 1); +assert.strictEqual(b.indexOf(buf_empty, b.length + 1), b.length); +assert.strictEqual(b.indexOf(buf_empty, Infinity), b.length); +assert.strictEqual(b.indexOf(0x61), 0); +assert.strictEqual(b.indexOf(0x61, 1), -1); +assert.strictEqual(b.indexOf(0x61, -1), -1); +assert.strictEqual(b.indexOf(0x61, -4), -1); +assert.strictEqual(b.indexOf(0x61, -b.length), 0); +assert.strictEqual(b.indexOf(0x61, NaN), 0); +assert.strictEqual(b.indexOf(0x61, -Infinity), 0); +assert.strictEqual(b.indexOf(0x61, Infinity), -1); +assert.strictEqual(b.indexOf(0x0), -1); + +// test offsets +assert.strictEqual(b.indexOf('d', 2), 3); +assert.strictEqual(b.indexOf('f', 5), 5); +assert.strictEqual(b.indexOf('f', -1), 5); +assert.strictEqual(b.indexOf('f', 6), -1); + +assert.strictEqual(b.indexOf(Buffer.from('d'), 2), 3); +assert.strictEqual(b.indexOf(Buffer.from('f'), 5), 5); +assert.strictEqual(b.indexOf(Buffer.from('f'), -1), 5); +assert.strictEqual(b.indexOf(Buffer.from('f'), 6), -1); + +// TODO(Soremwar) +// Enable again once encoding is taking into account when evaluating indexOf +// assert.strictEqual(Buffer.from('ff').indexOf(Buffer.from('f'), 1, 'ucs2'), -1); + +// Test invalid and uppercase encoding +assert.strictEqual(b.indexOf('b', 'utf8'), 1); +assert.strictEqual(b.indexOf('b', 'UTF8'), 1); +assert.strictEqual(b.indexOf('62', 'HEX'), 1); +assert.throws(() => b.indexOf('bad', 'enc'), /Unknown encoding: enc/); + +// test hex encoding +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .indexOf('64', 0, 'hex'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .indexOf(Buffer.from('64', 'hex'), 0, 'hex'), + 3 +); + +// Test base64 encoding +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .indexOf('ZA==', 0, 'base64'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .indexOf(Buffer.from('ZA==', 'base64'), 0, 'base64'), + 3 +); + +// Test base64url encoding +assert.strictEqual( + Buffer.from(b.toString('base64url'), 'base64url') + .indexOf('ZA==', 0, 'base64url'), + 3 +); + +// test ascii encoding +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .indexOf('d', 0, 'ascii'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .indexOf(Buffer.from('d', 'ascii'), 0, 'ascii'), + 3 +); + +// Test latin1 encoding +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .indexOf('d', 0, 'latin1'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .indexOf(Buffer.from('d', 'latin1'), 0, 'latin1'), + 3 +); +assert.strictEqual( + Buffer.from('aa\u00e8aa', 'latin1') + .indexOf('\u00e8', 'latin1'), + 2 +); +assert.strictEqual( + Buffer.from('\u00e8', 'latin1') + .indexOf('\u00e8', 'latin1'), + 0 +); +assert.strictEqual( + Buffer.from('\u00e8', 'latin1') + .indexOf(Buffer.from('\u00e8', 'latin1'), 'latin1'), + 0 +); + +// Test binary encoding +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .indexOf('d', 0, 'binary'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .indexOf(Buffer.from('d', 'binary'), 0, 'binary'), + 3 +); +assert.strictEqual( + Buffer.from('aa\u00e8aa', 'binary') + .indexOf('\u00e8', 'binary'), + 2 +); +assert.strictEqual( + Buffer.from('\u00e8', 'binary') + .indexOf('\u00e8', 'binary'), + 0 +); +assert.strictEqual( + Buffer.from('\u00e8', 'binary') + .indexOf(Buffer.from('\u00e8', 'binary'), 'binary'), + 0 +); + + +// Test optional offset with passed encoding +assert.strictEqual(Buffer.from('aaaa0').indexOf('30', 'hex'), 4); +assert.strictEqual(Buffer.from('aaaa00a').indexOf('3030', 'hex'), 4); + +{ + // Test usc2 and utf16le encoding + ['ucs2', 'utf16le'].forEach((encoding) => { + const twoByteString = Buffer.from( + '\u039a\u0391\u03a3\u03a3\u0395', encoding); + + assert.strictEqual(twoByteString.indexOf('\u0395', 4, encoding), 8); + assert.strictEqual(twoByteString.indexOf('\u03a3', -4, encoding), 6); + assert.strictEqual(twoByteString.indexOf('\u03a3', -6, encoding), 4); + assert.strictEqual(twoByteString.indexOf( + Buffer.from('\u03a3', encoding), -6, encoding), 4); + assert.strictEqual(-1, twoByteString.indexOf('\u03a3', -2, encoding)); + }); +} + +const mixedByteStringUcs2 = + Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2'); +assert.strictEqual(mixedByteStringUcs2.indexOf('bc', 0, 'ucs2'), 6); +assert.strictEqual(mixedByteStringUcs2.indexOf('\u03a3', 0, 'ucs2'), 10); +assert.strictEqual(-1, mixedByteStringUcs2.indexOf('\u0396', 0, 'ucs2')); + +assert.strictEqual( + mixedByteStringUcs2.indexOf(Buffer.from('bc', 'ucs2'), 0, 'ucs2'), 6); +assert.strictEqual( + mixedByteStringUcs2.indexOf(Buffer.from('\u03a3', 'ucs2'), 0, 'ucs2'), 10); +assert.strictEqual( + -1, mixedByteStringUcs2.indexOf(Buffer.from('\u0396', 'ucs2'), 0, 'ucs2')); + +{ + const twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); + + // Test single char pattern + assert.strictEqual(twoByteString.indexOf('\u039a', 0, 'ucs2'), 0); + let index = twoByteString.indexOf('\u0391', 0, 'ucs2'); + assert.strictEqual(index, 2, `Alpha - at index ${index}`); + index = twoByteString.indexOf('\u03a3', 0, 'ucs2'); + assert.strictEqual(index, 4, `First Sigma - at index ${index}`); + index = twoByteString.indexOf('\u03a3', 6, 'ucs2'); + assert.strictEqual(index, 6, `Second Sigma - at index ${index}`); + index = twoByteString.indexOf('\u0395', 0, 'ucs2'); + assert.strictEqual(index, 8, `Epsilon - at index ${index}`); + index = twoByteString.indexOf('\u0392', 0, 'ucs2'); + assert.strictEqual(-1, index, `Not beta - at index ${index}`); + + // Test multi-char pattern + index = twoByteString.indexOf('\u039a\u0391', 0, 'ucs2'); + assert.strictEqual(index, 0, `Lambda Alpha - at index ${index}`); + index = twoByteString.indexOf('\u0391\u03a3', 0, 'ucs2'); + assert.strictEqual(index, 2, `Alpha Sigma - at index ${index}`); + index = twoByteString.indexOf('\u03a3\u03a3', 0, 'ucs2'); + assert.strictEqual(index, 4, `Sigma Sigma - at index ${index}`); + index = twoByteString.indexOf('\u03a3\u0395', 0, 'ucs2'); + assert.strictEqual(index, 6, `Sigma Epsilon - at index ${index}`); +} + +const mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395'); +assert.strictEqual(mixedByteStringUtf8.indexOf('bc'), 5); +assert.strictEqual(mixedByteStringUtf8.indexOf('bc', 5), 5); +assert.strictEqual(mixedByteStringUtf8.indexOf('bc', -8), 5); +assert.strictEqual(mixedByteStringUtf8.indexOf('\u03a3'), 7); +assert.strictEqual(mixedByteStringUtf8.indexOf('\u0396'), -1); + + +// Test complex string indexOf algorithms. Only trigger for long strings. +// Long string that isn't a simple repeat of a shorter string. +let longString = 'A'; +for (let i = 66; i < 76; i++) { // from 'B' to 'K' + longString = longString + String.fromCharCode(i) + longString; +} + +const longBufferString = Buffer.from(longString); + +// Pattern of 15 chars, repeated every 16 chars in long +let pattern = 'ABACABADABACABA'; +for (let i = 0; i < longBufferString.length - pattern.length; i += 7) { + const index = longBufferString.indexOf(pattern, i); + assert.strictEqual((i + 15) & ~0xf, index, + `Long ABACABA...-string at index ${i}`); +} + +let index = longBufferString.indexOf('AJABACA'); +assert.strictEqual(index, 510, `Long AJABACA, First J - at index ${index}`); +index = longBufferString.indexOf('AJABACA', 511); +assert.strictEqual(index, 1534, `Long AJABACA, Second J - at index ${index}`); + +pattern = 'JABACABADABACABA'; +index = longBufferString.indexOf(pattern); +assert.strictEqual(index, 511, `Long JABACABA..., First J - at index ${index}`); +index = longBufferString.indexOf(pattern, 512); +assert.strictEqual( + index, 1535, `Long JABACABA..., Second J - at index ${index}`); + +// Search for a non-ASCII string in a pure ASCII string. +const asciiString = Buffer.from( + 'arglebargleglopglyfarglebargleglopglyfarglebargleglopglyf'); +assert.strictEqual(-1, asciiString.indexOf('\x2061')); +assert.strictEqual(asciiString.indexOf('leb', 0), 3); + +// Search in string containing many non-ASCII chars. +const allCodePoints = []; +for (let i = 0; i < 65534; i++) allCodePoints[i] = i; +const allCharsString = String.fromCharCode.apply(String, allCodePoints) + + String.fromCharCode(65534, 65535); +const allCharsBufferUtf8 = Buffer.from(allCharsString); +const allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2'); + +// Search for string long enough to trigger complex search with ASCII pattern +// and UC16 subject. +assert.strictEqual(-1, allCharsBufferUtf8.indexOf('notfound')); +assert.strictEqual(-1, allCharsBufferUcs2.indexOf('notfound')); + +// Needle is longer than haystack, but only because it's encoded as UTF-16 +assert.strictEqual(Buffer.from('aaaa').indexOf('a'.repeat(4), 'ucs2'), -1); + +assert.strictEqual(Buffer.from('aaaa').indexOf('a'.repeat(4), 'utf8'), 0); +assert.strictEqual(Buffer.from('aaaa').indexOf('你好', 'ucs2'), -1); + +// Haystack has odd length, but the needle is UCS2. +assert.strictEqual(Buffer.from('aaaaa').indexOf('b', 'ucs2'), -1); + +{ + // Find substrings in Utf8. + const lengths = [1, 3, 15]; // Single char, simple and complex. + const indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b]; + for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { + for (let i = 0; i < indices.length; i++) { + const index = indices[i]; + let length = lengths[lengthIndex]; + + if (index + length > 0x7F) { + length = 2 * length; + } + + if (index + length > 0x7FF) { + length = 3 * length; + } + + if (index + length > 0xFFFF) { + length = 4 * length; + } + + const patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length); + assert.strictEqual(index, allCharsBufferUtf8.indexOf(patternBufferUtf8)); + + const patternStringUtf8 = patternBufferUtf8.toString(); + assert.strictEqual(index, allCharsBufferUtf8.indexOf(patternStringUtf8)); + } + } +} + +// TODO(Soremwar) +// Enable again once encoding is taking into account when evaluating indexOf +// { +// // Find substrings in Usc2. +// const lengths = [2, 4, 16]; // Single char, simple and complex. +// const indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0]; +// for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { +// for (let i = 0; i < indices.length; i++) { +// const index = indices[i] * 2; +// const length = lengths[lengthIndex]; + +// const patternBufferUcs2 = +// allCharsBufferUcs2.slice(index, index + length); +// assert.strictEqual( +// index, allCharsBufferUcs2.indexOf(patternBufferUcs2, 0, 'ucs2')); + +// const patternStringUcs2 = patternBufferUcs2.toString('ucs2'); +// assert.strictEqual( +// index, allCharsBufferUcs2.indexOf(patternStringUcs2, 0, 'ucs2')); +// } +// } +// } + +[ + () => {}, + {}, + [], +].forEach((val) => { + assert.throws( + () => b.indexOf(val), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "value" argument must be one of type number or string ' + + 'or an instance of Buffer or Uint8Array.' + + common.invalidArgTypeHelper(val) + } + ); +}); + +// Test weird offset arguments. +// The following offsets coerce to NaN or 0, searching the whole Buffer +assert.strictEqual(b.indexOf('b', undefined), 1); +assert.strictEqual(b.indexOf('b', {}), 1); +assert.strictEqual(b.indexOf('b', 0), 1); +assert.strictEqual(b.indexOf('b', null), 1); +assert.strictEqual(b.indexOf('b', []), 1); + +// The following offset coerces to 2, in other words +[2] === 2 +assert.strictEqual(b.indexOf('b', [2]), -1); + +// Behavior should match String.indexOf() +assert.strictEqual( + b.indexOf('b', undefined), + s.indexOf('b', undefined)); +assert.strictEqual( + b.indexOf('b', {}), + s.indexOf('b', {})); +assert.strictEqual( + b.indexOf('b', 0), + s.indexOf('b', 0)); +assert.strictEqual( + b.indexOf('b', null), + s.indexOf('b', null)); +assert.strictEqual( + b.indexOf('b', []), + s.indexOf('b', [])); +assert.strictEqual( + b.indexOf('b', [2]), + s.indexOf('b', [2])); + +// All code for handling encodings is shared between Buffer.indexOf and +// Buffer.lastIndexOf, so only testing the separate lastIndexOf semantics. + +// Test lastIndexOf basic functionality; Buffer b contains 'abcdef'. +// lastIndexOf string: +assert.strictEqual(b.lastIndexOf('a'), 0); +assert.strictEqual(b.lastIndexOf('a', 1), 0); +assert.strictEqual(b.lastIndexOf('b', 1), 1); +assert.strictEqual(b.lastIndexOf('c', 1), -1); +assert.strictEqual(b.lastIndexOf('a', -1), 0); +assert.strictEqual(b.lastIndexOf('a', -4), 0); +assert.strictEqual(b.lastIndexOf('a', -b.length), 0); +assert.strictEqual(b.lastIndexOf('a', -b.length - 1), -1); +assert.strictEqual(b.lastIndexOf('a', NaN), 0); +assert.strictEqual(b.lastIndexOf('a', -Infinity), -1); +assert.strictEqual(b.lastIndexOf('a', Infinity), 0); +// lastIndexOf Buffer: +assert.strictEqual(b.lastIndexOf(buf_a), 0); +assert.strictEqual(b.lastIndexOf(buf_a, 1), 0); +assert.strictEqual(b.lastIndexOf(buf_a, -1), 0); +assert.strictEqual(b.lastIndexOf(buf_a, -4), 0); +assert.strictEqual(b.lastIndexOf(buf_a, -b.length), 0); +assert.strictEqual(b.lastIndexOf(buf_a, -b.length - 1), -1); +assert.strictEqual(b.lastIndexOf(buf_a, NaN), 0); +assert.strictEqual(b.lastIndexOf(buf_a, -Infinity), -1); +assert.strictEqual(b.lastIndexOf(buf_a, Infinity), 0); +assert.strictEqual(b.lastIndexOf(buf_bc), 1); +assert.strictEqual(b.lastIndexOf(buf_bc, 2), 1); +assert.strictEqual(b.lastIndexOf(buf_bc, -1), 1); +assert.strictEqual(b.lastIndexOf(buf_bc, -3), 1); +assert.strictEqual(b.lastIndexOf(buf_bc, -5), 1); +assert.strictEqual(b.lastIndexOf(buf_bc, -6), -1); +assert.strictEqual(b.lastIndexOf(buf_bc, NaN), 1); +assert.strictEqual(b.lastIndexOf(buf_bc, -Infinity), -1); +assert.strictEqual(b.lastIndexOf(buf_bc, Infinity), 1); +assert.strictEqual(b.lastIndexOf(buf_f), b.length - 1); +assert.strictEqual(b.lastIndexOf(buf_z), -1); +assert.strictEqual(b.lastIndexOf(buf_empty), b.length); +assert.strictEqual(b.lastIndexOf(buf_empty, 1), 1); +assert.strictEqual(b.lastIndexOf(buf_empty, b.length + 1), b.length); +assert.strictEqual(b.lastIndexOf(buf_empty, Infinity), b.length); +// lastIndexOf number: +assert.strictEqual(b.lastIndexOf(0x61), 0); +assert.strictEqual(b.lastIndexOf(0x61, 1), 0); +assert.strictEqual(b.lastIndexOf(0x61, -1), 0); +assert.strictEqual(b.lastIndexOf(0x61, -4), 0); +assert.strictEqual(b.lastIndexOf(0x61, -b.length), 0); +assert.strictEqual(b.lastIndexOf(0x61, -b.length - 1), -1); +assert.strictEqual(b.lastIndexOf(0x61, NaN), 0); +assert.strictEqual(b.lastIndexOf(0x61, -Infinity), -1); +assert.strictEqual(b.lastIndexOf(0x61, Infinity), 0); +assert.strictEqual(b.lastIndexOf(0x0), -1); + +// Test weird offset arguments. +// The following offsets coerce to NaN, searching the whole Buffer +assert.strictEqual(b.lastIndexOf('b', undefined), 1); +assert.strictEqual(b.lastIndexOf('b', {}), 1); + +// The following offsets coerce to 0 +assert.strictEqual(b.lastIndexOf('b', 0), -1); +assert.strictEqual(b.lastIndexOf('b', null), -1); +assert.strictEqual(b.lastIndexOf('b', []), -1); + +// The following offset coerces to 2, in other words +[2] === 2 +assert.strictEqual(b.lastIndexOf('b', [2]), 1); + +// Behavior should match String.lastIndexOf() +assert.strictEqual( + b.lastIndexOf('b', undefined), + s.lastIndexOf('b', undefined)); +assert.strictEqual( + b.lastIndexOf('b', {}), + s.lastIndexOf('b', {})); +assert.strictEqual( + b.lastIndexOf('b', 0), + s.lastIndexOf('b', 0)); +assert.strictEqual( + b.lastIndexOf('b', null), + s.lastIndexOf('b', null)); +assert.strictEqual( + b.lastIndexOf('b', []), + s.lastIndexOf('b', [])); +assert.strictEqual( + b.lastIndexOf('b', [2]), + s.lastIndexOf('b', [2])); + +// Test needles longer than the haystack. +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'ucs2'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'utf8'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'latin1'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'binary'), -1); +assert.strictEqual(b.lastIndexOf(Buffer.from('aaaaaaaaaaaaaaa')), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 2, 'ucs2'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 3, 'utf8'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 5, 'latin1'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 5, 'binary'), -1); +assert.strictEqual(b.lastIndexOf(Buffer.from('aaaaaaaaaaaaaaa'), 7), -1); + +// 你好 expands to a total of 6 bytes using UTF-8 and 4 bytes using UTF-16 +assert.strictEqual(buf_bc.lastIndexOf('你好', 'ucs2'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 'utf8'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 'latin1'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 'binary'), -1); +assert.strictEqual(buf_bc.lastIndexOf(Buffer.from('你好')), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 2, 'ucs2'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 3, 'utf8'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 5, 'latin1'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 5, 'binary'), -1); +assert.strictEqual(buf_bc.lastIndexOf(Buffer.from('你好'), 7), -1); + +// Test lastIndexOf on a longer buffer: +const bufferString = Buffer.from('a man a plan a canal panama'); +assert.strictEqual(bufferString.lastIndexOf('canal'), 15); +assert.strictEqual(bufferString.lastIndexOf('panama'), 21); +assert.strictEqual(bufferString.lastIndexOf('a man a plan a canal panama'), 0); +assert.strictEqual(-1, bufferString.lastIndexOf('a man a plan a canal mexico')); +assert.strictEqual(-1, bufferString + .lastIndexOf('a man a plan a canal mexico city')); +assert.strictEqual(-1, bufferString.lastIndexOf(Buffer.from('a'.repeat(1000)))); +assert.strictEqual(bufferString.lastIndexOf('a man a plan', 4), 0); +assert.strictEqual(bufferString.lastIndexOf('a '), 13); +assert.strictEqual(bufferString.lastIndexOf('a ', 13), 13); +assert.strictEqual(bufferString.lastIndexOf('a ', 12), 6); +assert.strictEqual(bufferString.lastIndexOf('a ', 5), 0); +assert.strictEqual(bufferString.lastIndexOf('a ', -1), 13); +assert.strictEqual(bufferString.lastIndexOf('a ', -27), 0); +assert.strictEqual(-1, bufferString.lastIndexOf('a ', -28)); + +// Test lastIndexOf for the case that the first character can be found, +// but in a part of the buffer that does not make search to search +// due do length constraints. +const abInUCS2 = Buffer.from('ab', 'ucs2'); +assert.strictEqual(-1, Buffer.from('µaaaa¶bbbb', 'latin1').lastIndexOf('µ')); +assert.strictEqual(-1, Buffer.from('µaaaa¶bbbb', 'binary').lastIndexOf('µ')); +assert.strictEqual(-1, Buffer.from('bc').lastIndexOf('ab')); +assert.strictEqual(-1, Buffer.from('abc').lastIndexOf('qa')); +assert.strictEqual(-1, Buffer.from('abcdef').lastIndexOf('qabc')); +assert.strictEqual(-1, Buffer.from('bc').lastIndexOf(Buffer.from('ab'))); +assert.strictEqual(-1, Buffer.from('bc', 'ucs2').lastIndexOf('ab', 'ucs2')); +assert.strictEqual(-1, Buffer.from('bc', 'ucs2').lastIndexOf(abInUCS2)); + +assert.strictEqual(Buffer.from('abc').lastIndexOf('ab'), 0); +assert.strictEqual(Buffer.from('abc').lastIndexOf('ab', 1), 0); +assert.strictEqual(Buffer.from('abc').lastIndexOf('ab', 2), 0); +assert.strictEqual(Buffer.from('abc').lastIndexOf('ab', 3), 0); + +// The above tests test the LINEAR and SINGLE-CHAR strategies. +// Now, we test the BOYER-MOORE-HORSPOOL strategy. +// Test lastIndexOf on a long buffer w multiple matches: +pattern = 'JABACABADABACABA'; +assert.strictEqual(longBufferString.lastIndexOf(pattern), 1535); +assert.strictEqual(longBufferString.lastIndexOf(pattern, 1535), 1535); +assert.strictEqual(longBufferString.lastIndexOf(pattern, 1534), 511); + +// Finally, give it a really long input to trigger fallback from BMH to +// regular BOYER-MOORE (which has better worst-case complexity). + +// Generate a really long Thue-Morse sequence of 'yolo' and 'swag', +// "yolo swag swag yolo swag yolo yolo swag" ..., goes on for about 5MB. +// This is hard to search because it all looks similar, but never repeats. + +// countBits returns the number of bits in the binary representation of n. +function countBits(n) { + let count; + for (count = 0; n > 0; count++) { + n = n & (n - 1); // remove top bit + } + return count; +} +const parts = []; +for (let i = 0; i < 1000000; i++) { + parts.push((countBits(i) % 2 === 0) ? 'yolo' : 'swag'); +} +const reallyLong = Buffer.from(parts.join(' ')); +assert.strictEqual(reallyLong.slice(0, 19).toString(), 'yolo swag swag yolo'); + +// Expensive reverse searches. Stress test lastIndexOf: +pattern = reallyLong.slice(0, 100000); // First 1/50th of the pattern. +assert.strictEqual(reallyLong.lastIndexOf(pattern), 4751360); +assert.strictEqual(reallyLong.lastIndexOf(pattern, 4000000), 3932160); +assert.strictEqual(reallyLong.lastIndexOf(pattern, 3000000), 2949120); +pattern = reallyLong.slice(100000, 200000); // Second 1/50th. +assert.strictEqual(reallyLong.lastIndexOf(pattern), 4728480); +pattern = reallyLong.slice(0, 1000000); // First 1/5th. +assert.strictEqual(reallyLong.lastIndexOf(pattern), 3932160); +pattern = reallyLong.slice(0, 2000000); // first 2/5ths. +assert.strictEqual(reallyLong.lastIndexOf(pattern), 0); + +// Test truncation of Number arguments to uint8 +// TODO(Soremwar) +// Enable once multi byte number search is available +// { +// const buf = Buffer.from('this is a test'); +// assert.strictEqual(buf.indexOf(0x6973), 3); +// assert.strictEqual(buf.indexOf(0x697320), 4); +// assert.strictEqual(buf.indexOf(0x69732069), 2); +// assert.strictEqual(buf.indexOf(0x697374657374), 0); +// assert.strictEqual(buf.indexOf(0x69737374), 0); +// assert.strictEqual(buf.indexOf(0x69737465), 11); +// assert.strictEqual(buf.indexOf(0x69737465), 11); +// assert.strictEqual(buf.indexOf(-140), 0); +// assert.strictEqual(buf.indexOf(-152), 1); +// assert.strictEqual(buf.indexOf(0xff), -1); +// assert.strictEqual(buf.indexOf(0xffff), -1); +// } + +// Test that Uint8Array arguments are okay. +{ + const needle = new Uint8Array([ 0x66, 0x6f, 0x6f ]); + const haystack = Buffer.from('a foo b foo'); + assert.strictEqual(haystack.indexOf(needle), 2); + assert.strictEqual(haystack.lastIndexOf(needle), haystack.length - 3); +} + +// Avoid abort because of invalid usage +// see https://github.com/nodejs/node/issues/32753 +{ + assert.throws(() => { + const buffer = require('buffer'); + new buffer.Buffer.prototype.lastIndexOf(1, 'str'); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer" argument must be an instance of Buffer, ' + + 'TypedArray, or DataView. ' + + 'Received an instance of lastIndexOf' + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-inheritance.js b/cli/tests/node_compat/test/parallel/test-buffer-inheritance.js new file mode 100644 index 00000000000000..fb22a19a9b6665 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-inheritance.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + + +function T(n) { + const ui8 = new Uint8Array(n); + Object.setPrototypeOf(ui8, T.prototype); + return ui8; +} +Object.setPrototypeOf(T.prototype, Buffer.prototype); +Object.setPrototypeOf(T, Buffer); + +T.prototype.sum = function sum() { + let cntr = 0; + for (let i = 0; i < this.length; i++) + cntr += this[i]; + return cntr; +}; + + +const vals = [new T(4), T(4)]; + +vals.forEach(function(t) { + assert.strictEqual(t.constructor, T); + assert.strictEqual(Object.getPrototypeOf(t), T.prototype); + assert.strictEqual(Object.getPrototypeOf(Object.getPrototypeOf(t)), + Buffer.prototype); + + t.fill(5); + let cntr = 0; + for (let i = 0; i < t.length; i++) + cntr += t[i]; + assert.strictEqual(cntr, t.length * 5); + + // Check this does not throw + t.toString(); +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-isencoding.js b/cli/tests/node_compat/test/parallel/test-buffer-isencoding.js new file mode 100644 index 00000000000000..f6fdfd8f9a50fe --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-isencoding.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +[ + 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'latin1', + 'binary', + 'base64', + 'base64url', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le', +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), true); +}); + +[ + 'utf9', + 'utf-7', + 'Unicode-FTW', + 'new gnu gun', + false, + NaN, + {}, + Infinity, + [], + 1, + 0, + -1, +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), false); +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-iterator.js b/cli/tests/node_compat/test/parallel/test-buffer-iterator.js new file mode 100644 index 00000000000000..a93bb161a289e3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-iterator.js @@ -0,0 +1,69 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +const buffer = Buffer.from([1, 2, 3, 4, 5]); +let arr; +let b; + +// Buffers should be iterable + +arr = []; + +for (b of buffer) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// Buffer iterators should be iterable + +arr = []; + +for (b of buffer[Symbol.iterator]()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#values() should return iterator for values + +arr = []; + +for (b of buffer.values()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#keys() should return iterator for keys + +arr = []; + +for (b of buffer.keys()) + arr.push(b); + +assert.deepStrictEqual(arr, [0, 1, 2, 3, 4]); + + +// buffer#entries() should return iterator for entries + +arr = []; + +for (b of buffer.entries()) + arr.push(b); + +assert.deepStrictEqual(arr, [ + [0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], +]); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-new.js b/cli/tests/node_compat/test/parallel/test-buffer-new.js new file mode 100644 index 00000000000000..c79e80b50c57bb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-new.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.throws(() => new Buffer(42, 'utf8'), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "string" argument must be of type string. Received type ' + + 'number (42)' +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-no-negative-allocation.js b/cli/tests/node_compat/test/parallel/test-buffer-no-negative-allocation.js new file mode 100644 index 00000000000000..742d298ba8fc58 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-no-negative-allocation.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const { SlowBuffer } = require('buffer'); + +const msg = { + code: 'ERR_INVALID_ARG_VALUE', + name: 'RangeError', + message: /^The argument 'size' is invalid\. Received [^"]*$/ +}; + +// Test that negative Buffer length inputs throw errors. + +assert.throws(() => Buffer(-Buffer.poolSize), msg); +assert.throws(() => Buffer(-100), msg); +assert.throws(() => Buffer(-1), msg); +assert.throws(() => Buffer(NaN), msg); + +assert.throws(() => Buffer.alloc(-Buffer.poolSize), msg); +assert.throws(() => Buffer.alloc(-100), msg); +assert.throws(() => Buffer.alloc(-1), msg); +assert.throws(() => Buffer.alloc(NaN), msg); + +assert.throws(() => Buffer.allocUnsafe(-Buffer.poolSize), msg); +assert.throws(() => Buffer.allocUnsafe(-100), msg); +assert.throws(() => Buffer.allocUnsafe(-1), msg); +assert.throws(() => Buffer.allocUnsafe(NaN), msg); + +assert.throws(() => Buffer.allocUnsafeSlow(-Buffer.poolSize), msg); +assert.throws(() => Buffer.allocUnsafeSlow(-100), msg); +assert.throws(() => Buffer.allocUnsafeSlow(-1), msg); +assert.throws(() => Buffer.allocUnsafeSlow(NaN), msg); + +assert.throws(() => SlowBuffer(-Buffer.poolSize), msg); +assert.throws(() => SlowBuffer(-100), msg); +assert.throws(() => SlowBuffer(-1), msg); +assert.throws(() => SlowBuffer(NaN), msg); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-nopendingdep-map.js b/cli/tests/node_compat/test/parallel/test-buffer-nopendingdep-map.js new file mode 100644 index 00000000000000..5a6abca86a1cd3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-nopendingdep-map.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --no-warnings --pending-deprecation +'use strict'; + +const common = require('../common'); + +process.on('warning', common.mustNotCall('A warning should not be emitted')); + +// With the --pending-deprecation flag, the deprecation warning for +// new Buffer() should not be emitted when Uint8Array methods are called. + +Buffer.from('abc').map((i) => i); +Buffer.from('abc').filter((i) => i); +Buffer.from('abc').slice(1, 2); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-of-no-deprecation.js b/cli/tests/node_compat/test/parallel/test-buffer-of-no-deprecation.js new file mode 100644 index 00000000000000..b98bc25a3584d7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-of-no-deprecation.js @@ -0,0 +1,14 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +process.on('warning', common.mustNotCall()); + +Buffer.of(0, 1); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-over-max-length.js b/cli/tests/node_compat/test/parallel/test-buffer-over-max-length.js new file mode 100644 index 00000000000000..4bcbfeb9be17c1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-over-max-length.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +const assert = require('assert'); + +const buffer = require('buffer'); +const SlowBuffer = buffer.SlowBuffer; + +const kMaxLength = buffer.kMaxLength; +const bufferMaxSizeMsg = { + code: 'ERR_INVALID_ARG_VALUE', + name: 'RangeError', + message: /^The argument 'size' is invalid\. Received [^"]*$/ +}; + +assert.throws(() => Buffer((-1 >>> 0) + 2), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer((-1 >>> 0) + 2), bufferMaxSizeMsg); +assert.throws(() => Buffer.alloc((-1 >>> 0) + 2), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafe((-1 >>> 0) + 2), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafeSlow((-1 >>> 0) + 2), bufferMaxSizeMsg); + +assert.throws(() => Buffer(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.alloc(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafe(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafeSlow(kMaxLength + 1), bufferMaxSizeMsg); + +// issue GH-4331 +assert.throws(() => Buffer.allocUnsafe(0x100000001), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafe(0xFFFFFFFFF), bufferMaxSizeMsg); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-parent-property.js b/cli/tests/node_compat/test/parallel/test-buffer-parent-property.js new file mode 100644 index 00000000000000..c433ec04a969f5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-parent-property.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Fix for https://github.com/nodejs/node/issues/8266 +// +// Zero length Buffer objects should expose the `buffer` property of the +// TypedArrays, via the `parent` property. +require('../common'); +const assert = require('assert'); + +// If the length of the buffer object is zero +assert((new Buffer(0)).parent instanceof ArrayBuffer); + +// If the length of the buffer object is equal to the underlying ArrayBuffer +assert((new Buffer(Buffer.poolSize)).parent instanceof ArrayBuffer); + +// Same as the previous test, but with user created buffer +const arrayBuffer = new ArrayBuffer(0); +assert.strictEqual(new Buffer(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(new Buffer(arrayBuffer).buffer, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).buffer, arrayBuffer); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-read.js b/cli/tests/node_compat/test/parallel/test-buffer-read.js new file mode 100644 index 00000000000000..6645250f5433a0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-read.js @@ -0,0 +1,113 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +// Testing basic buffer read functions +const buf = Buffer.from([0xa4, 0xfd, 0x48, 0xea, 0xcf, 0xff, 0xd9, 0x01, 0xde]); + +function read(buff, funx, args, expected) { + assert.strictEqual(buff[funx](...args), expected); + assert.throws( + () => buff[funx](-1, args[1]), + { code: 'ERR_OUT_OF_RANGE' } + ); +} + +// Testing basic functionality of readDoubleBE() and readDoubleLE() +read(buf, 'readDoubleBE', [1], -3.1827727774563287e+295); +read(buf, 'readDoubleLE', [1], -6.966010051009108e+144); + +// Testing basic functionality of readFloatBE() and readFloatLE() +read(buf, 'readFloatBE', [1], -1.6691549692541768e+37); +read(buf, 'readFloatLE', [1], -7861303808); + +// Testing basic functionality of readInt8() +read(buf, 'readInt8', [1], -3); + +// Testing basic functionality of readInt16BE() and readInt16LE() +read(buf, 'readInt16BE', [1], -696); +read(buf, 'readInt16LE', [1], 0x48fd); + +// Testing basic functionality of readInt32BE() and readInt32LE() +read(buf, 'readInt32BE', [1], -45552945); +read(buf, 'readInt32LE', [1], -806729475); + +// Testing basic functionality of readIntBE() and readIntLE() +read(buf, 'readIntBE', [1, 1], -3); +read(buf, 'readIntLE', [2, 1], 0x48); + +// Testing basic functionality of readUInt8() +read(buf, 'readUInt8', [1], 0xfd); + +// Testing basic functionality of readUInt16BE() and readUInt16LE() +read(buf, 'readUInt16BE', [2], 0x48ea); +read(buf, 'readUInt16LE', [2], 0xea48); + +// Testing basic functionality of readUInt32BE() and readUInt32LE() +read(buf, 'readUInt32BE', [1], 0xfd48eacf); +read(buf, 'readUInt32LE', [1], 0xcfea48fd); + +// Testing basic functionality of readUIntBE() and readUIntLE() +read(buf, 'readUIntBE', [2, 2], 0x48ea); +read(buf, 'readUIntLE', [2, 2], 0xea48); + +// Error name and message +const OOR_ERROR = +{ + name: 'RangeError' +}; + +const OOB_ERROR = +{ + name: 'RangeError', + message: 'Attempt to access memory outside buffer bounds' +}; + +// Attempt to overflow buffers, similar to previous bug in array buffers +assert.throws( + () => Buffer.allocUnsafe(8).readFloatBE(0xffffffff), OOR_ERROR); + +assert.throws( + () => Buffer.allocUnsafe(8).readFloatLE(0xffffffff), OOR_ERROR); + +// Ensure negative values can't get past offset +assert.throws( + () => Buffer.allocUnsafe(8).readFloatBE(-1), OOR_ERROR); +assert.throws( + () => Buffer.allocUnsafe(8).readFloatLE(-1), OOR_ERROR); + +// Offset checks +{ + const buf = Buffer.allocUnsafe(0); + + assert.throws( + () => buf.readUInt8(0), OOB_ERROR); + assert.throws( + () => buf.readInt8(0), OOB_ERROR); +} + +[16, 32].forEach((bit) => { + const buf = Buffer.allocUnsafe(bit / 8 - 1); + [`Int${bit}B`, `Int${bit}L`, `UInt${bit}B`, `UInt${bit}L`].forEach((fn) => { + assert.throws( + () => buf[`read${fn}E`](0), OOB_ERROR); + }); +}); + +[16, 32].forEach((bits) => { + const buf = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]); + ['LE', 'BE'].forEach((endian) => { + assert.strictEqual(buf[`readUInt${bits}${endian}`](0), + (0xFFFFFFFF >>> (32 - bits))); + + assert.strictEqual(buf[`readInt${bits}${endian}`](0), + (0xFFFFFFFF >> (32 - bits))); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-readdouble.js b/cli/tests/node_compat/test/parallel/test-buffer-readdouble.js new file mode 100644 index 00000000000000..2cd2b82eec859c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-readdouble.js @@ -0,0 +1,151 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test (64 bit) double +const buffer = Buffer.allocUnsafe(8); + +buffer[0] = 0x55; +buffer[1] = 0x55; +buffer[2] = 0x55; +buffer[3] = 0x55; +buffer[4] = 0x55; +buffer[5] = 0x55; +buffer[6] = 0xd5; +buffer[7] = 0x3f; +assert.strictEqual(buffer.readDoubleBE(0), 1.1945305291680097e+103); +assert.strictEqual(buffer.readDoubleLE(0), 0.3333333333333333); + +buffer[0] = 1; +buffer[1] = 0; +buffer[2] = 0; +buffer[3] = 0; +buffer[4] = 0; +buffer[5] = 0; +buffer[6] = 0xf0; +buffer[7] = 0x3f; +assert.strictEqual(buffer.readDoubleBE(0), 7.291122019655968e-304); +assert.strictEqual(buffer.readDoubleLE(0), 1.0000000000000002); + +buffer[0] = 2; +assert.strictEqual(buffer.readDoubleBE(0), 4.778309726801735e-299); +assert.strictEqual(buffer.readDoubleLE(0), 1.0000000000000004); + +buffer[0] = 1; +buffer[6] = 0; +buffer[7] = 0; +// eslint-disable-next-line no-loss-of-precision +assert.strictEqual(buffer.readDoubleBE(0), 7.291122019556398e-304); +assert.strictEqual(buffer.readDoubleLE(0), 5e-324); + +buffer[0] = 0xff; +buffer[1] = 0xff; +buffer[2] = 0xff; +buffer[3] = 0xff; +buffer[4] = 0xff; +buffer[5] = 0xff; +buffer[6] = 0x0f; +buffer[7] = 0x00; +assert.ok(Number.isNaN(buffer.readDoubleBE(0))); +assert.strictEqual(buffer.readDoubleLE(0), 2.225073858507201e-308); + +buffer[6] = 0xef; +buffer[7] = 0x7f; +assert.ok(Number.isNaN(buffer.readDoubleBE(0))); +assert.strictEqual(buffer.readDoubleLE(0), 1.7976931348623157e+308); + +buffer[0] = 0; +buffer[1] = 0; +buffer[2] = 0; +buffer[3] = 0; +buffer[4] = 0; +buffer[5] = 0; +buffer[6] = 0xf0; +buffer[7] = 0x3f; +assert.strictEqual(buffer.readDoubleBE(0), 3.03865e-319); +assert.strictEqual(buffer.readDoubleLE(0), 1); + +buffer[6] = 0; +buffer[7] = 0x40; +assert.strictEqual(buffer.readDoubleBE(0), 3.16e-322); +assert.strictEqual(buffer.readDoubleLE(0), 2); + +buffer[7] = 0xc0; +assert.strictEqual(buffer.readDoubleBE(0), 9.5e-322); +assert.strictEqual(buffer.readDoubleLE(0), -2); + +buffer[6] = 0x10; +buffer[7] = 0; +assert.strictEqual(buffer.readDoubleBE(0), 2.0237e-320); +assert.strictEqual(buffer.readDoubleLE(0), 2.2250738585072014e-308); + +buffer[6] = 0; +assert.strictEqual(buffer.readDoubleBE(0), 0); +assert.strictEqual(buffer.readDoubleLE(0), 0); +assert.ok(1 / buffer.readDoubleLE(0) >= 0); + +buffer[7] = 0x80; +assert.strictEqual(buffer.readDoubleBE(0), 6.3e-322); +assert.strictEqual(buffer.readDoubleLE(0), -0); +assert.ok(1 / buffer.readDoubleLE(0) < 0); + +buffer[6] = 0xf0; +buffer[7] = 0x7f; +assert.strictEqual(buffer.readDoubleBE(0), 3.0418e-319); +assert.strictEqual(buffer.readDoubleLE(0), Infinity); + +buffer[7] = 0xff; +assert.strictEqual(buffer.readDoubleBE(0), 3.04814e-319); +assert.strictEqual(buffer.readDoubleLE(0), -Infinity); + +['readDoubleLE', 'readDoubleBE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[fn](undefined); + buffer[fn](); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => buffer[fn](off), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + }); + + [Infinity, -1, 1].forEach((offset) => { + assert.throws( + () => buffer[fn](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= 0. Received ${offset}` + }); + }); + + assert.throws( + () => Buffer.alloc(1)[fn](1), + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: 'Attempt to access memory outside buffer bounds' + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-readfloat.js b/cli/tests/node_compat/test/parallel/test-buffer-readfloat.js new file mode 100644 index 00000000000000..3606594da30506 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-readfloat.js @@ -0,0 +1,113 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test 32 bit float +const buffer = Buffer.alloc(4); + +buffer[0] = 0; +buffer[1] = 0; +buffer[2] = 0x80; +buffer[3] = 0x3f; +assert.strictEqual(buffer.readFloatBE(0), 4.600602988224807e-41); +assert.strictEqual(buffer.readFloatLE(0), 1); + +buffer[0] = 0; +buffer[1] = 0; +buffer[2] = 0; +buffer[3] = 0xc0; +assert.strictEqual(buffer.readFloatBE(0), 2.6904930515036488e-43); +assert.strictEqual(buffer.readFloatLE(0), -2); + +buffer[0] = 0xff; +buffer[1] = 0xff; +buffer[2] = 0x7f; +buffer[3] = 0x7f; +assert.ok(Number.isNaN(buffer.readFloatBE(0))); +assert.strictEqual(buffer.readFloatLE(0), 3.4028234663852886e+38); + +buffer[0] = 0xab; +buffer[1] = 0xaa; +buffer[2] = 0xaa; +buffer[3] = 0x3e; +assert.strictEqual(buffer.readFloatBE(0), -1.2126478207002966e-12); +assert.strictEqual(buffer.readFloatLE(0), 0.3333333432674408); + +buffer[0] = 0; +buffer[1] = 0; +buffer[2] = 0; +buffer[3] = 0; +assert.strictEqual(buffer.readFloatBE(0), 0); +assert.strictEqual(buffer.readFloatLE(0), 0); +assert.ok(1 / buffer.readFloatLE(0) >= 0); + +buffer[3] = 0x80; +assert.strictEqual(buffer.readFloatBE(0), 1.793662034335766e-43); +assert.strictEqual(buffer.readFloatLE(0), -0); +assert.ok(1 / buffer.readFloatLE(0) < 0); + +buffer[0] = 0; +buffer[1] = 0; +buffer[2] = 0x80; +buffer[3] = 0x7f; +assert.strictEqual(buffer.readFloatBE(0), 4.609571298396486e-41); +assert.strictEqual(buffer.readFloatLE(0), Infinity); + +buffer[0] = 0; +buffer[1] = 0; +buffer[2] = 0x80; +buffer[3] = 0xff; +assert.strictEqual(buffer.readFloatBE(0), 4.627507918739843e-41); +assert.strictEqual(buffer.readFloatLE(0), -Infinity); + +['readFloatLE', 'readFloatBE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[fn](undefined); + buffer[fn](); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => buffer[fn](off), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + }); + + [Infinity, -1, 1].forEach((offset) => { + assert.throws( + () => buffer[fn](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= 0. Received ${offset}` + }); + }); + + assert.throws( + () => Buffer.alloc(1)[fn](1), + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: 'Attempt to access memory outside buffer bounds' + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-readint.js b/cli/tests/node_compat/test/parallel/test-buffer-readint.js new file mode 100644 index 00000000000000..420c2731774db9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-readint.js @@ -0,0 +1,204 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test OOB +{ + const buffer = Buffer.alloc(4); + + ['Int8', 'Int16BE', 'Int16LE', 'Int32BE', 'Int32LE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[`read${fn}`](undefined); + buffer[`read${fn}`](); + + ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { + assert.throws( + () => buffer[`read${fn}`](o), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => buffer[`read${fn}`](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError' + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[`read${fn}`](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); +} + +// Test 8 bit signed integers +{ + const data = Buffer.from([0x23, 0xab, 0x7c, 0xef]); + + assert.strictEqual(data.readInt8(0), 0x23); + + data[0] = 0xff; + assert.strictEqual(data.readInt8(0), -1); + + data[0] = 0x87; + assert.strictEqual(data.readInt8(0), -121); + assert.strictEqual(data.readInt8(1), -85); + assert.strictEqual(data.readInt8(2), 124); + assert.strictEqual(data.readInt8(3), -17); +} + +// Test 16 bit integers +{ + const buffer = Buffer.from([0x16, 0x79, 0x65, 0x6e, 0x69, 0x78]); + + assert.strictEqual(buffer.readInt16BE(0), 0x1679); + assert.strictEqual(buffer.readInt16LE(0), 0x7916); + + buffer[0] = 0xff; + buffer[1] = 0x80; + assert.strictEqual(buffer.readInt16BE(0), -128); + assert.strictEqual(buffer.readInt16LE(0), -32513); + + buffer[0] = 0x77; + buffer[1] = 0x65; + assert.strictEqual(buffer.readInt16BE(0), 0x7765); + assert.strictEqual(buffer.readInt16BE(1), 0x6565); + assert.strictEqual(buffer.readInt16BE(2), 0x656e); + assert.strictEqual(buffer.readInt16BE(3), 0x6e69); + assert.strictEqual(buffer.readInt16BE(4), 0x6978); + assert.strictEqual(buffer.readInt16LE(0), 0x6577); + assert.strictEqual(buffer.readInt16LE(1), 0x6565); + assert.strictEqual(buffer.readInt16LE(2), 0x6e65); + assert.strictEqual(buffer.readInt16LE(3), 0x696e); + assert.strictEqual(buffer.readInt16LE(4), 0x7869); +} + +// Test 32 bit integers +{ + const buffer = Buffer.from([0x43, 0x53, 0x16, 0x79, 0x36, 0x17]); + + assert.strictEqual(buffer.readInt32BE(0), 0x43531679); + assert.strictEqual(buffer.readInt32LE(0), 0x79165343); + + buffer[0] = 0xff; + buffer[1] = 0xfe; + buffer[2] = 0xef; + buffer[3] = 0xfa; + assert.strictEqual(buffer.readInt32BE(0), -69638); + assert.strictEqual(buffer.readInt32LE(0), -84934913); + + buffer[0] = 0x42; + buffer[1] = 0xc3; + buffer[2] = 0x95; + buffer[3] = 0xa9; + assert.strictEqual(buffer.readInt32BE(0), 0x42c395a9); + assert.strictEqual(buffer.readInt32BE(1), -1013601994); + assert.strictEqual(buffer.readInt32BE(2), -1784072681); + assert.strictEqual(buffer.readInt32LE(0), -1449802942); + assert.strictEqual(buffer.readInt32LE(1), 917083587); + assert.strictEqual(buffer.readInt32LE(2), 389458325); +} + +// Test Int +{ + const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]); + + assert.strictEqual(buffer.readIntLE(0, 1), 0x01); + assert.strictEqual(buffer.readIntBE(0, 1), 0x01); + assert.strictEqual(buffer.readIntLE(0, 3), 0x030201); + assert.strictEqual(buffer.readIntBE(0, 3), 0x010203); + assert.strictEqual(buffer.readIntLE(0, 5), 0x0504030201); + assert.strictEqual(buffer.readIntBE(0, 5), 0x0102030405); + assert.strictEqual(buffer.readIntLE(0, 6), 0x060504030201); + assert.strictEqual(buffer.readIntBE(0, 6), 0x010203040506); + assert.strictEqual(buffer.readIntLE(1, 6), 0x070605040302); + assert.strictEqual(buffer.readIntBE(1, 6), 0x020304050607); + assert.strictEqual(buffer.readIntLE(2, 6), 0x080706050403); + assert.strictEqual(buffer.readIntBE(2, 6), 0x030405060708); + + // Check byteLength. + ['readIntBE', 'readIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((len) => { + assert.throws( + () => buffer[fn](0, len), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [Infinity, -1].forEach((byteLength) => { + assert.throws( + () => buffer[fn](0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "byteLength" is out of range. ' + + `It must be >= 1 and <= 6. Received ${byteLength}` + }); + }); + + [NaN, 1.01].forEach((byteLength) => { + assert.throws( + () => buffer[fn](0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "byteLength" is out of range. ' + + `It must be an integer. Received ${byteLength}` + }); + }); + }); + + // Test 1 to 6 bytes. + for (let i = 1; i <= 6; i++) { + ['readIntBE', 'readIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => { + assert.throws( + () => buffer[fn](o, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => buffer[fn](offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= ${8 - i}. Received ${offset}` + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-readuint.js b/cli/tests/node_compat/test/parallel/test-buffer-readuint.js new file mode 100644 index 00000000000000..b70e2909016a42 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-readuint.js @@ -0,0 +1,172 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test OOB +{ + const buffer = Buffer.alloc(4); + + ['UInt8', 'UInt16BE', 'UInt16LE', 'UInt32BE', 'UInt32LE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[`read${fn}`](undefined); + buffer[`read${fn}`](); + + ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { + assert.throws( + () => buffer[`read${fn}`](o), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => buffer[`read${fn}`](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError' + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[`read${fn}`](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); +} + +// Test 8 bit unsigned integers +{ + const data = Buffer.from([0xff, 0x2a, 0x2a, 0x2a]); + assert.strictEqual(data.readUInt8(0), 255); + assert.strictEqual(data.readUInt8(1), 42); + assert.strictEqual(data.readUInt8(2), 42); + assert.strictEqual(data.readUInt8(3), 42); +} + +// Test 16 bit unsigned integers +{ + const data = Buffer.from([0x00, 0x2a, 0x42, 0x3f]); + assert.strictEqual(data.readUInt16BE(0), 0x2a); + assert.strictEqual(data.readUInt16BE(1), 0x2a42); + assert.strictEqual(data.readUInt16BE(2), 0x423f); + assert.strictEqual(data.readUInt16LE(0), 0x2a00); + assert.strictEqual(data.readUInt16LE(1), 0x422a); + assert.strictEqual(data.readUInt16LE(2), 0x3f42); + + data[0] = 0xfe; + data[1] = 0xfe; + assert.strictEqual(data.readUInt16BE(0), 0xfefe); + assert.strictEqual(data.readUInt16LE(0), 0xfefe); +} + +// Test 32 bit unsigned integers +{ + const data = Buffer.from([0x32, 0x65, 0x42, 0x56, 0x23, 0xff]); + assert.strictEqual(data.readUInt32BE(0), 0x32654256); + assert.strictEqual(data.readUInt32BE(1), 0x65425623); + assert.strictEqual(data.readUInt32BE(2), 0x425623ff); + assert.strictEqual(data.readUInt32LE(0), 0x56426532); + assert.strictEqual(data.readUInt32LE(1), 0x23564265); + assert.strictEqual(data.readUInt32LE(2), 0xff235642); +} + +// Test UInt +{ + const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]); + + assert.strictEqual(buffer.readUIntLE(0, 1), 0x01); + assert.strictEqual(buffer.readUIntBE(0, 1), 0x01); + assert.strictEqual(buffer.readUIntLE(0, 3), 0x030201); + assert.strictEqual(buffer.readUIntBE(0, 3), 0x010203); + assert.strictEqual(buffer.readUIntLE(0, 5), 0x0504030201); + assert.strictEqual(buffer.readUIntBE(0, 5), 0x0102030405); + assert.strictEqual(buffer.readUIntLE(0, 6), 0x060504030201); + assert.strictEqual(buffer.readUIntBE(0, 6), 0x010203040506); + assert.strictEqual(buffer.readUIntLE(1, 6), 0x070605040302); + assert.strictEqual(buffer.readUIntBE(1, 6), 0x020304050607); + assert.strictEqual(buffer.readUIntLE(2, 6), 0x080706050403); + assert.strictEqual(buffer.readUIntBE(2, 6), 0x030405060708); + + // Check byteLength. + ['readUIntBE', 'readUIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((len) => { + assert.throws( + () => buffer[fn](0, len), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [Infinity, -1].forEach((byteLength) => { + assert.throws( + () => buffer[fn](0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "byteLength" is out of range. ' + + `It must be >= 1 and <= 6. Received ${byteLength}` + }); + }); + + [NaN, 1.01].forEach((byteLength) => { + assert.throws( + () => buffer[fn](0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "byteLength" is out of range. ' + + `It must be an integer. Received ${byteLength}` + }); + }); + }); + + // Test 1 to 6 bytes. + for (let i = 1; i <= 6; i++) { + ['readUIntBE', 'readUIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => { + assert.throws( + () => buffer[fn](o, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => buffer[fn](offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= ${8 - i}. Received ${offset}` + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-safe-unsafe.js b/cli/tests/node_compat/test/parallel/test-buffer-safe-unsafe.js new file mode 100644 index 00000000000000..6529c25e515c10 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-safe-unsafe.js @@ -0,0 +1,31 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const safe = Buffer.alloc(10); + +function isZeroFilled(buf) { + for (let n = 0; n < buf.length; n++) + if (buf[n] !== 0) return false; + return true; +} + +assert(isZeroFilled(safe)); + +// Test that unsafe allocations doesn't affect subsequent safe allocations +Buffer.allocUnsafe(10); +assert(isZeroFilled(new Float64Array(10))); + +new Buffer(10); +assert(isZeroFilled(new Float64Array(10))); + +Buffer.allocUnsafe(10); +assert(isZeroFilled(Buffer.alloc(10))); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-sharedarraybuffer.js b/cli/tests/node_compat/test/parallel/test-buffer-sharedarraybuffer.js new file mode 100644 index 00000000000000..9ec8f1a6d2c248 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-sharedarraybuffer.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const sab = new SharedArrayBuffer(24); +const arr1 = new Uint16Array(sab); +const arr2 = new Uint16Array(12); +arr2[0] = 5000; +arr1[0] = 5000; +arr1[1] = 4000; +arr2[1] = 4000; + +const arr_buf = Buffer.from(arr1.buffer); +const ar_buf = Buffer.from(arr2.buffer); + +assert.deepStrictEqual(arr_buf, ar_buf); + +arr1[1] = 6000; +arr2[1] = 6000; + +assert.deepStrictEqual(arr_buf, ar_buf); + +// Checks for calling Buffer.byteLength on a SharedArrayBuffer. +assert.strictEqual(Buffer.byteLength(sab), sab.byteLength); + +Buffer.from({ buffer: sab }); // Should not throw. diff --git a/cli/tests/node_compat/test/parallel/test-buffer-slice.js b/cli/tests/node_compat/test/parallel/test-buffer-slice.js new file mode 100644 index 00000000000000..8732f69dbe123c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-slice.js @@ -0,0 +1,136 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(Buffer.from('hello', 'utf8').slice(0, 0).length, 0); +assert.strictEqual(Buffer('hello', 'utf8').slice(0, 0).length, 0); + +const buf = Buffer.from('0123456789', 'utf8'); +const expectedSameBufs = [ + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, -10), Buffer.from('', 'utf8')], + [buf.slice(), Buffer.from('0123456789', 'utf8')], + [buf.slice(0), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, 0), Buffer.from('', 'utf8')], + [buf.slice(undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice('foobar'), Buffer.from('0123456789', 'utf8')], + [buf.slice(undefined, undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice(2), Buffer.from('23456789', 'utf8')], + [buf.slice(5), Buffer.from('56789', 'utf8')], + [buf.slice(10), Buffer.from('', 'utf8')], + [buf.slice(5, 8), Buffer.from('567', 'utf8')], + [buf.slice(8, -1), Buffer.from('8', 'utf8')], + [buf.slice(-10), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, -9), Buffer.from('0', 'utf8')], + [buf.slice(0, -10), Buffer.from('', 'utf8')], + [buf.slice(0, -1), Buffer.from('012345678', 'utf8')], + [buf.slice(2, -2), Buffer.from('234567', 'utf8')], + [buf.slice(0, 65536), Buffer.from('0123456789', 'utf8')], + [buf.slice(65536, 0), Buffer.from('', 'utf8')], + [buf.slice(-5, -8), Buffer.from('', 'utf8')], + [buf.slice(-5, -3), Buffer.from('56', 'utf8')], + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice('0', '1'), Buffer.from('0', 'utf8')], + [buf.slice('-5', '10'), Buffer.from('56789', 'utf8')], + [buf.slice('-10', '10'), Buffer.from('0123456789', 'utf8')], + [buf.slice('-10', '-5'), Buffer.from('01234', 'utf8')], + [buf.slice('-10', '-0'), Buffer.from('', 'utf8')], + [buf.slice('111'), Buffer.from('', 'utf8')], + [buf.slice('0', '-111'), Buffer.from('', 'utf8')], +]; + +for (let i = 0, s = buf.toString(); i < buf.length; ++i) { + expectedSameBufs.push( + [buf.slice(i), Buffer.from(s.slice(i))], + [buf.slice(0, i), Buffer.from(s.slice(0, i))], + [buf.slice(-i), Buffer.from(s.slice(-i))], + [buf.slice(0, -i), Buffer.from(s.slice(0, -i))] + ); +} + +expectedSameBufs.forEach(([buf1, buf2]) => { + assert.strictEqual(Buffer.compare(buf1, buf2), 0); +}); + +const utf16Buf = Buffer.from('0123456789', 'utf16le'); +assert.deepStrictEqual(utf16Buf.slice(0, 6), Buffer.from('012', 'utf16le')); +// Try to slice a zero length Buffer. +// See https://github.com/joyent/node/issues/5881 +assert.strictEqual(Buffer.alloc(0).slice(0, 1).length, 0); + +{ + // Single argument slice + assert.strictEqual(Buffer.from('abcde', 'utf8').slice(1).toString('utf8'), + 'bcde'); +} + +// slice(0,0).length === 0 +assert.strictEqual(Buffer.from('hello', 'utf8').slice(0, 0).length, 0); + +{ + // Regression tests for https://github.com/nodejs/node/issues/9096 + const buf = Buffer.from('abcd', 'utf8'); + assert.strictEqual(buf.slice(buf.length / 3).toString('utf8'), 'bcd'); + assert.strictEqual( + buf.slice(buf.length / 3, buf.length).toString(), + 'bcd' + ); +} + +{ + const buf = Buffer.from('abcdefg', 'utf8'); + assert.strictEqual(buf.slice(-(-1 >>> 0) - 1).toString('utf8'), + buf.toString('utf8')); +} + +{ + const buf = Buffer.from('abc', 'utf8'); + assert.strictEqual(buf.slice(-0.5).toString('utf8'), buf.toString('utf8')); +} + +{ + const buf = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, + ]); + const chunk1 = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, + ]); + const chunk2 = Buffer.from([ + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, + ]); + const middle = buf.length / 2; + + assert.deepStrictEqual(buf.slice(0, middle), chunk1); + assert.deepStrictEqual(buf.slice(middle), chunk2); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-slow.js b/cli/tests/node_compat/test/parallel/test-buffer-slow.js new file mode 100644 index 00000000000000..1cc4e5d2eb58bf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-slow.js @@ -0,0 +1,69 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const buffer = require('buffer'); +const SlowBuffer = buffer.SlowBuffer; + +const ones = [1, 1, 1, 1]; + +// Should create a Buffer +let sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// underlying ArrayBuffer should have the same length +assert.strictEqual(sb.buffer.byteLength, 4); + +// Should work without new +sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// Should work with edge cases +assert.strictEqual(SlowBuffer(0).length, 0); +try { + assert.strictEqual( + SlowBuffer(buffer.kMaxLength).length, buffer.kMaxLength); +} catch (e) { + // Don't match on message as it is from the JavaScript engine. V8 and + // ChakraCore provide different messages. + assert.strictEqual(e.name, 'RangeError'); +} + +// Should throw with invalid length type +const bufferInvalidTypeMsg = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "size" argument must be of type number/, +}; +assert.throws(() => SlowBuffer(), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer({}), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer('6'), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer(true), bufferInvalidTypeMsg); + +// Should throw with invalid length value +const bufferMaxSizeMsg = { + code: 'ERR_INVALID_ARG_VALUE', + name: 'RangeError', + message: /^The argument 'size' is invalid\. Received [^"]*$/ +}; +assert.throws(() => SlowBuffer(NaN), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(Infinity), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(-1), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(buffer.kMaxLength + 1), bufferMaxSizeMsg); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-swap.js b/cli/tests/node_compat/test/parallel/test-buffer-swap.js new file mode 100644 index 00000000000000..5464e9d9648237 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-swap.js @@ -0,0 +1,159 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test buffers small enough to use the JS implementation +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + assert.strictEqual(buf, buf.swap16()); + assert.deepStrictEqual(buf, Buffer.from([0x02, 0x01, 0x04, 0x03, 0x06, 0x05, + 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, + 0x0e, 0x0d, 0x10, 0x0f])); + buf.swap16(); // restore + + assert.strictEqual(buf, buf.swap32()); + assert.deepStrictEqual(buf, Buffer.from([0x04, 0x03, 0x02, 0x01, 0x08, 0x07, + 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, + 0x10, 0x0f, 0x0e, 0x0d])); + buf.swap32(); // restore + + assert.strictEqual(buf, buf.swap64()); + assert.deepStrictEqual(buf, Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, + 0x0c, 0x0b, 0x0a, 0x09])); +} + +// Operates in-place +{ + const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7]); + buf.slice(1, 5).swap32(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x5, 0x4, 0x3, 0x2, 0x6, 0x7])); + buf.slice(1, 5).swap16(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7])); + + // Length assertions + const re16 = /Buffer size must be a multiple of 16-bits/; + const re32 = /Buffer size must be a multiple of 32-bits/; + const re64 = /Buffer size must be a multiple of 64-bits/; + + assert.throws(() => Buffer.from(buf).swap16(), re16); + assert.throws(() => Buffer.alloc(1025).swap16(), re16); + assert.throws(() => Buffer.from(buf).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap32(), re32); + assert.throws(() => Buffer.alloc(1025).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap64(), re64); + assert.throws(() => Buffer.alloc(1025).swap64(), re64); +} + +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + buf.slice(2, 18).swap64(); + + assert.deepStrictEqual(buf, Buffer.from([0x01, 0x02, 0x0a, 0x09, 0x08, 0x07, + 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10])); +} + +// Force use of native code (Buffer size above threshold limit for js impl) +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBufData = new Uint32Array(256).fill(0x03040102); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap16(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer); + const otherBufData = new Uint32Array(256).fill(0x01020304); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap32(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + otherBufData[otherBufData.length - i - 1] = i % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap64(); + assert.deepStrictEqual(buf, otherBuf); +} + +// Test native code with buffers that are not memory-aligned +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 2); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 2; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 2; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 0|1 0|1... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 1|0 1|0... + + buf.slice(1, buf.length - 1).swap16(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 4); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 4; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 4; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 0|1 2 3... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 3 2 1|0 3 2... + + buf.slice(1, buf.length - 3).swap32(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 4 5 6 7 0|1 2 3 4... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 7 6 5 4 3 2 1|0 7 6 5... + + buf.slice(1, buf.length - 7).swap64(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-tojson.js b/cli/tests/node_compat/test/parallel/test-buffer-tojson.js new file mode 100644 index 00000000000000..e4d344c95e09e2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-tojson.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +{ + assert.strictEqual(JSON.stringify(Buffer.alloc(0)), + '{"type":"Buffer","data":[]}'); + assert.strictEqual(JSON.stringify(Buffer.from([1, 2, 3, 4])), + '{"type":"Buffer","data":[1,2,3,4]}'); +} + +// issue GH-7849 +{ + const buf = Buffer.from('test'); + const json = JSON.stringify(buf); + const obj = JSON.parse(json); + const copy = Buffer.from(obj); + + assert.deepStrictEqual(buf, copy); +} + +// GH-5110 +{ + const buffer = Buffer.from('test'); + const string = JSON.stringify(buffer); + + assert.strictEqual(string, '{"type":"Buffer","data":[116,101,115,116]}'); + + function receiver(key, value) { + return value && value.type === 'Buffer' ? Buffer.from(value.data) : value; + } + + assert.deepStrictEqual(buffer, JSON.parse(string, receiver)); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-tostring-range.js b/cli/tests/node_compat/test/parallel/test-buffer-tostring-range.js new file mode 100644 index 00000000000000..8e6db4754756ee --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-tostring-range.js @@ -0,0 +1,107 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const rangeBuffer = Buffer.from('abc'); + +// If start >= buffer's length, empty string will be returned +assert.strictEqual(rangeBuffer.toString('ascii', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', +Infinity), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 3.14, 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 'Infinity', 3), ''); + +// If end <= 0, empty string will be returned +assert.strictEqual(rangeBuffer.toString('ascii', 1, 0), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -1.2), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -100), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -Infinity), ''); + +// If start < 0, start will be taken as zero +assert.strictEqual(rangeBuffer.toString('ascii', -1, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', -1.99, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', -Infinity, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); + +// If start is an invalid integer, start will be taken as zero +assert.strictEqual(rangeBuffer.toString('ascii', 'node.js', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', {}, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', [], 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', NaN, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', null, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', undefined, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', false, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '', 3), 'abc'); + +// But, if start is an integer when coerced, then it will be coerced and used. +assert.strictEqual(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '1', 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '3', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', Number(3), 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', '3.14', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', '1.99', 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 1.99, 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', true, 3), 'bc'); + +// If end > buffer's length, end will be taken as buffer's length +assert.strictEqual(rangeBuffer.toString('ascii', 0, 5), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 6.99), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, Infinity), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '5'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '6.99'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 'Infinity'), 'abc'); + +// If end is an invalid integer, end will be taken as buffer's length +assert.strictEqual(rangeBuffer.toString('ascii', 0, 'node.js'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, {}), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, NaN), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, undefined), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, null), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, []), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, false), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, ''), ''); + +// But, if end is an integer when coerced, then it will be coerced and used. +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-1'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '1'), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-Infinity'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '3'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, Number(3)), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '3.14'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '1.99'), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-1.99'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 1.99), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, true), 'a'); + +// Try toString() with an object as an encoding +assert.strictEqual(rangeBuffer.toString({ toString: function() { + return 'ascii'; +} }), 'abc'); + +// Try toString() with 0 and null as the encoding +assert.throws(() => { + rangeBuffer.toString(0, 1, 2); +}, { + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: 'Unknown encoding: 0' +}); +assert.throws(() => { + rangeBuffer.toString(null, 1, 2); +}, { + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: 'Unknown encoding: null' +}); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-tostring-rangeerror.js b/cli/tests/node_compat/test/parallel/test-buffer-tostring-rangeerror.js new file mode 100644 index 00000000000000..3a0f9a0227d62b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-tostring-rangeerror.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test ensures that Node.js throws a RangeError when trying to convert a +// gigantic buffer into a string. +// Regression test for https://github.com/nodejs/node/issues/649. + +const assert = require('assert'); +const SlowBuffer = require('buffer').SlowBuffer; + +const len = 1422561062959; +const message = { + code: 'ERR_INVALID_ARG_VALUE', + name: 'RangeError', + message: /^The argument 'size' is invalid\. Received [^"]*$/ +}; +assert.throws(() => Buffer(len).toString('utf8'), message); +assert.throws(() => SlowBuffer(len).toString('utf8'), message); +assert.throws(() => Buffer.alloc(len).toString('utf8'), message); +assert.throws(() => Buffer.allocUnsafe(len).toString('utf8'), message); +assert.throws(() => Buffer.allocUnsafeSlow(len).toString('utf8'), message); diff --git a/cli/tests/node_compat/test/parallel/test-buffer-tostring.js b/cli/tests/node_compat/test/parallel/test-buffer-tostring.js new file mode 100644 index 00000000000000..24e2bf1dfbd728 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-tostring.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// utf8, ucs2, ascii, latin1, utf16le +const encodings = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', + 'binary', 'utf16le', 'utf-16le']; + +encodings + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.from('foo', encoding).toString(encoding), 'foo'); + }); + +// base64 +['base64', 'BASE64'].forEach((encoding) => { + assert.strictEqual(Buffer.from('Zm9v', encoding).toString(encoding), 'Zm9v'); +}); + +// hex +['hex', 'HEX'].forEach((encoding) => { + assert.strictEqual(Buffer.from('666f6f', encoding).toString(encoding), + '666f6f'); +}); + +// Invalid encodings +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + const error = common.expectsError({ + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: `Unknown encoding: ${encoding}` + }); + assert.ok(!Buffer.isEncoding(encoding)); + assert.throws(() => Buffer.from('foo').toString(encoding), error); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-write.js b/cli/tests/node_compat/test/parallel/test-buffer-write.js new file mode 100644 index 00000000000000..5ca26c96d07512 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-write.js @@ -0,0 +1,115 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +[-1, 10].forEach((offset) => { + assert.throws( + () => Buffer.alloc(9).write('foo', offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 && <= 9. Received ${offset}` + } + ); +}); + +const resultMap = new Map([ + ['utf8', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['ucs2', Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], + ['ascii', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['latin1', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['binary', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['utf16le', Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], + ['base64', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['base64url', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['hex', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], +]); + +// utf8, ucs2, ascii, latin1, utf16le +const encodings = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', + 'binary', 'utf16le', 'utf-16le']; + +encodings + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + const buf = Buffer.alloc(9); + const len = Buffer.byteLength('foo', encoding); + assert.strictEqual(buf.write('foo', 0, len, encoding), len); + + if (encoding.includes('-')) + encoding = encoding.replace('-', ''); + + assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase())); + }); + +// base64 +['base64', 'BASE64', 'base64url', 'BASE64URL'].forEach((encoding) => { + const buf = Buffer.alloc(9); + const len = Buffer.byteLength('Zm9v', encoding); + + assert.strictEqual(buf.write('Zm9v', 0, len, encoding), len); + assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase())); +}); + +// hex +['hex', 'HEX'].forEach((encoding) => { + const buf = Buffer.alloc(9); + const len = Buffer.byteLength('666f6f', encoding); + + assert.strictEqual(buf.write('666f6f', 0, len, encoding), len); + assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase())); +}); + +// Invalid encodings +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + const error = common.expectsError({ + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: `Unknown encoding: ${encoding}` + }); + + assert.ok(!Buffer.isEncoding(encoding)); + assert.throws(() => Buffer.alloc(9).write('foo', encoding), error); +} + +// UCS-2 overflow CVE-2018-12115 +for (let i = 1; i < 4; i++) { + // Allocate two Buffers sequentially off the pool. Run more than once in case + // we hit the end of the pool and don't get sequential allocations + const x = Buffer.allocUnsafe(4).fill(0); + const y = Buffer.allocUnsafe(4).fill(1); + // Should not write anything, pos 3 doesn't have enough room for a 16-bit char + assert.strictEqual(x.write('ыыыыыы', 3, 'ucs2'), 0); + // CVE-2018-12115 experienced via buffer overrun to next block in the pool + assert.strictEqual(Buffer.compare(y, Buffer.alloc(4, 1)), 0); +} + +// Should not write any data when there is no space for 16-bit chars +const z = Buffer.alloc(4, 0); +assert.strictEqual(z.write('\u0001', 3, 'ucs2'), 0); +assert.strictEqual(Buffer.compare(z, Buffer.alloc(4, 0)), 0); +// Make sure longer strings are written up to the buffer end. +assert.strictEqual(z.write('abcd', 2), 2); +assert.deepStrictEqual([...z], [0, 0, 0x61, 0x62]); + +// Large overrun could corrupt the process +assert.strictEqual(Buffer.alloc(4) + .write('ыыыыыы'.repeat(100), 3, 'utf16le'), 0); + +{ + // .write() does not affect the byte after the written-to slice of the Buffer. + // Refs: https://github.com/nodejs/node/issues/26422 + const buf = Buffer.alloc(8); + assert.strictEqual(buf.write('ыы', 1, 'utf16le'), 4); + assert.deepStrictEqual([...buf], [0, 0x4b, 0x04, 0x4b, 0x04, 0, 0, 0]); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-writedouble.js b/cli/tests/node_compat/test/parallel/test-buffer-writedouble.js new file mode 100644 index 00000000000000..92c9c9c7331720 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-writedouble.js @@ -0,0 +1,140 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests to verify doubles are correctly written + +require('../common'); +const assert = require('assert'); + +const buffer = Buffer.allocUnsafe(16); + +buffer.writeDoubleBE(2.225073858507201e-308, 0); +buffer.writeDoubleLE(2.225073858507201e-308, 8); +assert.ok(buffer.equals(new Uint8Array([ + 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, +]))); + +buffer.writeDoubleBE(1.0000000000000004, 0); +buffer.writeDoubleLE(1.0000000000000004, 8); +assert.ok(buffer.equals(new Uint8Array([ + 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, +]))); + +buffer.writeDoubleBE(-2, 0); +buffer.writeDoubleLE(-2, 8); +assert.ok(buffer.equals(new Uint8Array([ + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, +]))); + +buffer.writeDoubleBE(1.7976931348623157e+308, 0); +buffer.writeDoubleLE(1.7976931348623157e+308, 8); +assert.ok(buffer.equals(new Uint8Array([ + 0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f, +]))); + +buffer.writeDoubleBE(0 * -1, 0); +buffer.writeDoubleLE(0 * -1, 8); +assert.ok(buffer.equals(new Uint8Array([ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +]))); + +buffer.writeDoubleBE(Infinity, 0); +buffer.writeDoubleLE(Infinity, 8); + +assert.ok(buffer.equals(new Uint8Array([ + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F, +]))); + +assert.strictEqual(buffer.readDoubleBE(0), Infinity); +assert.strictEqual(buffer.readDoubleLE(8), Infinity); + +buffer.writeDoubleBE(-Infinity, 0); +buffer.writeDoubleLE(-Infinity, 8); + +assert.ok(buffer.equals(new Uint8Array([ + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, +]))); + +assert.strictEqual(buffer.readDoubleBE(0), -Infinity); +assert.strictEqual(buffer.readDoubleLE(8), -Infinity); + +buffer.writeDoubleBE(NaN, 0); +buffer.writeDoubleLE(NaN, 8); + +// JS only knows a single NaN but there exist two platform specific +// implementations. Therefore, allow both quiet and signalling NaNs. +if (buffer[1] === 0xF7) { + assert.ok(buffer.equals(new Uint8Array([ + 0x7F, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7F, + ]))); +} else { + assert.ok(buffer.equals(new Uint8Array([ + 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F, + ]))); +} + +assert.ok(Number.isNaN(buffer.readDoubleBE(0))); +assert.ok(Number.isNaN(buffer.readDoubleLE(8))); + +// OOB in writeDouble{LE,BE} should throw. +{ + const small = Buffer.allocUnsafe(1); + + ['writeDoubleLE', 'writeDoubleBE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[fn](23, undefined); + buffer[fn](23); + + assert.throws( + () => small[fn](11.11, 0), + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: 'Attempt to access memory outside buffer bounds' + }); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => small[fn](23, off), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [Infinity, -1, 9].forEach((offset) => { + assert.throws( + () => buffer[fn](23, offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= 8. Received ${offset}` + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](42, offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-writefloat.js b/cli/tests/node_compat/test/parallel/test-buffer-writefloat.js new file mode 100644 index 00000000000000..3a70ba51c8036f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-writefloat.js @@ -0,0 +1,124 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests to verify floats are correctly written + +require('../common'); +const assert = require('assert'); + +const buffer = Buffer.allocUnsafe(8); + +buffer.writeFloatBE(1, 0); +buffer.writeFloatLE(1, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f ]))); + +buffer.writeFloatBE(1 / 3, 0); +buffer.writeFloatLE(1 / 3, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x3e, 0xaa, 0xaa, 0xab, 0xab, 0xaa, 0xaa, 0x3e ]))); + +buffer.writeFloatBE(3.4028234663852886e+38, 0); +buffer.writeFloatLE(3.4028234663852886e+38, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x7f ]))); + +buffer.writeFloatLE(1.1754943508222875e-38, 0); +buffer.writeFloatBE(1.1754943508222875e-38, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00 ]))); + +buffer.writeFloatBE(0 * -1, 0); +buffer.writeFloatLE(0 * -1, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 ]))); + +buffer.writeFloatBE(Infinity, 0); +buffer.writeFloatLE(Infinity, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F ]))); + +assert.strictEqual(buffer.readFloatBE(0), Infinity); +assert.strictEqual(buffer.readFloatLE(4), Infinity); + +buffer.writeFloatBE(-Infinity, 0); +buffer.writeFloatLE(-Infinity, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF ]))); + +assert.strictEqual(buffer.readFloatBE(0), -Infinity); +assert.strictEqual(buffer.readFloatLE(4), -Infinity); + +buffer.writeFloatBE(NaN, 0); +buffer.writeFloatLE(NaN, 4); + +// JS only knows a single NaN but there exist two platform specific +// implementations. Therefore, allow both quiet and signalling NaNs. +if (buffer[1] === 0xBF) { + assert.ok( + buffer.equals(new Uint8Array( + [ 0x7F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x7F ]))); +} else { + assert.ok( + buffer.equals(new Uint8Array( + [ 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x7F ]))); +} + +assert.ok(Number.isNaN(buffer.readFloatBE(0))); +assert.ok(Number.isNaN(buffer.readFloatLE(4))); + +// OOB in writeFloat{LE,BE} should throw. +{ + const small = Buffer.allocUnsafe(1); + + ['writeFloatLE', 'writeFloatBE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[fn](23, undefined); + buffer[fn](23); + + assert.throws( + () => small[fn](11.11, 0), + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: 'Attempt to access memory outside buffer bounds' + }); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => small[fn](23, off), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + }); + + [Infinity, -1, 5].forEach((offset) => { + assert.throws( + () => buffer[fn](23, offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= 4. Received ${offset}` + } + ); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](42, offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-writeint.js b/cli/tests/node_compat/test/parallel/test-buffer-writeint.js new file mode 100644 index 00000000000000..6f67cf451fad6f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-writeint.js @@ -0,0 +1,277 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests to verify signed integers are correctly written + +require('../common'); +const assert = require('assert'); +const errorOutOfBounds = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: new RegExp('^The value of "value" is out of range\\. ' + + 'It must be >= -\\d+ and <= \\d+\\. Received .+$') +}; + +// Test 8 bit +{ + const buffer = Buffer.alloc(2); + + buffer.writeInt8(0x23, 0); + buffer.writeInt8(-5, 1); + assert.ok(buffer.equals(new Uint8Array([ 0x23, 0xfb ]))); + + /* Make sure we handle min/max correctly */ + buffer.writeInt8(0x7f, 0); + buffer.writeInt8(-0x80, 1); + assert.ok(buffer.equals(new Uint8Array([ 0x7f, 0x80 ]))); + + assert.throws(() => { + buffer.writeInt8(0x7f + 1, 0); + }, errorOutOfBounds); + assert.throws(() => { + buffer.writeInt8(-0x80 - 1, 0); + }, errorOutOfBounds); + + // Verify that default offset works fine. + buffer.writeInt8(23, undefined); + buffer.writeInt8(23); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => buffer.writeInt8(23, off), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [NaN, Infinity, -1, 1.01].forEach((off) => { + assert.throws( + () => buffer.writeInt8(23, off), + { code: 'ERR_OUT_OF_RANGE' }); + }); +} + +// Test 16 bit +{ + const buffer = Buffer.alloc(4); + + buffer.writeInt16BE(0x0023, 0); + buffer.writeInt16LE(0x0023, 2); + assert.ok(buffer.equals(new Uint8Array([ 0x00, 0x23, 0x23, 0x00 ]))); + + buffer.writeInt16BE(-5, 0); + buffer.writeInt16LE(-5, 2); + assert.ok(buffer.equals(new Uint8Array([ 0xff, 0xfb, 0xfb, 0xff ]))); + + buffer.writeInt16BE(-1679, 0); + buffer.writeInt16LE(-1679, 2); + assert.ok(buffer.equals(new Uint8Array([ 0xf9, 0x71, 0x71, 0xf9 ]))); + + /* Make sure we handle min/max correctly */ + buffer.writeInt16BE(0x7fff, 0); + buffer.writeInt16BE(-0x8000, 2); + assert.ok(buffer.equals(new Uint8Array([ 0x7f, 0xff, 0x80, 0x00 ]))); + + buffer.writeInt16LE(0x7fff, 0); + buffer.writeInt16LE(-0x8000, 2); + assert.ok(buffer.equals(new Uint8Array([ 0xff, 0x7f, 0x00, 0x80 ]))); + + ['writeInt16BE', 'writeInt16LE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[fn](23, undefined); + buffer[fn](23); + + assert.throws(() => { + buffer[fn](0x7fff + 1, 0); + }, errorOutOfBounds); + assert.throws(() => { + buffer[fn](-0x8000 - 1, 0); + }, errorOutOfBounds); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => buffer[fn](23, off), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [NaN, Infinity, -1, 1.01].forEach((off) => { + assert.throws( + () => buffer[fn](23, off), + { code: 'ERR_OUT_OF_RANGE' }); + }); + }); +} + +// Test 32 bit +{ + const buffer = Buffer.alloc(8); + + buffer.writeInt32BE(0x23, 0); + buffer.writeInt32LE(0x23, 4); + assert.ok(buffer.equals(new Uint8Array([ + 0x00, 0x00, 0x00, 0x23, 0x23, 0x00, 0x00, 0x00, + ]))); + + buffer.writeInt32BE(-5, 0); + buffer.writeInt32LE(-5, 4); + assert.ok(buffer.equals(new Uint8Array([ + 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xff, 0xff, 0xff, + ]))); + + buffer.writeInt32BE(-805306713, 0); + buffer.writeInt32LE(-805306713, 4); + assert.ok(buffer.equals(new Uint8Array([ + 0xcf, 0xff, 0xfe, 0xa7, 0xa7, 0xfe, 0xff, 0xcf, + ]))); + + /* Make sure we handle min/max correctly */ + buffer.writeInt32BE(0x7fffffff, 0); + buffer.writeInt32BE(-0x80000000, 4); + assert.ok(buffer.equals(new Uint8Array([ + 0x7f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, + ]))); + + buffer.writeInt32LE(0x7fffffff, 0); + buffer.writeInt32LE(-0x80000000, 4); + assert.ok(buffer.equals(new Uint8Array([ + 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x80, + ]))); + + ['writeInt32BE', 'writeInt32LE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[fn](23, undefined); + buffer[fn](23); + + assert.throws(() => { + buffer[fn](0x7fffffff + 1, 0); + }, errorOutOfBounds); + assert.throws(() => { + buffer[fn](-0x80000000 - 1, 0); + }, errorOutOfBounds); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => buffer[fn](23, off), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [NaN, Infinity, -1, 1.01].forEach((off) => { + assert.throws( + () => buffer[fn](23, off), + { code: 'ERR_OUT_OF_RANGE' }); + }); + }); +} + +// Test 48 bit +{ + const value = 0x1234567890ab; + const buffer = Buffer.allocUnsafe(6); + buffer.writeIntBE(value, 0, 6); + assert.ok(buffer.equals(new Uint8Array([ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, + ]))); + + buffer.writeIntLE(value, 0, 6); + assert.ok(buffer.equals(new Uint8Array([ + 0xab, 0x90, 0x78, 0x56, 0x34, 0x12, + ]))); +} + +// Test Int +{ + const data = Buffer.alloc(8); + + // Check byteLength. + ['writeIntBE', 'writeIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((bl) => { + assert.throws( + () => data[fn](23, 0, bl), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [Infinity, -1].forEach((byteLength) => { + assert.throws( + () => data[fn](23, 0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "byteLength" is out of range. ' + + `It must be >= 1 and <= 6. Received ${byteLength}` + } + ); + }); + + [NaN, 1.01].forEach((byteLength) => { + assert.throws( + () => data[fn](42, 0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "byteLength" is out of range. ' + + `It must be an integer. Received ${byteLength}` + }); + }); + }); + + // Test 1 to 6 bytes. + for (let i = 1; i <= 6; i++) { + ['writeIntBE', 'writeIntLE'].forEach((fn) => { + const min = -(2 ** (i * 8 - 1)); + const max = 2 ** (i * 8 - 1) - 1; + let range = `>= ${min} and <= ${max}`; + if (i > 4) { + range = `>= -(2 ** ${i * 8 - 1}) and < 2 ** ${i * 8 - 1}`; + } + [min - 1, max + 1].forEach((val) => { + const received = i > 4 ? + String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_') : + val; + assert.throws(() => { + data[fn](val, 0, i); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "value" is out of range. ' + + `It must be ${range}. Received ${received}` + }); + }); + + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => { + assert.throws( + () => data[fn](min, o, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => data[fn](min, offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= ${8 - i}. Received ${offset}` + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => data[fn](max, offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-writeuint.js b/cli/tests/node_compat/test/parallel/test-buffer-writeuint.js new file mode 100644 index 00000000000000..43867476b5d816 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-writeuint.js @@ -0,0 +1,237 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// We need to check the following things: +// - We are correctly resolving big endian (doesn't mean anything for 8 bit) +// - Correctly resolving little endian (doesn't mean anything for 8 bit) +// - Correctly using the offsets +// - Correctly interpreting values that are beyond the signed range as unsigned + +{ // OOB + const data = Buffer.alloc(8); + ['UInt8', 'UInt16BE', 'UInt16LE', 'UInt32BE', 'UInt32LE'].forEach((fn) => { + + // Verify that default offset works fine. + data[`write${fn}`](23, undefined); + data[`write${fn}`](23); + + ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { + assert.throws( + () => data[`write${fn}`](23, o), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [NaN, Infinity, -1, 1.01].forEach((o) => { + assert.throws( + () => data[`write${fn}`](23, o), + { code: 'ERR_OUT_OF_RANGE' }); + }); + }); +} + +{ // Test 8 bit + const data = Buffer.alloc(4); + + data.writeUInt8(23, 0); + data.writeUInt8(23, 1); + data.writeUInt8(23, 2); + data.writeUInt8(23, 3); + assert.ok(data.equals(new Uint8Array([23, 23, 23, 23]))); + + data.writeUInt8(23, 0); + data.writeUInt8(23, 1); + data.writeUInt8(23, 2); + data.writeUInt8(23, 3); + assert.ok(data.equals(new Uint8Array([23, 23, 23, 23]))); + + data.writeUInt8(255, 0); + assert.strictEqual(data[0], 255); + + data.writeUInt8(255, 0); + assert.strictEqual(data[0], 255); +} + +// Test 16 bit +{ + let value = 0x2343; + const data = Buffer.alloc(4); + + data.writeUInt16BE(value, 0); + assert.ok(data.equals(new Uint8Array([0x23, 0x43, 0, 0]))); + + data.writeUInt16BE(value, 1); + assert.ok(data.equals(new Uint8Array([0x23, 0x23, 0x43, 0]))); + + data.writeUInt16BE(value, 2); + assert.ok(data.equals(new Uint8Array([0x23, 0x23, 0x23, 0x43]))); + + data.writeUInt16LE(value, 0); + assert.ok(data.equals(new Uint8Array([0x43, 0x23, 0x23, 0x43]))); + + data.writeUInt16LE(value, 1); + assert.ok(data.equals(new Uint8Array([0x43, 0x43, 0x23, 0x43]))); + + data.writeUInt16LE(value, 2); + assert.ok(data.equals(new Uint8Array([0x43, 0x43, 0x43, 0x23]))); + + value = 0xff80; + data.writeUInt16LE(value, 0); + assert.ok(data.equals(new Uint8Array([0x80, 0xff, 0x43, 0x23]))); + + data.writeUInt16BE(value, 0); + assert.ok(data.equals(new Uint8Array([0xff, 0x80, 0x43, 0x23]))); + + value = 0xfffff; + ['writeUInt16BE', 'writeUInt16LE'].forEach((fn) => { + assert.throws( + () => data[fn](value, 0), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "value" is out of range. ' + + `It must be >= 0 and <= 65535. Received ${value}` + } + ); + }); +} + +// Test 32 bit +{ + const data = Buffer.alloc(6); + const value = 0xe7f90a6d; + + data.writeUInt32BE(value, 0); + assert.ok(data.equals(new Uint8Array([0xe7, 0xf9, 0x0a, 0x6d, 0, 0]))); + + data.writeUInt32BE(value, 1); + assert.ok(data.equals(new Uint8Array([0xe7, 0xe7, 0xf9, 0x0a, 0x6d, 0]))); + + data.writeUInt32BE(value, 2); + assert.ok(data.equals(new Uint8Array([0xe7, 0xe7, 0xe7, 0xf9, 0x0a, 0x6d]))); + + data.writeUInt32LE(value, 0); + assert.ok(data.equals(new Uint8Array([0x6d, 0x0a, 0xf9, 0xe7, 0x0a, 0x6d]))); + + data.writeUInt32LE(value, 1); + assert.ok(data.equals(new Uint8Array([0x6d, 0x6d, 0x0a, 0xf9, 0xe7, 0x6d]))); + + data.writeUInt32LE(value, 2); + assert.ok(data.equals(new Uint8Array([0x6d, 0x6d, 0x6d, 0x0a, 0xf9, 0xe7]))); +} + +// Test 48 bit +{ + const value = 0x1234567890ab; + const data = Buffer.allocUnsafe(6); + data.writeUIntBE(value, 0, 6); + assert.ok(data.equals(new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]))); + + data.writeUIntLE(value, 0, 6); + assert.ok(data.equals(new Uint8Array([0xab, 0x90, 0x78, 0x56, 0x34, 0x12]))); +} + +// Test UInt +{ + const data = Buffer.alloc(8); + let val = 0x100; + + // Check byteLength. + ['writeUIntBE', 'writeUIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((bl) => { + assert.throws( + () => data[fn](23, 0, bl), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [Infinity, -1].forEach((byteLength) => { + assert.throws( + () => data[fn](23, 0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "byteLength" is out of range. ' + + `It must be >= 1 and <= 6. Received ${byteLength}` + } + ); + }); + + [NaN, 1.01].forEach((byteLength) => { + assert.throws( + () => data[fn](42, 0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "byteLength" is out of range. ' + + `It must be an integer. Received ${byteLength}` + }); + }); + }); + + // Test 1 to 6 bytes. + for (let i = 1; i <= 6; i++) { + const range = i < 5 ? `= ${val - 1}` : ` 2 ** ${i * 8}`; + const received = i > 4 ? + String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_') : + val; + ['writeUIntBE', 'writeUIntLE'].forEach((fn) => { + assert.throws(() => { + data[fn](val, 0, i); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "value" is out of range. ' + + `It must be >= 0 and <${range}. Received ${received}` + }); + + ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { + assert.throws( + () => data[fn](23, o, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => data[fn](val - 1, offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= ${8 - i}. Received ${offset}` + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => data[fn](val - 1, offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); + + val *= 0x100; + } +} + +for (const fn of [ + 'UInt8', 'UInt16LE', 'UInt16BE', 'UInt32LE', 'UInt32BE', 'UIntLE', 'UIntBE', + 'BigUInt64LE', 'BigUInt64BE', +]) { + const p = Buffer.prototype; + const lowerFn = fn.replace(/UInt/, 'Uint'); + assert.strictEqual(p[`write${fn}`], p[`write${lowerFn}`]); + assert.strictEqual(p[`read${fn}`], p[`read${lowerFn}`]); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-zero-fill-cli.js b/cli/tests/node_compat/test/parallel/test-buffer-zero-fill-cli.js new file mode 100644 index 00000000000000..02a91b2e69d619 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-zero-fill-cli.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Flags: --zero-fill-buffers + +// when using --zero-fill-buffers, every Buffer and SlowBuffer +// instance must be zero filled upon creation + +require('../common'); +const SlowBuffer = require('buffer').SlowBuffer; +const assert = require('assert'); + +function isZeroFilled(buf) { + for (const n of buf) + if (n > 0) return false; + return true; +} + +// This can be somewhat unreliable because the +// allocated memory might just already happen to +// contain all zeroes. The test is run multiple +// times to improve the reliability. +for (let i = 0; i < 50; i++) { + const bufs = [ + Buffer.alloc(20), + Buffer.allocUnsafe(20), + SlowBuffer(20), + Buffer(20), + new SlowBuffer(20), + ]; + for (const buf of bufs) { + assert(isZeroFilled(buf)); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-zero-fill-reset.js b/cli/tests/node_compat/test/parallel/test-buffer-zero-fill-reset.js new file mode 100644 index 00000000000000..5812f2609fd286 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-zero-fill-reset.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + + +function testUint8Array(ui) { + const length = ui.length; + for (let i = 0; i < length; i++) + if (ui[i] !== 0) return false; + return true; +} + + +for (let i = 0; i < 100; i++) { + Buffer.alloc(0); + const ui = new Uint8Array(65); + assert.ok(testUint8Array(ui), `Uint8Array is not zero-filled: ${ui}`); +} diff --git a/cli/tests/node_compat/test/parallel/test-buffer-zero-fill.js b/cli/tests/node_compat/test/parallel/test-buffer-zero-fill.js new file mode 100644 index 00000000000000..c1f9219cdd52d4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-buffer-zero-fill.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Tests deprecated Buffer API on purpose +const buf1 = Buffer(100); +const buf2 = new Buffer(100); + +for (let n = 0; n < buf1.length; n++) + assert.strictEqual(buf1[n], 0); + +for (let n = 0; n < buf2.length; n++) + assert.strictEqual(buf2[n], 0); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-can-write-to-stdout.js b/cli/tests/node_compat/test/parallel/test-child-process-can-write-to-stdout.js new file mode 100644 index 00000000000000..11dbf9bcb3d211 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-can-write-to-stdout.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Tests that a spawned child process can write to stdout without throwing. +// See https://github.com/nodejs/node-v0.x-archive/issues/1899. + +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.argv[0], [ + fixtures.path('GH-1899-output.js'), +]); +let output = ''; + +child.stdout.on('data', function(data) { + output += data; +}); + +child.on('exit', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(output, 'hello, world!\n'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-default-options.js b/cli/tests/node_compat/test/parallel/test-child-process-default-options.js new file mode 100644 index 00000000000000..6f91058b51d745 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-default-options.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; +const debug = require('util').debuglog('test'); + +process.env.HELLO = 'WORLD'; + +let child; +if (isWindows) { + child = spawn('cmd.exe', ['/c', 'set'], {}); +} else { + child = spawn('/usr/bin/env', [], {}); +} + +let response = ''; + +child.stdout.setEncoding('utf8'); + +child.stdout.on('data', function(chunk) { + debug(`stdout: ${chunk}`); + response += chunk; +}); + +process.on('exit', function() { + assert.ok(response.includes('HELLO=WORLD'), + 'spawn did not use process.env as default ' + + `(process.env.HELLO = ${process.env.HELLO})`); +}); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-double-pipe.js b/cli/tests/node_compat/test/parallel/test-child-process-double-pipe.js new file mode 100644 index 00000000000000..081eda3cb40efe --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-double-pipe.js @@ -0,0 +1,129 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { + isWindows, + mustCall, + mustCallAtLeast, +} = require('../common'); +const assert = require('assert'); +const os = require('os'); +const spawn = require('child_process').spawn; +const debug = require('util').debuglog('test'); + +// We're trying to reproduce: +// $ echo "hello\nnode\nand\nworld" | grep o | sed s/o/a/ + +let grep, sed, echo; + +if (isWindows) { + grep = spawn('grep', ['--binary', 'o']); + sed = spawn('sed', ['--binary', 's/o/O/']); + echo = spawn('cmd.exe', + ['/c', 'echo', 'hello&&', 'echo', + 'node&&', 'echo', 'and&&', 'echo', 'world']); +} else { + grep = spawn('grep', ['o']); + sed = spawn('sed', ['s/o/O/']); + echo = spawn('echo', ['hello\nnode\nand\nworld\n']); +} + +// If the spawn function leaks file descriptors to subprocesses, grep and sed +// hang. +// This happens when calling pipe(2) and then forgetting to set the +// FD_CLOEXEC flag on the resulting file descriptors. +// +// This test checks child processes exit, meaning they don't hang like +// explained above. + + +// pipe echo | grep +echo.stdout.on('data', mustCallAtLeast((data) => { + debug(`grep stdin write ${data.length}`); + if (!grep.stdin.write(data)) { + echo.stdout.pause(); + } +})); + +// TODO(@jasnell): This does not appear to ever be +// emitted. It's not clear if it is necessary. +grep.stdin.on('drain', (data) => { + echo.stdout.resume(); +}); + +// Propagate end from echo to grep +echo.stdout.on('end', mustCall((code) => { + grep.stdin.end(); +})); + +echo.on('exit', mustCall(() => { + debug('echo exit'); +})); + +grep.on('exit', mustCall(() => { + debug('grep exit'); +})); + +sed.on('exit', mustCall(() => { + debug('sed exit'); +})); + + +// pipe grep | sed +grep.stdout.on('data', mustCallAtLeast((data) => { + debug(`grep stdout ${data.length}`); + if (!sed.stdin.write(data)) { + grep.stdout.pause(); + } +})); + +// TODO(@jasnell): This does not appear to ever be +// emitted. It's not clear if it is necessary. +sed.stdin.on('drain', (data) => { + grep.stdout.resume(); +}); + +// Propagate end from grep to sed +grep.stdout.on('end', mustCall((code) => { + debug('grep stdout end'); + sed.stdin.end(); +})); + + +let result = ''; + +// print sed's output +sed.stdout.on('data', mustCallAtLeast((data) => { + result += data.toString('utf8', 0, data.length); + debug(data); +})); + +sed.stdout.on('end', mustCall((code) => { + assert.strictEqual(result, `hellO${os.EOL}nOde${os.EOL}wOrld${os.EOL}`); +})); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-abortcontroller-promisified.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-abortcontroller-promisified.js new file mode 100644 index 00000000000000..4ba699ba49e8ea --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-abortcontroller-promisified.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The "eval" subcommand passed to execPromisifed() should be the "-e" option. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; +const { promisify } = require('util'); + +const execPromisifed = promisify(exec); +const invalidArgTypeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}; + +const waitCommand = common.isLinux ? + 'sleep 2m' : + `${process.execPath} eval "setInterval(()=>{}, 99)"`; + +{ + const ac = new AbortController(); + const signal = ac.signal; + const promise = execPromisifed(waitCommand, { signal }); + assert.rejects(promise, /AbortError/, 'post aborted sync signal failed') + .then(common.mustCall()); + ac.abort(); +} + +{ + assert.throws(() => { + execPromisifed(waitCommand, { signal: {} }); + }, invalidArgTypeError); +} + +{ + function signal() {} + assert.throws(() => { + execPromisifed(waitCommand, { signal }); + }, invalidArgTypeError); +} + +{ + const signal = AbortSignal.abort(); // Abort in advance + const promise = execPromisifed(waitCommand, { signal }); + + assert.rejects(promise, /AbortError/, 'pre aborted signal failed') + .then(common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-cwd.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-cwd.js new file mode 100644 index 00000000000000..79f18a43584ef9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-cwd.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; + +let pwdcommand, dir; + +if (common.isWindows) { + pwdcommand = 'echo %cd%'; + dir = 'c:\\windows'; +} else { + pwdcommand = 'pwd'; + dir = '/dev'; +} + +exec(pwdcommand, { cwd: dir }, common.mustSucceed((stdout, stderr) => { + assert(stdout.startsWith(dir)); +})); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-encoding.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-encoding.js new file mode 100644 index 00000000000000..fe03e98d06635d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-encoding.js @@ -0,0 +1,59 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The process.argv[3] check should be argv[2], and the +// command passed to exec() should not need to include "run", "-A", +// and "require.ts". + +'use strict'; +const common = require('../common'); +const stdoutData = 'foo'; +const stderrData = 'bar'; + +if (process.argv[3] === 'child') { + // The following console calls are part of the test. + console.log(stdoutData); + console.error(stderrData); +} else { + const assert = require('assert'); + const cp = require('child_process'); + const expectedStdout = `${stdoutData}\n`; + const expectedStderr = `${stderrData}\n`; + function run(options, callback) { + const cmd = `"${process.execPath}" run -A require.ts "${__filename}" child`; + + cp.exec(cmd, options, common.mustSucceed((stdout, stderr) => { + callback(stdout, stderr); + })); + } + + // Test default encoding, which should be utf8. + run({}, (stdout, stderr) => { + assert.strictEqual(typeof stdout, 'string'); + assert.strictEqual(typeof stderr, 'string'); + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + }); + + // Test explicit utf8 encoding. + run({ encoding: 'utf8' }, (stdout, stderr) => { + assert.strictEqual(typeof stdout, 'string'); + assert.strictEqual(typeof stderr, 'string'); + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + }); + + // Test cases that result in buffer encodings. + [undefined, null, 'buffer', 'invalid'].forEach((encoding) => { + run({ encoding }, (stdout, stderr) => { + assert(stdout instanceof Buffer); + assert(stdout instanceof Buffer); + assert.strictEqual(stdout.toString(), expectedStdout); + assert.strictEqual(stderr.toString(), expectedStderr); + }); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-env.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-env.js new file mode 100644 index 00000000000000..6a70252612e9c4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-env.js @@ -0,0 +1,71 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; +const debug = require('util').debuglog('test'); + +let success_count = 0; +let error_count = 0; +let response = ''; +let child; + +function after(err, stdout, stderr) { + if (err) { + error_count++; + debug(`error!: ${err.code}`); + debug(`stdout: ${JSON.stringify(stdout)}`); + debug(`stderr: ${JSON.stringify(stderr)}`); + assert.strictEqual(err.killed, false); + } else { + success_count++; + assert.notStrictEqual(stdout, ''); + } +} + +if (!isWindows) { + child = exec('/usr/bin/env', { env: { 'HELLO': 'WORLD' } }, after); +} else { + child = exec('set', + { env: { ...process.env, 'HELLO': 'WORLD' } }, + after); +} + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(chunk) { + response += chunk; +}); + +process.on('exit', function() { + debug('response: ', response); + assert.strictEqual(success_count, 1); + assert.strictEqual(error_count, 0); + assert.ok(response.includes('HELLO=WORLD')); +}); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-error.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-error.js new file mode 100644 index 00000000000000..3c553457d92fcf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-error.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); + +function test(fn, code, expectPidType = 'number') { + const child = fn('does-not-exist', common.mustCall(function(err) { + assert.strictEqual(err.code, code); + assert(err.cmd.includes('does-not-exist')); + })); + + assert.strictEqual(typeof child.pid, expectPidType); +} + +// With `shell: true`, expect pid (of the shell) +if (common.isWindows) { + test(child_process.exec, 1, 'number'); // Exit code of cmd.exe +} else { + test(child_process.exec, 127, 'number'); // Exit code of /bin/sh +} + +// With `shell: false`, expect no pid +test(child_process.execFile, 'ENOENT', 'undefined'); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-kill-throws.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-kill-throws.js new file mode 100644 index 00000000000000..6a28c2a18dfd23 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-kill-throws.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The process.argv[3] check should be argv[2], and the +// command passed to exec() should not need to include "run", "-A", +// and "require.ts". + +'use strict'; +// Flags: --expose-internals +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +if (process.argv[3] === 'child') { + // Since maxBuffer is 0, this should trigger an error. + console.log('foo'); +} else { + const internalCp = require('internal/child_process'); + + // Monkey patch ChildProcess#kill() to kill the process and then throw. + const kill = internalCp.ChildProcess.prototype.kill; + + internalCp.ChildProcess.prototype.kill = function() { + kill.apply(this, arguments); + throw new Error('mock error'); + }; + + const cmd = `"${process.execPath}" run -A require.ts "${__filename}" child`; + const options = { maxBuffer: 0, killSignal: 'SIGKILL' }; + + const child = cp.exec(cmd, options, common.mustCall((err, stdout, stderr) => { + // Verify that if ChildProcess#kill() throws, the error is reported. + assert.strictEqual(err.message, 'mock error', err); + assert.strictEqual(stdout, ''); + assert.strictEqual(stderr, ''); + assert.strictEqual(child.killed, true); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-maxbuf.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-maxbuf.js new file mode 100644 index 00000000000000..2e99855c040f52 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-maxbuf.js @@ -0,0 +1,161 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The "eval" subcommand passed to exec() should be the "-e" option. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +function runChecks(err, stdio, streamName, expected) { + assert.strictEqual(err.message, `${streamName} maxBuffer length exceeded`); + assert(err instanceof RangeError); + assert.strictEqual(err.code, 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'); + assert.deepStrictEqual(stdio[streamName], expected); +} + +// default value +{ + const cmd = + `"${process.execPath}" eval "console.log('a'.repeat(1024 * 1024))"`; + + cp.exec(cmd, common.mustCall((err) => { + assert(err instanceof RangeError); + assert.strictEqual(err.message, 'stdout maxBuffer length exceeded'); + assert.strictEqual(err.code, 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'); + })); +} + +// default value +{ + const cmd = + `${process.execPath} eval "console.log('a'.repeat(1024 * 1024 - 1))"`; + + cp.exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'a'.repeat(1024 * 1024 - 1)); + assert.strictEqual(stderr, ''); + })); +} + +{ + const cmd = `"${process.execPath}" eval "console.log('hello world');"`; + const options = { maxBuffer: Infinity }; + + cp.exec(cmd, options, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'hello world'); + assert.strictEqual(stderr, ''); + })); +} + +{ + const cmd = 'echo hello world'; + + cp.exec( + cmd, + { maxBuffer: 5 }, + common.mustCall((err, stdout, stderr) => { + runChecks(err, { stdout, stderr }, 'stdout', 'hello'); + }) + ); +} + +// default value +{ + const cmd = + `"${process.execPath}" eval "console.log('a'.repeat(1024 * 1024))"`; + + cp.exec( + cmd, + common.mustCall((err, stdout, stderr) => { + runChecks( + err, + { stdout, stderr }, + 'stdout', + 'a'.repeat(1024 * 1024) + ); + }) + ); +} + +// default value +{ + const cmd = + `"${process.execPath}" eval "console.log('a'.repeat(1024 * 1024 - 1))"`; + + cp.exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'a'.repeat(1024 * 1024 - 1)); + assert.strictEqual(stderr, ''); + })); +} + +const unicode = '中文测试'; // length = 4, byte length = 12 + +{ + const cmd = `"${process.execPath}" eval "console.log('${unicode}');"`; + + cp.exec( + cmd, + { maxBuffer: 10 }, + common.mustCall((err, stdout, stderr) => { + runChecks(err, { stdout, stderr }, 'stdout', '中文测试\n'); + }) + ); +} + +{ + const cmd = `"${process.execPath}" eval "console.error('${unicode}');"`; + + cp.exec( + cmd, + { maxBuffer: 3 }, + common.mustCall((err, stdout, stderr) => { + runChecks(err, { stdout, stderr }, 'stderr', '中文测'); + }) + ); +} + +{ + const cmd = `"${process.execPath}" eval "console.log('${unicode}');"`; + + const child = cp.exec( + cmd, + { encoding: null, maxBuffer: 10 }, + common.mustCall((err, stdout, stderr) => { + runChecks(err, { stdout, stderr }, 'stdout', '中文测试\n'); + }) + ); + + child.stdout.setEncoding('utf-8'); +} + +{ + const cmd = `"${process.execPath}" eval "console.error('${unicode}');"`; + + const child = cp.exec( + cmd, + { encoding: null, maxBuffer: 3 }, + common.mustCall((err, stdout, stderr) => { + runChecks(err, { stdout, stderr }, 'stderr', '中文测'); + }) + ); + + child.stderr.setEncoding('utf-8'); +} + +{ + const cmd = `"${process.execPath}" eval "console.error('${unicode}');"`; + + cp.exec( + cmd, + { encoding: null, maxBuffer: 5 }, + common.mustCall((err, stdout, stderr) => { + const buf = Buffer.from(unicode).slice(0, 5); + runChecks(err, { stdout, stderr }, 'stderr', buf); + }) + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-std-encoding.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-std-encoding.js new file mode 100644 index 00000000000000..85f3ec2bf17507 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-std-encoding.js @@ -0,0 +1,33 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The process.argv[3] check should be argv[2], and the +// command passed to exec() should not need to include "run", "-A", +// and "require.ts". + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const stdoutData = 'foo'; +const stderrData = 'bar'; +const expectedStdout = `${stdoutData}\n`; +const expectedStderr = `${stderrData}\n`; + +if (process.argv[3] === 'child') { + // The following console calls are part of the test. + console.log(stdoutData); + console.error(stderrData); +} else { + const cmd = `"${process.execPath}" run -A require.ts "${__filename}" child`; + const child = cp.exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + })); + child.stdout.setEncoding('utf-8'); + child.stderr.setEncoding('utf-8'); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-stdout-stderr-data-string.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-stdout-stderr-data-string.js new file mode 100644 index 00000000000000..6a4c9fe5fdda23 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-stdout-stderr-data-string.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Refs: https://github.com/nodejs/node/issues/7342 +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; + +const command = common.isWindows ? 'dir' : 'ls'; + +exec(command).stdout.on('data', common.mustCallAtLeast()); + +exec('fhqwhgads').stderr.on('data', common.mustCallAtLeast((data) => { + assert.strictEqual(typeof data, 'string'); +})); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-expire.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-expire.js new file mode 100644 index 00000000000000..67c4a720460838 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-expire.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The process.argv[3] check should be argv[2], and the +// command passed to exec() should not need to include "run", "-A", +// and "require.ts". + +'use strict'; + +// Test exec() with a timeout that expires. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime, + kExpiringChildRunTime, + kExpiringParentTimer +} = require('../common/child_process'); + +if (process.argv[3] === 'child') { + logAfterTime(kExpiringChildRunTime); + return; +} + +const cmd = `"${process.execPath}" run -A require.ts "${__filename}" child`; + +cp.exec(cmd, { + timeout: kExpiringParentTimer, +}, common.mustCall((err, stdout, stderr) => { + console.log('[stdout]', stdout.trim()); + console.log('[stderr]', stderr.trim()); + + let sigterm = 'SIGTERM'; + assert.strictEqual(err.killed, true); + // TODO OpenBSD returns a null signal and 143 for code + if (common.isOpenBSD) { + assert.strictEqual(err.code, 143); + sigterm = null; + } else { + assert.strictEqual(err.code, null); + } + // At least starting with Darwin Kernel Version 16.4.0, sending a SIGTERM to a + // process that is still starting up kills it with SIGKILL instead of SIGTERM. + // See: https://github.com/libuv/libuv/issues/1226 + if (common.isOSX) + assert.ok(err.signal === 'SIGTERM' || err.signal === 'SIGKILL'); + else + assert.strictEqual(err.signal, sigterm); + assert.strictEqual(err.cmd, cmd); + assert.strictEqual(stdout.trim(), ''); + assert.strictEqual(stderr.trim(), ''); +})); + +cleanupStaleProcess(__filename); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-kill.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-kill.js new file mode 100644 index 00000000000000..fd4884fc5fc8df --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-kill.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The process.argv[3] check should be argv[2], and the +// command passed to exec() should not need to include "run", "-A", +// and "require.ts". + +'use strict'; + +// Test exec() with both a timeout and a killSignal. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logInTimeout, + kExpiringChildRunTime, + kExpiringParentTimer, +} = require('../common/child_process'); + +if (process.argv[3] === 'child') { + logInTimeout(kExpiringChildRunTime); + return; +} + +const cmd = `"${process.execPath}" run -A require.ts "${__filename}" child`; + +// Test with a different kill signal. +cp.exec(cmd, { + timeout: kExpiringParentTimer, + killSignal: 'SIGKILL' +}, common.mustCall((err, stdout, stderr) => { + console.log('[stdout]', stdout.trim()); + console.log('[stderr]', stderr.trim()); + + assert.strictEqual(err.killed, true); + assert.strictEqual(err.code, null); + assert.strictEqual(err.signal, 'SIGKILL'); + assert.strictEqual(err.cmd, cmd); + assert.strictEqual(stdout.trim(), ''); + assert.strictEqual(stderr.trim(), ''); +})); + +cleanupStaleProcess(__filename); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-not-expired.js b/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-not-expired.js new file mode 100644 index 00000000000000..31fa1f7259f747 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exec-timeout-not-expired.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The process.argv[3] check should be argv[2], and the +// command passed to exec() should not need to include "run", "-A", +// and "require.ts". + +'use strict'; + +// Test exec() when a timeout is set, but not expired. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime +} = require('../common/child_process'); + +const kTimeoutNotSupposedToExpire = 2 ** 30; +const childRunTime = common.platformTimeout(100); + +// The time spent in the child should be smaller than the timeout below. +assert(childRunTime < kTimeoutNotSupposedToExpire); + +if (process.argv[3] === 'child') { + logAfterTime(childRunTime); + return; +} + +const cmd = `"${process.execPath}" run -A require.ts "${__filename}" child`; + +cp.exec(cmd, { + timeout: kTimeoutNotSupposedToExpire +}, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'child stdout'); + assert.strictEqual(stderr.trim(), 'child stderr'); +})); + +cleanupStaleProcess(__filename); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-execFile-promisified-abortController.js b/cli/tests/node_compat/test/parallel/test-child-process-execFile-promisified-abortController.js new file mode 100644 index 00000000000000..fe3e8765ea26dd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-execFile-promisified-abortController.js @@ -0,0 +1,66 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The args passed to promisified() should not need to +// include "require.ts". + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { promisify } = require('util'); +const execFile = require('child_process').execFile; +const fixtures = require('../common/fixtures'); + +const echoFixture = fixtures.path('echo.js'); +const promisified = promisify(execFile); +const invalidArgTypeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}; + +{ + // Verify that the signal option works properly + const ac = new AbortController(); + const signal = ac.signal; + const promise = promisified(process.execPath, ['require.ts', echoFixture, 0], { signal }); + + ac.abort(); + + assert.rejects( + promise, + { name: 'AbortError' } + ).then(common.mustCall()); +} + +{ + // Verify that the signal option works properly when already aborted + const signal = AbortSignal.abort(); + + assert.rejects( + promisified(process.execPath, ['require.ts', echoFixture, 0], { signal }), + { name: 'AbortError' } + ).then(common.mustCall()); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + const signal = {}; + assert.throws(() => { + promisified(process.execPath, ['require.ts', echoFixture, 0], { signal }); + }, invalidArgTypeError); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + const signal = 'world!'; + assert.throws(() => { + promisified(process.execPath, ['require.ts', echoFixture, 0], { signal }); + }, invalidArgTypeError); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-execfile-maxbuf.js b/cli/tests/node_compat/test/parallel/test-child-process-execfile-maxbuf.js new file mode 100644 index 00000000000000..ef31f4e225201b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-execfile-maxbuf.js @@ -0,0 +1,99 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { execFile } = require('child_process'); + +function checkFactory(streamName) { + return common.mustCall((err) => { + assert(err instanceof RangeError); + assert.strictEqual(err.message, `${streamName} maxBuffer length exceeded`); + assert.strictEqual(err.code, 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'); + }); +} + +// default value +{ + execFile( + process.execPath, + ['-e', 'console.log("a".repeat(1024 * 1024))'], + checkFactory('stdout') + ); +} + +// default value +{ + execFile( + process.execPath, + ['-e', 'console.log("a".repeat(1024 * 1024 - 1))'], + common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'a'.repeat(1024 * 1024 - 1)); + assert.strictEqual(stderr, ''); + }) + ); +} + +{ + const options = { maxBuffer: Infinity }; + + execFile( + process.execPath, + ['-e', 'console.log("hello world");'], + options, + common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'hello world'); + assert.strictEqual(stderr, ''); + }) + ); +} + +{ + execFile('echo', ['hello world'], { maxBuffer: 5 }, checkFactory('stdout')); +} + +const unicode = '中文测试'; // length = 4, byte length = 12 + +{ + execFile( + process.execPath, + ['-e', `console.log('${unicode}');`], + { maxBuffer: 10 }, + checkFactory('stdout')); +} + +{ + execFile( + process.execPath, + ['-e', `console.error('${unicode}');`], + { maxBuffer: 10 }, + checkFactory('stderr') + ); +} + +{ + const child = execFile( + process.execPath, + ['-e', `console.log('${unicode}');`], + { encoding: null, maxBuffer: 10 }, + checkFactory('stdout') + ); + + child.stdout.setEncoding('utf-8'); +} + +{ + const child = execFile( + process.execPath, + ['-e', `console.error('${unicode}');`], + { encoding: null, maxBuffer: 10 }, + checkFactory('stderr') + ); + + child.stderr.setEncoding('utf-8'); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-execfile.js b/cli/tests/node_compat/test/parallel/test-child-process-execfile.js new file mode 100644 index 00000000000000..9f9268407baa47 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-execfile.js @@ -0,0 +1,135 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(PolarETech): The args passed to execFile() should not need to +// include "require.ts". + +// TODO(cjihrig): See inline TODO comments below. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { execFile, execFileSync } = require('child_process'); +const { getSystemErrorName } = require('util'); +const fixtures = require('../common/fixtures'); +const os = require('os'); + +const fixture = fixtures.path('exit.js'); +const echoFixture = fixtures.path('echo.js'); +const execOpts = { encoding: 'utf8', shell: true }; + +{ + execFile( + process.execPath, + ['require.ts', fixture, 42], + common.mustCall((e) => { + // Check that arguments are included in message + assert.strictEqual(e.message.trim(), + `Command failed: ${process.execPath} require.ts ${fixture} 42`); + assert.strictEqual(e.code, 42); + }) + ); +} + +{ + // Verify that negative exit codes can be translated to UV error names. + const errorString = `Error: Command failed: ${process.execPath}`; + const code = -1; + const callback = common.mustCall((err, stdout, stderr) => { + assert.strictEqual(err.toString().trim(), errorString); + assert.strictEqual(err.code, getSystemErrorName(code)); + assert.strictEqual(err.killed, true); + assert.strictEqual(err.signal, null); + assert.strictEqual(err.cmd, process.execPath); + assert.strictEqual(stdout.trim(), ''); + assert.strictEqual(stderr.trim(), ''); + }); + const child = execFile(process.execPath, callback); + + child.kill(); + child.emit('close', code, null); +} + +{ + // Verify the shell option works properly + execFile(process.execPath, ['require.ts', fixture, 0], execOpts, common.mustSucceed()); +} + +{ + // Verify that the signal option works properly + const ac = new AbortController(); + const { signal } = ac; + + const test = () => { + const check = common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + assert.strictEqual(err.signal, undefined); + }); + execFile(process.execPath, ['require.ts', echoFixture, 0], { signal }, check); + }; + + // Verify that it still works the same way now that the signal is aborted. + test(); + ac.abort(); +} + +{ + // Verify that does not spawn a child if already aborted + const signal = AbortSignal.abort(); + + const check = common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + assert.strictEqual(err.signal, undefined); + }); + execFile(process.execPath, ['require.ts', echoFixture, 0], { signal }, check); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + assert.throws(() => { + const callback = common.mustNotCall(() => {}); + + execFile(process.execPath, ['require.ts', echoFixture, 0], { signal: 'hello' }, callback); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} +{ + // Verify that the process completing removes the abort listener + const ac = new AbortController(); + const { signal } = ac; + + const callback = common.mustCall((err) => { + // TODO(cjihrig): The assertion on the next line currently fails because + // kEvents is not defined. See TODO comment in _events.mjs. + // assert.strictEqual(getEventListeners(ac.signal).length, 0); + assert.strictEqual(err, null); + }); + execFile(process.execPath, ['require.ts', fixture, 0], { signal }, callback); +} + +// Verify the execFile() stdout is the same as execFileSync(). +{ + const file = 'echo'; + const args = ['foo', 'bar']; + + // Test with and without `{ shell: true }` + [ + // Skipping shell-less test on Windows because its echo command is a shell built-in command. + ...(common.isWindows ? [] : [{ encoding: 'utf8' }]), + { shell: true, encoding: 'utf8' }, + ].forEach((options) => { + const execFileSyncStdout = execFileSync(file, args, options); + assert.strictEqual(execFileSyncStdout, `foo bar${os.EOL}`); + + execFile(file, args, options, common.mustCall((_, stdout) => { + assert.strictEqual(stdout, execFileSyncStdout); + })); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-execfilesync-maxbuf.js b/cli/tests/node_compat/test/parallel/test-child-process-execfilesync-maxbuf.js new file mode 100644 index 00000000000000..e5f7166ea51305 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-execfilesync-maxbuf.js @@ -0,0 +1,60 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test checks that the maxBuffer option for child_process.execFileSync() +// works as expected. + +const assert = require('assert'); +const { getSystemErrorName } = require('util'); +const { execFileSync } = require('child_process'); +const msgOut = 'this is stdout'; +const msgOutBuf = Buffer.from(`${msgOut}\n`); + +const args = [ + '-e', + `console.log("${msgOut}");`, +]; + +// Verify that an error is returned if maxBuffer is surpassed. +{ + assert.throws(() => { + execFileSync(process.execPath, args, { maxBuffer: 1 }); + }, (e) => { + assert.ok(e, 'maxBuffer should error'); + assert.strictEqual(e.code, 'ENOBUFS'); + assert.strictEqual(getSystemErrorName(e.errno), 'ENOBUFS'); + // We can have buffers larger than maxBuffer because underneath we alloc 64k + // that matches our read sizes. + assert.deepStrictEqual(e.stdout, msgOutBuf); + return true; + }); +} + +// Verify that a maxBuffer size of Infinity works. +{ + const ret = execFileSync(process.execPath, args, { maxBuffer: Infinity }); + + assert.deepStrictEqual(ret, msgOutBuf); +} + +// Default maxBuffer size is 1024 * 1024. +{ + assert.throws(() => { + execFileSync( + process.execPath, + ['-e', "console.log('a'.repeat(1024 * 1024))"] + ); + }, (e) => { + assert.ok(e, 'maxBuffer should error'); + assert.strictEqual(e.code, 'ENOBUFS'); + assert.strictEqual(getSystemErrorName(e.errno), 'ENOBUFS'); + return true; + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-execsync-maxbuf.js b/cli/tests/node_compat/test/parallel/test-child-process-execsync-maxbuf.js new file mode 100644 index 00000000000000..703896ef170216 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-execsync-maxbuf.js @@ -0,0 +1,76 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(cjihrig): This should use Node's -e instead of Deno's eval CLI arg. + +'use strict'; +require('../common'); + +// This test checks that the maxBuffer option for child_process.spawnSync() +// works as expected. + +const assert = require('assert'); +const { getSystemErrorName } = require('util'); +const { execSync } = require('child_process'); +const msgOut = 'this is stdout'; +const msgOutBuf = Buffer.from(`${msgOut}\n`); + +const args = [ + 'eval', + `"console.log('${msgOut}')";`, +]; + +// Verify that an error is returned if maxBuffer is surpassed. +{ + assert.throws(() => { + execSync(`"${process.execPath}" ${args.join(' ')}`, { maxBuffer: 1 }); + }, (e) => { + assert.ok(e, 'maxBuffer should error'); + assert.strictEqual(e.code, 'ENOBUFS'); + assert.strictEqual(getSystemErrorName(e.errno), 'ENOBUFS'); + // We can have buffers larger than maxBuffer because underneath we alloc 64k + // that matches our read sizes. + assert.deepStrictEqual(e.stdout, msgOutBuf); + return true; + }); +} + +// Verify that a maxBuffer size of Infinity works. +{ + const ret = execSync( + `"${process.execPath}" ${args.join(' ')}`, + { maxBuffer: Infinity } + ); + + assert.deepStrictEqual(ret, msgOutBuf); +} + +// Default maxBuffer size is 1024 * 1024. +{ + assert.throws(() => { + execSync( + `"${process.execPath}" eval "console.log('a'.repeat(1024 * 1024))"` + ); + }, (e) => { + assert.ok(e, 'maxBuffer should error'); + assert.strictEqual(e.code, 'ENOBUFS'); + assert.strictEqual(getSystemErrorName(e.errno), 'ENOBUFS'); + return true; + }); +} + +// Default maxBuffer size is 1024 * 1024. +{ + const ret = execSync( + `"${process.execPath}" eval "console.log('a'.repeat(1024 * 1024 - 1))"` + ); + + assert.deepStrictEqual( + ret.toString().trim(), + 'a'.repeat(1024 * 1024 - 1) + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-exit-code.js b/cli/tests/node_compat/test/parallel/test-child-process-exit-code.js new file mode 100644 index 00000000000000..caa57986b42bc6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-exit-code.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The args passed to spawn() should not need to +// include "require.ts". + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const exitScript = fixtures.path('exit.js'); +const exitChild = spawn(process.argv[0], ['require.ts', exitScript, 23]); +exitChild.on('exit', common.mustCall(function(code, signal) { + assert.strictEqual(code, 23); + assert.strictEqual(signal, null); +})); + + +const errorScript = fixtures.path('child_process_should_emit_error.js'); +const errorChild = spawn(process.argv[0], ['require.ts', errorScript]); +errorChild.on('exit', common.mustCall(function(code, signal) { + assert.ok(code !== 0); + assert.strictEqual(signal, null); +})); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-flush-stdio.js b/cli/tests/node_compat/test/parallel/test-child-process-flush-stdio.js new file mode 100644 index 00000000000000..72357bcdd42f6a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-flush-stdio.js @@ -0,0 +1,40 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const cp = require('child_process'); +const assert = require('assert'); + +// Windows' `echo` command is a built-in shell command and not an external +// executable like on *nix +const opts = { shell: common.isWindows }; + +const p = cp.spawn('echo', [], opts); + +p.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + spawnWithReadable(); +})); + +p.stdout.read(); + +const spawnWithReadable = () => { + const buffer = []; + const p = cp.spawn('echo', ['123'], opts); + p.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + assert.strictEqual(Buffer.concat(buffer).toString().trim(), '123'); + })); + p.stdout.on('readable', () => { + let buf; + while ((buf = p.stdout.read()) !== null) + buffer.push(buf); + }); +}; diff --git a/cli/tests/node_compat/test/parallel/test-child-process-ipc.js b/cli/tests/node_compat/test/parallel/test-child-process-ipc.js new file mode 100644 index 00000000000000..c1d7bc2b618414 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-ipc.js @@ -0,0 +1,73 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The args passed to spawn() should not need to +// include "require.ts". + +'use strict'; + +const { + mustCall, + mustNotCall, +} = require('../common'); +const assert = require('assert'); +const debug = require('util').debuglog('test'); + +const { spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); + +const sub = fixtures.path('echo.js'); + +const child = spawn(process.argv[0], ['require.ts', sub]); + +child.stderr.on('data', mustNotCall()); + +child.stdout.setEncoding('utf8'); + +const messages = [ + 'hello world\r\n', + 'echo me\r\n', +]; + +child.stdout.on('data', mustCall((data) => { + debug(`child said: ${JSON.stringify(data)}`); + const test = messages.shift(); + debug(`testing for '${test}'`); + assert.strictEqual(data, test); + if (messages.length) { + debug(`writing '${messages[0]}'`); + child.stdin.write(messages[0]); + } else { + assert.strictEqual(messages.length, 0); + child.stdin.end(); + } +}, messages.length)); + +child.stdout.on('end', mustCall((data) => { + debug('child end'); +})); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-kill.js b/cli/tests/node_compat/test/parallel/test-child-process-kill.js new file mode 100644 index 00000000000000..6d5c4cba1d6462 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-kill.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const cat = spawn(common.isWindows ? 'cmd' : 'cat'); + +cat.stdout.on('end', common.mustCall()); +cat.stderr.on('data', common.mustNotCall()); +cat.stderr.on('end', common.mustCall()); + +cat.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + assert.strictEqual(cat.signalCode, 'SIGTERM'); +})); + +assert.strictEqual(cat.signalCode, null); +assert.strictEqual(cat.killed, false); +cat.kill(); +assert.strictEqual(cat.killed, true); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-set-blocking.js b/cli/tests/node_compat/test/parallel/test-child-process-set-blocking.js new file mode 100644 index 00000000000000..0c771bad8fb97c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-set-blocking.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const ch = require('child_process'); + +const SIZE = 100000; +const python = process.env.PYTHON || (common.isWindows ? 'python' : 'python3'); + +const cp = ch.spawn(python, ['-c', `print(${SIZE} * "C")`], { + stdio: 'inherit' +}); + +cp.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); +})); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-spawn-args.js b/cli/tests/node_compat/test/parallel/test-child-process-spawn-args.js new file mode 100644 index 00000000000000..1e3378935fe4ad --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-spawn-args.js @@ -0,0 +1,62 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This test confirms that `undefined`, `null`, and `[]` +// can be used as a placeholder for the second argument (`args`) of `spawn()`. +// Previously, there was a bug where using `undefined` for the second argument +// caused the third argument (`options`) to be ignored. +// See https://github.com/nodejs/node/issues/24912. + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +const assert = require('assert'); +const { spawn } = require('child_process'); + +tmpdir.refresh(); + +const command = common.isWindows ? 'cd' : 'pwd'; +const options = { cwd: tmpdir.path }; + +if (common.isWindows) { + // This test is not the case for Windows based systems + // unless the `shell` options equals to `true` + + options.shell = true; +} + +const testCases = [ + undefined, + null, + [], +]; + +const expectedResult = tmpdir.path.trim().toLowerCase(); + +(async () => { + const results = await Promise.all( + testCases.map((testCase) => { + return new Promise((resolve) => { + const subprocess = spawn(command, testCase, options); + + let accumulatedData = Buffer.alloc(0); + + subprocess.stdout.on('data', common.mustCall((data) => { + accumulatedData = Buffer.concat([accumulatedData, data]); + })); + + subprocess.stdout.on('end', () => { + resolve(accumulatedData.toString().trim().toLowerCase()); + }); + }); + }) + ); + + assert.deepStrictEqual([...new Set(results)], [expectedResult]); +})().then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-spawn-event.js b/cli/tests/node_compat/test/parallel/test-child-process-spawn-event.js new file mode 100644 index 00000000000000..62252f829ff2e9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-spawn-event.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const spawn = require('child_process').spawn; +const assert = require('assert'); + +const subprocess = spawn('echo', ['ok']); + +let didSpawn = false; +subprocess.on('spawn', function() { + didSpawn = true; +}); +function mustCallAfterSpawn() { + return common.mustCall(function() { + assert.ok(didSpawn); + }); +} + +subprocess.on('error', common.mustNotCall()); +subprocess.on('spawn', common.mustCall()); +subprocess.stdout.on('data', mustCallAfterSpawn()); +subprocess.stdout.on('end', mustCallAfterSpawn()); +subprocess.stdout.on('close', mustCallAfterSpawn()); +subprocess.stderr.on('data', common.mustNotCall()); +subprocess.stderr.on('end', mustCallAfterSpawn()); +subprocess.stderr.on('close', mustCallAfterSpawn()); +subprocess.on('exit', mustCallAfterSpawn()); +subprocess.on('close', mustCallAfterSpawn()); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-args.js b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-args.js new file mode 100644 index 00000000000000..9bca413b09cb0e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-args.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This test confirms that `undefined`, `null`, and `[]` can be used +// as a placeholder for the second argument (`args`) of `spawnSync()`. +// Previously, there was a bug where using `undefined` for the second argument +// caused the third argument (`options`) to be ignored. +// See https://github.com/nodejs/node/issues/24912. + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const command = common.isWindows ? 'cd' : 'pwd'; +const options = { cwd: tmpdir.path }; + +tmpdir.refresh(); + +if (common.isWindows) { + // This test is not the case for Windows based systems + // unless the `shell` options equals to `true` + + options.shell = true; +} + +const testCases = [ + undefined, + null, + [], +]; + +const expectedResult = tmpdir.path.trim().toLowerCase(); + +const results = testCases.map((testCase) => { + const { stdout, stderr, error } = spawnSync( + command, + testCase, + options + ); + + assert.ifError(error); + assert.deepStrictEqual(stderr, Buffer.alloc(0)); + + return stdout.toString().trim().toLowerCase(); +}); + +assert.deepStrictEqual([...new Set(results)], [expectedResult]); diff --git a/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-env.js b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-env.js new file mode 100644 index 00000000000000..d08ed48d95f4af --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-env.js @@ -0,0 +1,47 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(cjihrig): The process.argv[3] check should be argv[2], and the +// arguments array passed to spawnSync() should not need to include +// "require.ts". + +'use strict'; +require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +if (process.argv[3] === 'child') { + console.log(process.env.foo); +} else { + const expected = 'bar'; + const child = cp.spawnSync(process.execPath, ["require.ts", __filename, 'child'], { + env: Object.assign(process.env, { foo: expected }) + }); + + assert.strictEqual(child.stdout.toString().trim(), expected); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-maxbuf.js b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-maxbuf.js new file mode 100644 index 00000000000000..5d18f127a69ddd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-maxbuf.js @@ -0,0 +1,65 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test checks that the maxBuffer option for child_process.spawnSync() +// works as expected. + +const assert = require('assert'); +const spawnSync = require('child_process').spawnSync; +const { getSystemErrorName } = require('util'); +const msgOut = 'this is stdout'; +const msgOutBuf = Buffer.from(`${msgOut}\n`); + +const args = [ + '-e', + `console.log("${msgOut}");`, +]; + +// Verify that an error is returned if maxBuffer is surpassed. +{ + const ret = spawnSync(process.execPath, args, { maxBuffer: 1 }); + + assert.ok(ret.error, 'maxBuffer should error'); + assert.strictEqual(ret.error.code, 'ENOBUFS'); + assert.strictEqual(getSystemErrorName(ret.error.errno), 'ENOBUFS'); + // We can have buffers larger than maxBuffer because underneath we alloc 64k + // that matches our read sizes. + assert.deepStrictEqual(ret.stdout, msgOutBuf); +} + +// Verify that a maxBuffer size of Infinity works. +{ + const ret = spawnSync(process.execPath, args, { maxBuffer: Infinity }); + + assert.ifError(ret.error); + assert.deepStrictEqual(ret.stdout, msgOutBuf); +} + +// Default maxBuffer size is 1024 * 1024. +{ + const args = ['-e', "console.log('a'.repeat(1024 * 1024))"]; + const ret = spawnSync(process.execPath, args); + + assert.ok(ret.error, 'maxBuffer should error'); + assert.strictEqual(ret.error.code, 'ENOBUFS'); + assert.strictEqual(getSystemErrorName(ret.error.errno), 'ENOBUFS'); +} + +// Default maxBuffer size is 1024 * 1024. +{ + const args = ['-e', "console.log('a'.repeat(1024 * 1024 - 1))"]; + const ret = spawnSync(process.execPath, args); + + assert.ifError(ret.error); + assert.deepStrictEqual( + ret.stdout.toString().trim(), + 'a'.repeat(1024 * 1024 - 1) + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-validation-errors.js b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-validation-errors.js new file mode 100644 index 00000000000000..b2b96e3c7af2c1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync-validation-errors.js @@ -0,0 +1,223 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawnSync = require('child_process').spawnSync; +const signals = require('os').constants.signals; +const rootUser = common.isWindows ? false : + common.isIBMi ? true : process.getuid() === 0; + +const invalidArgTypeError = { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }; +const invalidRangeError = { code: 'ERR_OUT_OF_RANGE', name: 'RangeError' }; + +function pass(option, value) { + // Run the command with the specified option. Since it's not a real command, + // spawnSync() should run successfully but return an ENOENT error. + const child = spawnSync('not_a_real_command', { [option]: value }); + + assert.strictEqual(child.error.code, 'ENOENT'); +} + +function fail(option, value, message) { + assert.throws(() => { + spawnSync('not_a_real_command', { [option]: value }); + }, message); +} + +{ + // Validate the cwd option + pass('cwd', undefined); + pass('cwd', null); + pass('cwd', __dirname); + fail('cwd', 0, invalidArgTypeError); + fail('cwd', 1, invalidArgTypeError); + fail('cwd', true, invalidArgTypeError); + fail('cwd', false, invalidArgTypeError); + fail('cwd', [], invalidArgTypeError); + fail('cwd', {}, invalidArgTypeError); + fail('cwd', common.mustNotCall(), invalidArgTypeError); +} + +{ + // Validate the detached option + pass('detached', undefined); + pass('detached', null); + pass('detached', true); + pass('detached', false); + fail('detached', 0, invalidArgTypeError); + fail('detached', 1, invalidArgTypeError); + fail('detached', __dirname, invalidArgTypeError); + fail('detached', [], invalidArgTypeError); + fail('detached', {}, invalidArgTypeError); + fail('detached', common.mustNotCall(), invalidArgTypeError); +} + +if (!common.isWindows) { + { + // Validate the uid option + if (!rootUser) { + pass('uid', undefined); + pass('uid', null); + pass('uid', process.getuid()); + fail('uid', __dirname, invalidArgTypeError); + fail('uid', true, invalidArgTypeError); + fail('uid', false, invalidArgTypeError); + fail('uid', [], invalidArgTypeError); + fail('uid', {}, invalidArgTypeError); + fail('uid', common.mustNotCall(), invalidArgTypeError); + fail('uid', NaN, invalidArgTypeError); + fail('uid', Infinity, invalidArgTypeError); + fail('uid', 3.1, invalidArgTypeError); + fail('uid', -3.1, invalidArgTypeError); + } + } + + { + // Validate the gid option + if (process.getgid() !== 0) { + pass('gid', undefined); + pass('gid', null); + pass('gid', process.getgid()); + fail('gid', __dirname, invalidArgTypeError); + fail('gid', true, invalidArgTypeError); + fail('gid', false, invalidArgTypeError); + fail('gid', [], invalidArgTypeError); + fail('gid', {}, invalidArgTypeError); + fail('gid', common.mustNotCall(), invalidArgTypeError); + fail('gid', NaN, invalidArgTypeError); + fail('gid', Infinity, invalidArgTypeError); + fail('gid', 3.1, invalidArgTypeError); + fail('gid', -3.1, invalidArgTypeError); + } + } +} + +{ + // Validate the shell option + pass('shell', undefined); + pass('shell', null); + pass('shell', false); + fail('shell', 0, invalidArgTypeError); + fail('shell', 1, invalidArgTypeError); + fail('shell', [], invalidArgTypeError); + fail('shell', {}, invalidArgTypeError); + fail('shell', common.mustNotCall(), invalidArgTypeError); +} + +{ + // Validate the argv0 option + pass('argv0', undefined); + pass('argv0', null); + pass('argv0', 'myArgv0'); + fail('argv0', 0, invalidArgTypeError); + fail('argv0', 1, invalidArgTypeError); + fail('argv0', true, invalidArgTypeError); + fail('argv0', false, invalidArgTypeError); + fail('argv0', [], invalidArgTypeError); + fail('argv0', {}, invalidArgTypeError); + fail('argv0', common.mustNotCall(), invalidArgTypeError); +} + +{ + // Validate the windowsHide option + pass('windowsHide', undefined); + pass('windowsHide', null); + pass('windowsHide', true); + pass('windowsHide', false); + fail('windowsHide', 0, invalidArgTypeError); + fail('windowsHide', 1, invalidArgTypeError); + fail('windowsHide', __dirname, invalidArgTypeError); + fail('windowsHide', [], invalidArgTypeError); + fail('windowsHide', {}, invalidArgTypeError); + fail('windowsHide', common.mustNotCall(), invalidArgTypeError); +} + +{ + // Validate the windowsVerbatimArguments option + pass('windowsVerbatimArguments', undefined); + pass('windowsVerbatimArguments', null); + pass('windowsVerbatimArguments', true); + pass('windowsVerbatimArguments', false); + fail('windowsVerbatimArguments', 0, invalidArgTypeError); + fail('windowsVerbatimArguments', 1, invalidArgTypeError); + fail('windowsVerbatimArguments', __dirname, invalidArgTypeError); + fail('windowsVerbatimArguments', [], invalidArgTypeError); + fail('windowsVerbatimArguments', {}, invalidArgTypeError); + fail('windowsVerbatimArguments', common.mustNotCall(), invalidArgTypeError); +} + +{ + // Validate the timeout option + pass('timeout', undefined); + pass('timeout', null); + pass('timeout', 1); + pass('timeout', 0); + fail('timeout', -1, invalidRangeError); + fail('timeout', true, invalidRangeError); + fail('timeout', false, invalidRangeError); + fail('timeout', __dirname, invalidRangeError); + fail('timeout', [], invalidRangeError); + fail('timeout', {}, invalidRangeError); + fail('timeout', common.mustNotCall(), invalidRangeError); + fail('timeout', NaN, invalidRangeError); + fail('timeout', Infinity, invalidRangeError); + fail('timeout', 3.1, invalidRangeError); + fail('timeout', -3.1, invalidRangeError); +} + +{ + // Validate the maxBuffer option + pass('maxBuffer', undefined); + pass('maxBuffer', null); + pass('maxBuffer', 1); + pass('maxBuffer', 0); + pass('maxBuffer', Infinity); + pass('maxBuffer', 3.14); + fail('maxBuffer', -1, invalidRangeError); + fail('maxBuffer', NaN, invalidRangeError); + fail('maxBuffer', -Infinity, invalidRangeError); + fail('maxBuffer', true, invalidRangeError); + fail('maxBuffer', false, invalidRangeError); + fail('maxBuffer', __dirname, invalidRangeError); + fail('maxBuffer', [], invalidRangeError); + fail('maxBuffer', {}, invalidRangeError); + fail('maxBuffer', common.mustNotCall(), invalidRangeError); +} + +{ + // Validate the killSignal option + const unknownSignalErr = { code: 'ERR_UNKNOWN_SIGNAL', name: 'TypeError' }; + + pass('killSignal', undefined); + pass('killSignal', null); + pass('killSignal', 'SIGKILL'); + fail('killSignal', 'SIGNOTAVALIDSIGNALNAME', unknownSignalErr); + fail('killSignal', true, invalidArgTypeError); + fail('killSignal', false, invalidArgTypeError); + fail('killSignal', [], invalidArgTypeError); + fail('killSignal', {}, invalidArgTypeError); + fail('killSignal', common.mustNotCall(), invalidArgTypeError); + + // Invalid signal names and numbers should fail + fail('killSignal', 500, unknownSignalErr); + fail('killSignal', 0, unknownSignalErr); + fail('killSignal', -200, unknownSignalErr); + fail('killSignal', 3.14, unknownSignalErr); + + Object.getOwnPropertyNames(Object.prototype).forEach((property) => { + fail('killSignal', property, unknownSignalErr); + }); + + // Valid signal names and numbers should pass + for (const signalName in signals) { + pass('killSignal', signals[signalName]); + pass('killSignal', signalName); + pass('killSignal', signalName.toLowerCase()); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-spawnsync.js b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync.js new file mode 100644 index 00000000000000..893e3b4e130124 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-spawnsync.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const { getSystemErrorName } = require('util'); + +// `sleep` does different things on Windows and Unix, but in both cases, it does +// more-or-less nothing if there are no parameters +const ret = spawnSync('sleep', ['0']); +assert.strictEqual(ret.status, 0); + +// Error test when command does not exist +const ret_err = spawnSync('command_does_not_exist', ['bar']).error; + +assert.strictEqual(ret_err.code, 'ENOENT'); +assert.strictEqual(getSystemErrorName(ret_err.errno), 'ENOENT'); +assert.strictEqual(ret_err.syscall, 'spawnSync command_does_not_exist'); +assert.strictEqual(ret_err.path, 'command_does_not_exist'); +assert.deepStrictEqual(ret_err.spawnargs, ['bar']); + +{ + // Test the cwd option + const cwd = tmpdir.path; + const response = spawnSync(...common.pwdCommand, { cwd }); + + assert.strictEqual(response.stdout.toString().trim(), cwd); +} + + +{ + // Assert Buffer is the default encoding + const retDefault = spawnSync(...common.pwdCommand); + const retBuffer = spawnSync(...common.pwdCommand, { encoding: 'buffer' }); + assert.deepStrictEqual(retDefault.output, retBuffer.output); + + const retUTF8 = spawnSync(...common.pwdCommand, { encoding: 'utf8' }); + const stringifiedDefault = [ + null, + retDefault.stdout.toString(), + retDefault.stderr.toString(), + ]; + assert.deepStrictEqual(retUTF8.output, stringifiedDefault); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-stdio-inherit.js b/cli/tests/node_compat/test/parallel/test-child-process-stdio-inherit.js new file mode 100644 index 00000000000000..e213dd6b800928 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-stdio-inherit.js @@ -0,0 +1,66 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The process.argv[3] check should be argv[2], and +// the args passed to spawn() should not need to include "require.ts". + +'use strict'; +require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[3] === 'parent') + parent(); +else + grandparent(); + +function grandparent() { + const child = spawn(process.execPath, ['require.ts', __filename, 'parent']); + child.stderr.pipe(process.stderr); + let output = ''; + const input = 'asdfasdf'; + + child.stdout.on('data', function(chunk) { + output += chunk; + }); + child.stdout.setEncoding('utf8'); + + child.stdin.end(input); + + child.on('close', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + // 'cat' on windows adds a \r\n at the end. + assert.strictEqual(output.trim(), input.trim()); + }); +} + +function parent() { + // Should not immediately exit. + spawn('cat', [], { stdio: 'inherit' }); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-stdout-flush-exit.js b/cli/tests/node_compat/test/parallel/test-child-process-stdout-flush-exit.js new file mode 100644 index 00000000000000..585cc6084062db --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-stdout-flush-exit.js @@ -0,0 +1,67 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The process.argv[3] check should be argv[2], +// the args passed to spawn() should not need to include "require.ts", +// and the process.argv[2] passed to spawn() should be argv[1]. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// If child process output to console and exit +// The console.log statements here are part of the test. +if (process.argv[3] === 'child') { + console.log('hello'); + for (let i = 0; i < 200; i++) { + console.log('filler'); + } + console.log('goodbye'); + process.exit(0); +} else { + // parent process + const spawn = require('child_process').spawn; + + // spawn self as child + const child = spawn(process.argv[0], ['require.ts', process.argv[2], 'child']); + + let stdout = ''; + + child.stderr.on('data', common.mustNotCall()); + + // Check if we receive both 'hello' at start and 'goodbye' at end + child.stdout.setEncoding('utf8'); + child.stdout.on('data', common.mustCallAtLeast((data) => { + stdout += data; + })); + + child.on('close', common.mustCall(() => { + assert.strictEqual(stdout.slice(0, 6), 'hello\n'); + assert.strictEqual(stdout.slice(stdout.length - 8), 'goodbye\n'); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-child-process-stdout-flush.js b/cli/tests/node_compat/test/parallel/test-child-process-stdout-flush.js new file mode 100644 index 00000000000000..4054d2189202cd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-child-process-stdout-flush.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The args passed to spawn() should not need to +// include "require.ts". + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const sub = fixtures.path('print-chars.js'); + +const n = 500000; + +const child = spawn(process.argv[0], ['require.ts', sub, n]); + +let count = 0; + +child.stderr.setEncoding('utf8'); +child.stderr.on('data', common.mustNotCall()); + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + count += data.length; +}); + +child.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + assert.strictEqual(n, count); +})); diff --git a/cli/tests/node_compat/test/parallel/test-client-request-destroy.js b/cli/tests/node_compat/test/parallel/test-client-request-destroy.js new file mode 100644 index 00000000000000..f7e11ae0bf47c8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-client-request-destroy.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Test that http.ClientRequest,prototype.destroy() returns `this`. +require('../common'); + +const assert = require('assert'); +const http = require('http'); +const clientRequest = new http.ClientRequest({ createConnection: () => {} }); + +assert.strictEqual(clientRequest.destroyed, false); +assert.strictEqual(clientRequest.destroy(), clientRequest); +assert.strictEqual(clientRequest.destroyed, true); +assert.strictEqual(clientRequest.destroy(), clientRequest); diff --git a/cli/tests/node_compat/test/parallel/test-console-async-write-error.js b/cli/tests/node_compat/test/parallel/test-console-async-write-error.js new file mode 100644 index 00000000000000..9816ced4f58ac3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-async-write-error.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + process.nextTick(callback, new Error('foobar')); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. +} diff --git a/cli/tests/node_compat/test/parallel/test-console-group.js b/cli/tests/node_compat/test/parallel/test-console-group.js new file mode 100644 index 00000000000000..257317214ec83f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-group.js @@ -0,0 +1,248 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const { + hijackStdout, + hijackStderr, + restoreStdout, + restoreStderr +} = require('../common/hijackstdio'); + +const assert = require('assert'); +const Console = require('console').Console; + +let c, stdout, stderr; + +function setup(groupIndentation) { + stdout = ''; + hijackStdout(function(data) { + stdout += data; + }); + + stderr = ''; + hijackStderr(function(data) { + stderr += data; + }); + + c = new Console({ stdout: process.stdout, + stderr: process.stderr, + colorMode: false, + groupIndentation: groupIndentation }); +} + +function teardown() { + restoreStdout(); + restoreStderr(); +} + +// Basic group() functionality +{ + setup(); + const expectedOut = 'This is the outer level\n' + + ' Level 2\n' + + ' Level 3\n' + + ' Back to level 2\n' + + 'Back to the outer level\n' + + 'Still at the outer level\n'; + + + const expectedErr = ' More of level 3\n'; + + c.log('This is the outer level'); + c.group(); + c.log('Level 2'); + c.group(); + c.log('Level 3'); + c.warn('More of level 3'); + c.groupEnd(); + c.log('Back to level 2'); + c.groupEnd(); + c.log('Back to the outer level'); + c.groupEnd(); + c.log('Still at the outer level'); + + assert.strictEqual(stdout, expectedOut); + assert.strictEqual(stderr, expectedErr); + teardown(); +} + +// Group indentation is tracked per Console instance. +{ + setup(); + const expectedOut = 'No indentation\n' + + 'None here either\n' + + ' Now the first console is indenting\n' + + 'But the second one does not\n'; + const expectedErr = ''; + + const c2 = new Console(process.stdout, process.stderr); + c.log('No indentation'); + c2.log('None here either'); + c.group(); + c.log('Now the first console is indenting'); + c2.log('But the second one does not'); + + assert.strictEqual(stdout, expectedOut); + assert.strictEqual(stderr, expectedErr); + teardown(); +} + +// Make sure labels work. +{ + setup(); + const expectedOut = 'This is a label\n' + + ' And this is the data for that label\n'; + const expectedErr = ''; + + c.group('This is a label'); + c.log('And this is the data for that label'); + + assert.strictEqual(stdout, expectedOut); + assert.strictEqual(stderr, expectedErr); + teardown(); +} + +// Check that console.groupCollapsed() is an alias of console.group() +{ + setup(); + const expectedOut = 'Label\n' + + ' Level 2\n' + + ' Level 3\n'; + const expectedErr = ''; + + c.groupCollapsed('Label'); + c.log('Level 2'); + c.groupCollapsed(); + c.log('Level 3'); + + assert.strictEqual(stdout, expectedOut); + assert.strictEqual(stderr, expectedErr); + teardown(); +} + +// Check that multiline strings and object output are indented properly. +{ + setup(); + const expectedOut = 'not indented\n' + + ' indented\n' + + ' also indented\n' + + ' {\n' + + " also: 'a',\n" + + " multiline: 'object',\n" + + " should: 'be',\n" + + " indented: 'properly',\n" + + " kthx: 'bai'\n" + + ' }\n'; + const expectedErr = ''; + + c.log('not indented'); + c.group(); + c.log('indented\nalso indented'); + c.log({ also: 'a', + multiline: 'object', + should: 'be', + indented: 'properly', + kthx: 'bai' }); + + assert.strictEqual(stdout, expectedOut); + assert.strictEqual(stderr, expectedErr); + teardown(); +} + +// Check that the kGroupIndent symbol property is not enumerable +{ + const keys = Reflect.ownKeys(console) + .filter((val) => Object.prototype.propertyIsEnumerable.call(console, val)) + .map((val) => val.toString()); + assert(!keys.includes('Symbol(groupIndent)'), + 'groupIndent should not be enumerable'); +} + +// Check custom groupIndentation. +{ + setup(3); + const expectedOut = 'Set the groupIndentation parameter to 3\n' + + 'This is the outer level\n' + + ' Level 2\n' + + ' Level 3\n' + + ' Back to level 2\n' + + 'Back to the outer level\n' + + 'Still at the outer level\n'; + + + const expectedErr = ' More of level 3\n'; + + c.log('Set the groupIndentation parameter to 3'); + c.log('This is the outer level'); + c.group(); + c.log('Level 2'); + c.group(); + c.log('Level 3'); + c.warn('More of level 3'); + c.groupEnd(); + c.log('Back to level 2'); + c.groupEnd(); + c.log('Back to the outer level'); + c.groupEnd(); + c.log('Still at the outer level'); + + assert.strictEqual(stdout, expectedOut); + assert.strictEqual(stderr, expectedErr); + teardown(); +} + +// Check the correctness of the groupIndentation parameter. +{ + // TypeError + [null, 'str', [], false, true, {}].forEach((e) => { + assert.throws( + () => { + new Console({ stdout: process.stdout, + stderr: process.stderr, + groupIndentation: e }); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + }); + + // RangeError for integer + [NaN, 1.01].forEach((e) => { + assert.throws( + () => { + new Console({ stdout: process.stdout, + stderr: process.stderr, + groupIndentation: e }); + }, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: /an integer/, + } + ); + }); + + // RangeError + [-1, 1001].forEach((e) => { + assert.throws( + () => { + new Console({ stdout: process.stdout, + stderr: process.stderr, + groupIndentation: e }); + }, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: />= 0 && <= 1000/, + } + ); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-console-instance.js b/cli/tests/node_compat/test/parallel/test-console-instance.js new file mode 100644 index 00000000000000..ee561564fb8bca --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-instance.js @@ -0,0 +1,156 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Stream = require('stream'); +const requiredConsole = require('console'); +const Console = requiredConsole.Console; + +const out = new Stream(); +const err = new Stream(); + +// Ensure the Console instance doesn't write to the +// process' "stdout" or "stderr" streams. +process.stdout.write = process.stderr.write = common.mustNotCall(); + +// Make sure that the "Console" function exists. +assert.strictEqual(typeof Console, 'function'); + +// Note: We don't replace global console with node.js console +// assert.strictEqual(requiredConsole, global.console); +// Make sure the custom instanceof of Console works +assert.ok(global.console instanceof Console); +assert.ok(!({} instanceof Console)); + +// Make sure that the Console constructor throws +// when not given a writable stream instance. +assert.throws( + () => { new Console(); }, + { + code: 'ERR_CONSOLE_WRITABLE_STREAM', + name: 'TypeError', + message: /stdout/ + } +); + +// Console constructor should throw if stderr exists but is not writable. +assert.throws( + () => { + out.write = () => {}; + err.write = undefined; + new Console(out, err); + }, + { + code: 'ERR_CONSOLE_WRITABLE_STREAM', + name: 'TypeError', + message: /stderr/ + } +); + +out.write = err.write = (d) => {}; + +{ + const c = new Console(out, err); + assert.ok(c instanceof Console); + + out.write = err.write = common.mustCall((d) => { + assert.strictEqual(d, 'test\n'); + }, 2); + + c.log('test'); + c.error('test'); + + out.write = common.mustCall((d) => { + assert.strictEqual(d, '{ foo: 1 }\n'); + }); + + c.dir({ foo: 1 }); + + // Ensure that the console functions are bound to the console instance. + let called = 0; + out.write = common.mustCall((d) => { + called++; + assert.strictEqual(d, `${called} ${called - 1} [ 1, 2, 3 ]\n`); + }, 3); + + [1, 2, 3].forEach(c.log); +} + +// Test calling Console without the `new` keyword. +{ + const withoutNew = Console(out, err); + assert.ok(withoutNew instanceof Console); +} + +// Test extending Console +{ + class MyConsole extends Console { + hello() {} + // See if the methods on Console.prototype are overridable. + log() { return 'overridden'; } + } + const myConsole = new MyConsole(process.stdout); + assert.strictEqual(typeof myConsole.hello, 'function'); + assert.ok(myConsole instanceof Console); + assert.strictEqual(myConsole.log(), 'overridden'); + + const log = myConsole.log; + assert.strictEqual(log(), 'overridden'); +} + +// Instance that does not ignore the stream errors. +{ + const c2 = new Console(out, err, false); + + out.write = () => { throw new Error('out'); }; + err.write = () => { throw new Error('err'); }; + + assert.throws(() => c2.log('foo'), /^Error: out$/); + assert.throws(() => c2.warn('foo'), /^Error: err$/); + assert.throws(() => c2.dir('foo'), /^Error: out$/); +} + +// Console constructor throws if inspectOptions is not an object. +[null, true, false, 'foo', 5, Symbol()].forEach((inspectOptions) => { + assert.throws( + () => { + new Console({ + stdout: out, + stderr: err, + inspectOptions + }); + }, + { + message: 'The "options.inspectOptions" property must be of type object.' + + common.invalidArgTypeHelper(inspectOptions), + code: 'ERR_INVALID_ARG_TYPE' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-console-log-stdio-broken-dest.js b/cli/tests/node_compat/test/parallel/test-console-log-stdio-broken-dest.js new file mode 100644 index 00000000000000..afe6f815f0a7b8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-log-stdio-broken-dest.js @@ -0,0 +1,31 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); +const { Console } = require('console'); +const { EventEmitter } = require('events'); + +const stream = new Writable({ + write(chunk, enc, cb) { + cb(); + }, + writev(chunks, cb) { + setTimeout(cb, 10, new Error('kaboom')); + } +}); +const myConsole = new Console(stream, stream); + +process.on('warning', common.mustNotCall()); + +stream.cork(); +for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { + myConsole.log('a message'); +} +stream.uncork(); diff --git a/cli/tests/node_compat/test/parallel/test-console-log-throw-primitive.js b/cli/tests/node_compat/test/parallel/test-console-log-throw-primitive.js new file mode 100644 index 00000000000000..9be7f963f3d506 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-log-throw-primitive.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const { Writable } = require('stream'); +const { Console } = require('console'); + +const stream = new Writable({ + write() { + throw null; // eslint-disable-line no-throw-literal + } +}); + +const console = new Console({ stdout: stream }); + +console.log('test'); // Should not throw diff --git a/cli/tests/node_compat/test/parallel/test-console-no-swallow-stack-overflow.js b/cli/tests/node_compat/test/parallel/test-console-no-swallow-stack-overflow.js new file mode 100644 index 00000000000000..81235e3c952981 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-no-swallow-stack-overflow.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + assert.throws(() => { + const out = new Writable({ + write: common.mustCall(function write(...args) { + // Exceeds call stack. + return write(...args); + }), + }); + const c = new Console(out, out, true); + + c[method]('Hello, world!'); + }, { name: 'RangeError' }); +} diff --git a/cli/tests/node_compat/test/parallel/test-console-sync-write-error.js b/cli/tests/node_compat/test/parallel/test-console-sync-write-error.js new file mode 100644 index 00000000000000..0eb34e6acf3d9e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-sync-write-error.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + callback(new Error('foobar')); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } + + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + throw new Error('foobar'); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } + + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + setImmediate(() => callback(new Error('foobar'))); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } +} diff --git a/cli/tests/node_compat/test/parallel/test-console-table.js b/cli/tests/node_compat/test/parallel/test-console-table.js new file mode 100644 index 00000000000000..3aa34d7c117d14 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-table.js @@ -0,0 +1,300 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +const assert = require('assert'); +const { Console } = require('console'); + +const queue = []; + +const console = new Console({ write: (x) => { + queue.push(x); +}, removeListener: () => {} }, process.stderr, false); + +function test(data, only, expected) { + if (arguments.length === 2) { + expected = only; + only = undefined; + } + console.table(data, only); + assert.deepStrictEqual( + queue.shift().split('\n'), + expected.trimLeft().split('\n') + ); +} + +assert.throws(() => console.table([], false), { + code: 'ERR_INVALID_ARG_TYPE', +}); + +test(null, 'null\n'); +test(undefined, 'undefined\n'); +test(false, 'false\n'); +test('hi', 'hi\n'); +test(Symbol(), 'Symbol()\n'); +test(function() {}, '[Function (anonymous)]\n'); + +test([1, 2, 3], ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└─────────┴────────┘ +`); + +test([Symbol(), 5, [10]], ` +┌─────────┬────┬──────────┐ +│ (index) │ 0 │ Values │ +├─────────┼────┼──────────┤ +│ 0 │ │ Symbol() │ +│ 1 │ │ 5 │ +│ 2 │ 10 │ │ +└─────────┴────┴──────────┘ +`); + +test([null, 5], ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ 0 │ null │ +│ 1 │ 5 │ +└─────────┴────────┘ +`); + +test([undefined, 5], ` +┌─────────┬───────────┐ +│ (index) │ Values │ +├─────────┼───────────┤ +│ 0 │ undefined │ +│ 1 │ 5 │ +└─────────┴───────────┘ +`); + +test({ a: 1, b: Symbol(), c: [10] }, ` +┌─────────┬────┬──────────┐ +│ (index) │ 0 │ Values │ +├─────────┼────┼──────────┤ +│ a │ │ 1 │ +│ b │ │ Symbol() │ +│ c │ 10 │ │ +└─────────┴────┴──────────┘ +`); + +test(new Map([ ['a', 1], [Symbol(), [2]] ]), ` +┌───────────────────┬──────────┬────────┐ +│ (iteration index) │ Key │ Values │ +├───────────────────┼──────────┼────────┤ +│ 0 │ 'a' │ 1 │ +│ 1 │ Symbol() │ [ 2 ] │ +└───────────────────┴──────────┴────────┘ +`); + +test(new Set([1, 2, Symbol()]), ` +┌───────────────────┬──────────┐ +│ (iteration index) │ Values │ +├───────────────────┼──────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ Symbol() │ +└───────────────────┴──────────┘ +`); + +test({ a: 1, b: 2 }, ['a'], ` +┌─────────┬───┐ +│ (index) │ a │ +├─────────┼───┤ +│ a │ │ +│ b │ │ +└─────────┴───┘ +`); + +test([{ a: 1, b: 2 }, { a: 3, c: 4 }], ['a'], ` +┌─────────┬───┐ +│ (index) │ a │ +├─────────┼───┤ +│ 0 │ 1 │ +│ 1 │ 3 │ +└─────────┴───┘ +`); + +test(new Map([[1, 1], [2, 2], [3, 3]]).entries(), ` +┌───────────────────┬─────┬────────┐ +│ (iteration index) │ Key │ Values │ +├───────────────────┼─────┼────────┤ +│ 0 │ 1 │ 1 │ +│ 1 │ 2 │ 2 │ +│ 2 │ 3 │ 3 │ +└───────────────────┴─────┴────────┘ +`); + +test(new Map([[1, 1], [2, 2], [3, 3]]).values(), ` +┌───────────────────┬────────┐ +│ (iteration index) │ Values │ +├───────────────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└───────────────────┴────────┘ +`); + +test(new Map([[1, 1], [2, 2], [3, 3]]).keys(), ` +┌───────────────────┬────────┐ +│ (iteration index) │ Values │ +├───────────────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└───────────────────┴────────┘ +`); + +test(new Set([1, 2, 3]).values(), ` +┌───────────────────┬────────┐ +│ (iteration index) │ Values │ +├───────────────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└───────────────────┴────────┘ +`); + + +test({ a: { a: 1, b: 2, c: 3 } }, ` +┌─────────┬───┬───┬───┐ +│ (index) │ a │ b │ c │ +├─────────┼───┼───┼───┤ +│ a │ 1 │ 2 │ 3 │ +└─────────┴───┴───┴───┘ +`); + +test({ a: { a: { a: 1, b: 2, c: 3 } } }, ` +┌─────────┬──────────┐ +│ (index) │ a │ +├─────────┼──────────┤ +│ a │ [Object] │ +└─────────┴──────────┘ +`); + +test({ a: [1, 2] }, ` +┌─────────┬───┬───┐ +│ (index) │ 0 │ 1 │ +├─────────┼───┼───┤ +│ a │ 1 │ 2 │ +└─────────┴───┴───┘ +`); + +test({ a: [1, 2, 3, 4, 5], b: 5, c: { e: 5 } }, ` +┌─────────┬───┬───┬───┬───┬───┬───┬────────┐ +│ (index) │ 0 │ 1 │ 2 │ 3 │ 4 │ e │ Values │ +├─────────┼───┼───┼───┼───┼───┼───┼────────┤ +│ a │ 1 │ 2 │ 3 │ 4 │ 5 │ │ │ +│ b │ │ │ │ │ │ │ 5 │ +│ c │ │ │ │ │ │ 5 │ │ +└─────────┴───┴───┴───┴───┴───┴───┴────────┘ +`); + +test(new Uint8Array([1, 2, 3]), ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└─────────┴────────┘ +`); + +test(Buffer.from([1, 2, 3]), ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└─────────┴────────┘ +`); + +test({ a: undefined }, ['x'], ` +┌─────────┬───┐ +│ (index) │ x │ +├─────────┼───┤ +│ a │ │ +└─────────┴───┘ +`); + +test([], ` +┌─────────┐ +│ (index) │ +├─────────┤ +└─────────┘ +`); + +test(new Map(), ` +┌───────────────────┬─────┬────────┐ +│ (iteration index) │ Key │ Values │ +├───────────────────┼─────┼────────┤ +└───────────────────┴─────┴────────┘ +`); + +test([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ` +┌─────────┬─────┬─────┐ +│ (index) │ a │ b │ +├─────────┼─────┼─────┤ +│ 0 │ 1 │ 'Y' │ +│ 1 │ 'Z' │ 2 │ +└─────────┴─────┴─────┘ +`); + +{ + const line = '─'.repeat(79); + const header = `${' '.repeat(37)}name${' '.repeat(40)}`; + const name = 'very long long long long long long long long long long long ' + + 'long long long long'; + test([{ name }], ` +┌─────────┬──${line}──┐ +│ (index) │ ${header}│ +├─────────┼──${line}──┤ +│ 0 │ '${name}' │ +└─────────┴──${line}──┘ +`); +} + +test({ foo: '¥', bar: '¥' }, ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ foo │ '¥' │ +│ bar │ '¥' │ +└─────────┴────────┘ +`); + +test({ foo: '你好', bar: 'hello' }, ` +┌─────────┬─────────┐ +│ (index) │ Values │ +├─────────┼─────────┤ +│ foo │ '你好' │ +│ bar │ 'hello' │ +└─────────┴─────────┘ +`); + +// Regression test for prototype pollution via console.table. Earlier versions +// of Node.js created an object with a non-null prototype within console.table +// and then wrote to object[column][index], which lead to an error as well as +// modifications to Object.prototype. +test([{ foo: 10 }, { foo: 20 }], ['__proto__'], ` +┌─────────┬───────────┐ +│ (index) │ __proto__ │ +├─────────┼───────────┤ +│ 0 │ │ +│ 1 │ │ +└─────────┴───────────┘ +`); +assert.strictEqual('0' in Object.prototype, false); +assert.strictEqual('1' in Object.prototype, false); diff --git a/cli/tests/node_compat/test/parallel/test-console-tty-colors.js b/cli/tests/node_compat/test/parallel/test-console-tty-colors.js new file mode 100644 index 00000000000000..9676529baa50f7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-console-tty-colors.js @@ -0,0 +1,102 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const util = require('util'); +const { Writable } = require('stream'); +const { Console } = require('console'); + +function check(isTTY, colorMode, expectedColorMode, inspectOptions) { + const items = [ + 1, + { a: 2 }, + [ 'foo' ], + { '\\a': '\\bar' }, + ]; + + let i = 0; + const stream = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + assert.strictEqual(chunk.trim(), + util.inspect(items[i++], { + colors: expectedColorMode, + ...inspectOptions + })); + cb(); + }, items.length), + decodeStrings: false + }); + stream.isTTY = isTTY; + + // Set ignoreErrors to `false` here so that we see assertion failures + // from the `write()` call happen. + const testConsole = new Console({ + stdout: stream, + ignoreErrors: false, + colorMode, + inspectOptions + }); + for (const item of items) { + testConsole.log(item); + } +} + +check(true, 'auto', true); +check(false, 'auto', false); +check(false, undefined, true, { colors: true, compact: false }); +check(true, 'auto', true, { compact: false }); +check(true, undefined, false, { colors: false }); +check(true, true, true); +check(false, true, true); +check(true, false, false); +check(false, false, false); + +// Check invalid options. +{ + const stream = new Writable({ + write: common.mustNotCall() + }); + + [0, 'true', null, {}, [], () => {}].forEach((colorMode) => { + const received = util.inspect(colorMode); + assert.throws( + () => { + new Console({ + stdout: stream, + ignoreErrors: false, + colorMode: colorMode + }); + }, + { + message: `The argument 'colorMode' is invalid. Received ${received}`, + code: 'ERR_INVALID_ARG_VALUE' + } + ); + }); + + [true, false, 'auto'].forEach((colorMode) => { + assert.throws( + () => { + new Console({ + stdout: stream, + ignoreErrors: false, + colorMode: colorMode, + inspectOptions: { + colors: false + } + }); + }, + { + message: 'Option "options.inspectOptions.color" cannot be used in ' + + 'combination with option "colorMode"', + code: 'ERR_INCOMPATIBLE_OPTION_PAIR' + } + ); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-crypto-hmac.js b/cli/tests/node_compat/test/parallel/test-crypto-hmac.js new file mode 100644 index 00000000000000..174457a6322d6c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-crypto-hmac.js @@ -0,0 +1,483 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +{ + const Hmac = crypto.Hmac; + const instance = crypto.Hmac('sha256', 'Node'); + assert(instance instanceof Hmac, 'Hmac is expected to return a new instance' + + ' when called without `new`'); +} + +assert.throws( + () => crypto.createHmac(null), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "hmac" argument must be of type string. Received null' + }); + +// This used to segfault. See: https://github.com/nodejs/node/issues/9819 +assert.throws( + () => crypto.createHmac('sha256', 'key').digest({ + toString: () => { throw new Error('boom'); }, + }), + { + name: 'Error', + message: 'boom' + }); + +assert.throws( + () => crypto.createHmac('sha1', null), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + }); + +function testHmac(algo, key, data, expected) { + // TODO(kt3k): Skip non-string key for now. + // Enable this when we implement crypto.createSecretKey + if (typeof key !== "string") { + return; + } + // FIPS does not support MD5. + if (common.hasFipsCrypto && algo === 'md5') + return; + + if (!Array.isArray(data)) + data = [data]; + + // If the key is a Buffer, test Hmac with a key object as well. + const keyWrappers = [ + (key) => key, + ...(typeof key === 'string' ? [] : [crypto.createSecretKey]), + ]; + + for (const keyWrapper of keyWrappers) { + const hmac = crypto.createHmac(algo, keyWrapper(key)); + for (const chunk of data) + hmac.update(chunk); + const actual = hmac.digest('hex'); + assert.strictEqual(actual, expected); + } +} + +{ + // Test HMAC with multiple updates. + testHmac('sha1', 'Node', ['some data', 'to hmac'], + '19fd6e1ba73d9ed2224dd5094a71babe85d9a892'); +} + +// Test HMAC (Wikipedia Test Cases) +const wikipedia = [ + { + key: 'key', data: 'The quick brown fox jumps over the lazy dog', + hmac: { // HMACs lifted from Wikipedia. + md5: '80070713463e7749b90c2dc24911e275', + sha1: 'de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9', + sha256: + 'f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc' + + '2d1a3cd8' + } + }, + { + key: 'key', data: '', + hmac: { // Intermediate test to help debugging. + md5: '63530468a04e386459855da0063b6596', + sha1: 'f42bb0eeb018ebbd4597ae7213711ec60760843f', + sha256: + '5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74' + + '832607d0' + } + }, + { + key: '', data: 'The quick brown fox jumps over the lazy dog', + hmac: { // Intermediate test to help debugging. + md5: 'ad262969c53bc16032f160081c4a07a0', + sha1: '2ba7f707ad5f187c412de3106583c3111d668de8', + sha256: + 'fb011e6154a19b9a4c767373c305275a5a69e8b68b0b4c9200c383dc' + + 'ed19a416' + } + }, + { + key: '', data: '', + hmac: { // HMACs lifted from Wikipedia. + md5: '74e6f7298a9c2d168935f58c001bad88', + sha1: 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', + sha256: + 'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c71214' + + '4292c5ad' + } + }, +]; + +for (const { key, data, hmac } of wikipedia) { + for (const hash in hmac) + testHmac(hash, key, data, hmac[hash]); +} + +// Test HMAC-SHA-* (rfc 4231 Test Cases) +const rfc4231 = [ + { + key: Buffer.from('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', 'hex'), + data: Buffer.from('4869205468657265', 'hex'), // 'Hi There' + hmac: { + sha224: '896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22', + sha256: + 'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c' + + '2e32cff7', + sha384: + 'afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c' + + '7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6', + sha512: + '87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b305' + + '45e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f170' + + '2e696c203a126854' + } + }, + { + key: Buffer.from('4a656665', 'hex'), // 'Jefe' + data: Buffer.from('7768617420646f2079612077616e7420666f72206e6f74686' + + '96e673f', 'hex'), // 'what do ya want for nothing?' + hmac: { + sha224: 'a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44', + sha256: + '5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b9' + + '64ec3843', + sha384: + 'af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec373' + + '6322445e8e2240ca5e69e2c78b3239ecfab21649', + sha512: + '164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7' + + 'ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b' + + '636e070a38bce737' + } + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'hex'), + data: Buffer.from('ddddddddddddddddddddddddddddddddddddddddddddddddd' + + 'ddddddddddddddddddddddddddddddddddddddddddddddddddd', + 'hex'), + hmac: { + sha224: '7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea', + sha256: + '773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514' + + 'ced565fe', + sha384: + '88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e5' + + '5966144b2a5ab39dc13814b94e3ab6e101a34f27', + sha512: + 'fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33' + + 'b2279d39bf3e848279a722c806b485a47e67c807b946a337bee89426' + + '74278859e13292fb' + } + }, + { + key: Buffer.from('0102030405060708090a0b0c0d0e0f10111213141516171819', + 'hex'), + data: Buffer.from('cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdc' + + 'dcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd', + 'hex'), + hmac: { + sha224: '6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a', + sha256: + '82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff4' + + '6729665b', + sha384: + '3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e' + + '1f573b4e6801dd23c4a7d679ccf8a386c674cffb', + sha512: + 'b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050' + + '361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2d' + + 'e2adebeb10a298dd' + } + }, + + { + key: Buffer.from('0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c', 'hex'), + // 'Test With Truncation' + data: Buffer.from('546573742057697468205472756e636174696f6e', 'hex'), + hmac: { + sha224: '0e2aea68a90c8d37c988bcdb9fca6fa8', + sha256: 'a3b6167473100ee06e0c796c2955552b', + sha384: '3abf34c3503b2a23a46efc619baef897', + sha512: '415fad6271580a531d4179bc891d87a6' + }, + truncate: true + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaa', 'hex'), + // 'Test Using Larger Than Block-Size Key - Hash Key First' + data: Buffer.from('54657374205573696e67204c6172676572205468616e20426' + + 'c6f636b2d53697a65204b6579202d2048617368204b657920' + + '4669727374', 'hex'), + hmac: { + sha224: '95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e', + sha256: + '60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f' + + '0ee37f54', + sha384: + '4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05' + + '033ac4c60c2ef6ab4030fe8296248df163f44952', + sha512: + '80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b0137' + + '83f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec' + + '8b915a985d786598' + } + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaa', 'hex'), + // 'This is a test using a larger than block-size key and a larger ' + + // 'than block-size data. The key needs to be hashed before being ' + + // 'used by the HMAC algorithm.' + data: Buffer.from('5468697320697320612074657374207573696e672061206c6' + + '172676572207468616e20626c6f636b2d73697a65206b6579' + + '20616e642061206c6172676572207468616e20626c6f636b2' + + 'd73697a6520646174612e20546865206b6579206e65656473' + + '20746f20626520686173686564206265666f7265206265696' + + 'e6720757365642062792074686520484d414320616c676f72' + + '6974686d2e', 'hex'), + hmac: { + sha224: '3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1', + sha256: + '9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f5153' + + '5c3a35e2', + sha384: + '6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82' + + '461e99c5a678cc31e799176d3860e6110c46523e', + sha512: + 'e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d' + + '20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de04460' + + '65c97440fa8c6a58' + } + }, +]; + +for (let i = 0, l = rfc4231.length; i < l; i++) { + for (const hash in rfc4231[i].hmac) { + const str = crypto.createHmac(hash, rfc4231[i].key); + str.end(rfc4231[i].data); + let strRes = str.read().toString('hex'); + let actual = crypto.createHmac(hash, rfc4231[i].key) + .update(rfc4231[i].data) + .digest('hex'); + if (rfc4231[i].truncate) { + actual = actual.slice(0, 32); // first 128 bits == 32 hex chars + strRes = strRes.slice(0, 32); + } + const expected = rfc4231[i].hmac[hash]; + assert.strictEqual( + actual, + expected, + `Test HMAC-${hash} rfc 4231 case ${i + 1}: ${actual} must be ${expected}` + ); + assert.strictEqual( + actual, + strRes, + `Should get same result from stream (hash: ${hash} and case: ${i + 1})` + + ` => ${actual} must be ${strRes}` + ); + } +} + +// Test HMAC-MD5/SHA1 (rfc 2202 Test Cases) +const rfc2202_md5 = [ + { + key: Buffer.from('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', 'hex'), + data: 'Hi There', + hmac: '9294727a3638bb1c13f48ef8158bfc9d' + }, + { + key: 'Jefe', + data: 'what do ya want for nothing?', + hmac: '750c783e6ab0b503eaa86e310a5db738' + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'hex'), + data: Buffer.from('ddddddddddddddddddddddddddddddddddddddddddddddddd' + + 'ddddddddddddddddddddddddddddddddddddddddddddddddddd', + 'hex'), + hmac: '56be34521d144c88dbb8c733f0e8b3f6' + }, + { + key: Buffer.from('0102030405060708090a0b0c0d0e0f10111213141516171819', + 'hex'), + data: Buffer.from('cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdc' + + 'dcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd' + + 'cdcdcdcdcd', + 'hex'), + hmac: '697eaf0aca3a3aea3a75164746ffaa79' + }, + { + key: Buffer.from('0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c', 'hex'), + data: 'Test With Truncation', + hmac: '56461ef2342edc00f9bab995690efd4c' + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaa', + 'hex'), + data: 'Test Using Larger Than Block-Size Key - Hash Key First', + hmac: '6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd' + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaa', + 'hex'), + data: + 'Test Using Larger Than Block-Size Key and Larger Than One ' + + 'Block-Size Data', + hmac: '6f630fad67cda0ee1fb1f562db3aa53e' + }, +]; + +for (const { key, data, hmac } of rfc2202_md5) + testHmac('md5', key, data, hmac); + +const rfc2202_sha1 = [ + { + key: Buffer.from('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', 'hex'), + data: 'Hi There', + hmac: 'b617318655057264e28bc0b6fb378c8ef146be00' + }, + { + key: 'Jefe', + data: 'what do ya want for nothing?', + hmac: 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79' + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'hex'), + data: Buffer.from('ddddddddddddddddddddddddddddddddddddddddddddd' + + 'ddddddddddddddddddddddddddddddddddddddddddddd' + + 'dddddddddd', + 'hex'), + hmac: '125d7342b9ac11cd91a39af48aa17b4f63f175d3' + }, + { + key: Buffer.from('0102030405060708090a0b0c0d0e0f10111213141516171819', + 'hex'), + data: Buffer.from('cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdc' + + 'dcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd' + + 'cdcdcdcdcd', + 'hex'), + hmac: '4c9007f4026250c6bc8414f9bf50c86c2d7235da' + }, + { + key: Buffer.from('0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c', 'hex'), + data: 'Test With Truncation', + hmac: '4c1a03424b55e07fe7f27be1d58bb9324a9a5a04' + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaa', + 'hex'), + data: 'Test Using Larger Than Block-Size Key - Hash Key First', + hmac: 'aa4ae5e15272d00e95705637ce8a3b55ed402112' + }, + { + key: Buffer.from('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaa', + 'hex'), + data: + 'Test Using Larger Than Block-Size Key and Larger Than One ' + + 'Block-Size Data', + hmac: 'e8e99d0f45237d786d6bbaa7965c7808bbff1a91' + }, +]; + +for (const { key, data, hmac } of rfc2202_sha1) + testHmac('sha1', key, data, hmac); + +assert.strictEqual( + crypto.createHmac('sha256', 'w00t').digest('ucs2'), + crypto.createHmac('sha256', 'w00t').digest().toString('ucs2')); + +// Check initialized -> uninitialized state transition after calling digest(). +{ + const expected = + '\u0010\u0041\u0052\u00c5\u00bf\u00dc\u00a0\u007b\u00c6\u0033' + + '\u00ee\u00bd\u0046\u0019\u009f\u0002\u0055\u00c9\u00f4\u009d'; + { + const h = crypto.createHmac('sha1', 'key').update('data'); + assert.deepStrictEqual(h.digest('buffer'), Buffer.from(expected, 'latin1')); + // TODO(kt3k): Enable this assertion + // assert.deepStrictEqual(h.digest('buffer'), Buffer.from('')); + } + { + const h = crypto.createHmac('sha1', 'key').update('data'); + assert.strictEqual(h.digest('latin1'), expected); + // TODO(kt3k): Enable this assertion + // assert.strictEqual(h.digest('latin1'), ''); + } +} + +// Check initialized -> uninitialized state transition after calling digest(). +// Calls to update() omitted intentionally. +{ + const expected = + '\u00f4\u002b\u00b0\u00ee\u00b0\u0018\u00eb\u00bd\u0045\u0097' + + '\u00ae\u0072\u0013\u0071\u001e\u00c6\u0007\u0060\u0084\u003f'; + { + const h = crypto.createHmac('sha1', 'key'); + assert.deepStrictEqual(h.digest('buffer'), Buffer.from(expected, 'latin1')); + // TODO(kt3k): Enable this assertion + // assert.deepStrictEqual(h.digest('buffer'), Buffer.from('')); + } + { + const h = crypto.createHmac('sha1', 'key'); + assert.strictEqual(h.digest('latin1'), expected); + // TODO(kt3k): Enable this assertion + // assert.strictEqual(h.digest('latin1'), ''); + } +} + +/* +TODO(kt3k): Enable this test. +{ + assert.throws( + () => crypto.createHmac('sha7', 'key'), + /Invalid digest/); +} +*/ + +/* + TODO(kt3k): enable this case when we implemented crypto.createSecretKey +{ + const buf = Buffer.alloc(0); + const keyObject = crypto.createSecretKey(Buffer.alloc(0)); + assert.deepStrictEqual( + crypto.createHmac('sha256', buf).update('foo').digest(), + crypto.createHmac('sha256', keyObject).update('foo').digest(), + ); +} +*/ diff --git a/cli/tests/node_compat/test/parallel/test-dgram-address.js b/cli/tests/node_compat/test/parallel/test-dgram-address.js new file mode 100644 index 00000000000000..ffcf8485e8aca1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-address.js @@ -0,0 +1,88 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +{ + // IPv4 Test + const socket = dgram.createSocket('udp4'); + + socket.on('listening', common.mustCall(() => { + const address = socket.address(); + + assert.strictEqual(address.address, common.localhostIPv4); + assert.strictEqual(typeof address.port, 'number'); + assert.ok(isFinite(address.port)); + assert.ok(address.port > 0); + assert.strictEqual(address.family, 'IPv4'); + socket.close(); + })); + + socket.on('error', (err) => { + socket.close(); + assert.fail(`Unexpected error on udp4 socket. ${err.toString()}`); + }); + + socket.bind(0, common.localhostIPv4); +} + +if (common.hasIPv6) { + // IPv6 Test + const socket = dgram.createSocket('udp6'); + const localhost = '::1'; + + socket.on('listening', common.mustCall(() => { + const address = socket.address(); + + assert.strictEqual(address.address, localhost); + assert.strictEqual(typeof address.port, 'number'); + assert.ok(isFinite(address.port)); + assert.ok(address.port > 0); + assert.strictEqual(address.family, 'IPv6'); + socket.close(); + })); + + socket.on('error', (err) => { + socket.close(); + assert.fail(`Unexpected error on udp6 socket. ${err.toString()}`); + }); + + socket.bind(0, localhost); +} + +{ + // Verify that address() throws if the socket is not bound. + const socket = dgram.createSocket('udp4'); + + assert.throws(() => { + socket.address(); + }, /^Error: getsockname EBADF$/); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-bind-default-address.js b/cli/tests/node_compat/test/parallel/test-dgram-bind-default-address.js new file mode 100644 index 00000000000000..9a6e1412ca2aec --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-bind-default-address.js @@ -0,0 +1,60 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +// Skip test in FreeBSD jails since 0.0.0.0 will resolve to default interface +if (common.inFreeBSDJail) + common.skip('In a FreeBSD jail'); + +const assert = require('assert'); +const dgram = require('dgram'); + +dgram.createSocket('udp4').bind(0, common.mustCall(function() { + assert.strictEqual(typeof this.address().port, 'number'); + assert.ok(isFinite(this.address().port)); + assert.ok(this.address().port > 0); + assert.strictEqual(this.address().address, '0.0.0.0'); + this.close(); +})); + +if (!common.hasIPv6) { + common.printSkipMessage('udp6 part of test, because no IPv6 support'); + return; +} + +dgram.createSocket('udp6').bind(0, common.mustCall(function() { + assert.strictEqual(typeof this.address().port, 'number'); + assert.ok(isFinite(this.address().port)); + assert.ok(this.address().port > 0); + let address = this.address().address; + if (address === '::ffff:0.0.0.0') + address = '::'; + assert.strictEqual(address, '::'); + this.close(); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-bind.js b/cli/tests/node_compat/test/parallel/test-dgram-bind.js new file mode 100644 index 00000000000000..93d68fd8a382cb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-bind.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const socket = dgram.createSocket('udp4'); + +socket.on('listening', common.mustCall(() => { + assert.throws(() => { + socket.bind(); + }, { + code: 'ERR_SOCKET_ALREADY_BOUND', + name: 'Error', + message: /^Socket is already bound$/ + }); + + socket.close(); +})); + +const result = socket.bind(); // Should not throw. + +assert.strictEqual(result, socket); // Should have returned itself. diff --git a/cli/tests/node_compat/test/parallel/test-dgram-bytes-length.js b/cli/tests/node_compat/test/parallel/test-dgram-bytes-length.js new file mode 100644 index 00000000000000..4ce2dabc352efd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-bytes-length.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const message = Buffer.from('Some bytes'); +const client = dgram.createSocket('udp4'); +client.send( + message, + 0, + message.length, + 41234, + 'localhost', + function(err, bytes) { + assert.strictEqual(bytes, message.length); + client.close(); + } +); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-close-during-bind.js b/cli/tests/node_compat/test/parallel/test-dgram-close-during-bind.js new file mode 100644 index 00000000000000..8e0621884cd1ea --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-close-during-bind.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); +const { kStateSymbol } = require('internal/dgram'); +const socket = dgram.createSocket('udp4'); +const { handle } = socket[kStateSymbol]; +const lookup = handle.lookup; + +// Test the scenario where the socket is closed during a bind operation. +handle.bind = common.mustNotCall('bind() should not be called.'); + +handle.lookup = common.mustCall(function(address, callback) { + socket.close(common.mustCall(() => { + lookup.call(this, address, callback); + })); +}); + +socket.bind(common.mustNotCall('Socket should not bind.')); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-close-in-listening.js b/cli/tests/node_compat/test/parallel/test-dgram-close-in-listening.js new file mode 100644 index 00000000000000..673b145b8637e2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-close-in-listening.js @@ -0,0 +1,33 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Ensure that if a dgram socket is closed before the sendQueue is drained +// will not crash + +const common = require('../common'); +const dgram = require('dgram'); + +const buf = Buffer.alloc(1024, 42); + +const socket = dgram.createSocket('udp4'); + +socket.on('listening', function() { + socket.close(); +}); + +// Get a random port for send +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + // Adds a listener to 'listening' to send the data when + // the socket is available + socket.send(buf, 0, buf.length, + portGetter.address().port, + portGetter.address().address); + + portGetter.close(); + })); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-close-is-not-callback.js b/cli/tests/node_compat/test/parallel/test-dgram-close-is-not-callback.js new file mode 100644 index 00000000000000..d67bf5aa4d1ec5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-close-is-not-callback.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +const buf = Buffer.alloc(1024, 42); + +const socket = dgram.createSocket('udp4'); + +// Get a random port for send +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + socket.send(buf, 0, buf.length, + portGetter.address().port, + portGetter.address().address); + + // If close callback is not function, ignore the argument. + socket.close('bad argument'); + portGetter.close(); + + socket.on('close', common.mustCall()); + })); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-close-signal.js b/cli/tests/node_compat/test/parallel/test-dgram-close-signal.js new file mode 100644 index 00000000000000..97683191bff836 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-close-signal.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +{ + // Test bad signal. + assert.throws( + () => dgram.createSocket({ type: 'udp4', signal: {} }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +{ + // Test close. + const controller = new AbortController(); + const { signal } = controller; + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); + controller.abort(); +} + +{ + // Test close with pre-aborted signal. + const signal = AbortSignal.abort(); + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-close.js b/cli/tests/node_compat/test/parallel/test-dgram-close.js new file mode 100644 index 00000000000000..46dfd80148b180 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-close.js @@ -0,0 +1,63 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --expose-internals +'use strict'; +// Ensure that if a dgram socket is closed before the DNS lookup completes, it +// won't crash. + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const { kStateSymbol } = require('internal/dgram'); + +const buf = Buffer.alloc(1024, 42); + +let socket = dgram.createSocket('udp4'); +const { handle } = socket[kStateSymbol]; + +// Get a random port for send +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + socket.send(buf, 0, buf.length, + portGetter.address().port, + portGetter.address().address); + + assert.strictEqual(socket.close(common.mustCall()), socket); + socket.on('close', common.mustCall()); + socket = null; + + // Verify that accessing handle after closure doesn't throw + setImmediate(function() { + setImmediate(function() { + console.log('Handle fd is: ', handle.fd); + }); + }); + + portGetter.close(); + })); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer-length.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer-length.js new file mode 100644 index 00000000000000..87f878754f0d6e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer-length.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); +const offset = 20; +const len = buf.length - offset; + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, common.mustCall(() => { + client.connect(client.address().port, common.mustCall(() => { + client.send(buf, offset, len, messageSent); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer.js new file mode 100644 index 00000000000000..3b1948cc012a57 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-buffer.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, common.mustCall(() => { + client.connect(client.address().port, common.mustCall(() => { + client.send(buf, onMessage); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-multi-buffer.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-multi-buffer.js new file mode 100644 index 00000000000000..365d0c4e76eaa8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-callback-multi-buffer.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustCall((err, bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', common.mustCall(() => { + const port = client.address().port; + client.connect(port, common.mustCall(() => { + client.send([buf1, buf2], messageSent); + })); +})); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-default-host.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-default-host.js new file mode 100644 index 00000000000000..3b2a579c0005d9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-default-host.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); +const server = dgram.createSocket('udp4'); + +const toSend = [Buffer.alloc(256, 'x'), + Buffer.alloc(256, 'y'), + Buffer.alloc(256, 'z'), + 'hello']; + +const received = []; + +server.on('listening', common.mustCall(() => { + const port = server.address().port; + client.connect(port, (err) => { + assert.ifError(err); + client.send(toSend[0], 0, toSend[0].length); + client.send(toSend[1]); + client.send([toSend[2]]); + client.send(toSend[3], 0, toSend[3].length); + + client.send(new Uint8Array(toSend[0]), 0, toSend[0].length); + client.send(new Uint8Array(toSend[1])); + client.send([new Uint8Array(toSend[2])]); + client.send(new Uint8Array(Buffer.from(toSend[3])), + 0, toSend[3].length); + }); +})); + +server.on('message', common.mustCall((buf, info) => { + received.push(buf.toString()); + + if (received.length === toSend.length * 2) { + // The replies may arrive out of order -> sort them before checking. + received.sort(); + + const expected = toSend.concat(toSend).map(String).sort(); + assert.deepStrictEqual(received, expected); + client.close(); + server.close(); + } +}, toSend.length * 2)); + +server.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-array.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-array.js new file mode 100644 index 00000000000000..3ca2f930b53380 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-array.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.alloc(0); + assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); + client.close(); +})); + +client.on('listening', common.mustCall(() => { + client.connect(client.address().port, + common.localhostIPv4, + common.mustCall(() => client.send([]))); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-buffer.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-buffer.js new file mode 100644 index 00000000000000..07a841305ac048 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-buffer.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + const port = this.address().port; + client.connect(port, common.mustCall(() => { + const buf = Buffer.alloc(0); + client.send(buf, 0, 0, common.mustSucceed()); + })); + + client.on('message', common.mustCall((buffer) => { + assert.strictEqual(buffer.length, 0); + client.close(); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-packet.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-packet.js new file mode 100644 index 00000000000000..04bb36ab62e24c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-empty-packet.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + client.connect(client.address().port, common.mustCall(() => { + client.on('message', common.mustCall(callback)); + const buf = Buffer.alloc(1); + + const interval = setInterval(function() { + client.send(buf, 0, 0, common.mustCall(callback)); + }, 10); + + function callback(firstArg) { + // If client.send() callback, firstArg should be null. + // If client.on('message') listener, firstArg should be a 0-length buffer. + if (firstArg instanceof Buffer) { + assert.strictEqual(firstArg.length, 0); + clearInterval(interval); + client.close(); + } + } + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-buffer-copy.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-buffer-copy.js new file mode 100644 index 00000000000000..aec62ee51a4f4a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-buffer-copy.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const onMessage = common.mustCall(common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +})); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', common.mustCall(function() { + const toSend = [buf1, buf2]; + client.connect(client.address().port, common.mustCall(() => { + client.send(toSend, onMessage); + })); +})); + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-string-array.js b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-string-array.js new file mode 100644 index 00000000000000..daf787e25ddcd8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect-send-multi-string-array.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const socket = dgram.createSocket('udp4'); +const data = ['foo', 'bar', 'baz']; + +socket.on('message', common.mustCall((msg, rinfo) => { + socket.close(); + assert.deepStrictEqual(msg.toString(), data.join('')); +})); + +socket.bind(0, () => { + socket.connect(socket.address().port, common.mustCall(() => { + socket.send(data); + })); +}); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-connect.js b/cli/tests/node_compat/test/parallel/test-dgram-connect.js new file mode 100644 index 00000000000000..41d6a60620f9a6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-connect.js @@ -0,0 +1,73 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const PORT = 12345; + +const client = dgram.createSocket('udp4'); +client.connect(PORT, common.mustCall(() => { + const remoteAddr = client.remoteAddress(); + assert.strictEqual(remoteAddr.port, PORT); + assert.throws(() => { + client.connect(PORT, common.mustNotCall()); + }, { + name: 'Error', + message: 'Already connected', + code: 'ERR_SOCKET_DGRAM_IS_CONNECTED' + }); + + client.disconnect(); + assert.throws(() => { + client.disconnect(); + }, { + name: 'Error', + message: 'Not connected', + code: 'ERR_SOCKET_DGRAM_NOT_CONNECTED' + }); + + assert.throws(() => { + client.remoteAddress(); + }, { + name: 'Error', + message: 'Not connected', + code: 'ERR_SOCKET_DGRAM_NOT_CONNECTED' + }); + + client.once('connect', common.mustCall(() => client.close())); + client.connect(PORT); +})); + +assert.throws(() => { + client.connect(PORT); +}, { + name: 'Error', + message: 'Already connected', + code: 'ERR_SOCKET_DGRAM_IS_CONNECTED' +}); + +assert.throws(() => { + client.disconnect(); +}, { + name: 'Error', + message: 'Not connected', + code: 'ERR_SOCKET_DGRAM_NOT_CONNECTED' +}); + +[ 0, null, 78960, undefined ].forEach((port) => { + assert.throws(() => { + client.connect(port); + }, { + name: 'RangeError', + message: /^Port should be > 0 and < 65536/, + code: 'ERR_SOCKET_BAD_PORT' + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-createSocket-type.js b/cli/tests/node_compat/test/parallel/test-dgram-createSocket-type.js new file mode 100644 index 00000000000000..afcb54ab2f22c1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-createSocket-type.js @@ -0,0 +1,68 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const invalidTypes = [ + 'test', + ['udp4'], + new String('udp4'), + 1, + {}, + true, + false, + null, + undefined, +]; +const validTypes = [ + 'udp4', + 'udp6', + { type: 'udp4' }, + { type: 'udp6' }, +]; +const errMessage = /^Bad socket type specified\. Valid types are: udp4, udp6$/; + +// Error must be thrown with invalid types +invalidTypes.forEach((invalidType) => { + assert.throws(() => { + dgram.createSocket(invalidType); + }, { + code: 'ERR_SOCKET_BAD_TYPE', + name: 'TypeError', + message: errMessage + }); +}); + +// Error must not be thrown with valid types +validTypes.forEach((validType) => { + const socket = dgram.createSocket(validType); + socket.close(); +}); + +// Ensure buffer sizes can be set +{ + const socket = dgram.createSocket({ + type: 'udp4', + recvBufferSize: 10000, + sendBufferSize: 15000 + }); + + socket.bind(common.mustCall(() => { + // note: linux will double the buffer size + assert.ok(socket.getRecvBufferSize() === 10000 || + socket.getRecvBufferSize() === 20000, + 'SO_RCVBUF not 10000 or 20000, ' + + `was ${socket.getRecvBufferSize()}`); + assert.ok(socket.getSendBufferSize() === 15000 || + socket.getSendBufferSize() === 30000, + 'SO_SNDBUF not 15000 or 30000, ' + + `was ${socket.getRecvBufferSize()}`); + socket.close(); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-custom-lookup.js b/cli/tests/node_compat/test/parallel/test-dgram-custom-lookup.js new file mode 100644 index 00000000000000..ca3bd3df337d0b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-custom-lookup.js @@ -0,0 +1,56 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const dns = require('dns'); + +{ + // Verify that the provided lookup function is called. + const lookup = common.mustCall((host, family, callback) => { + dns.lookup(host, family, callback); + }); + + const socket = dgram.createSocket({ type: 'udp4', lookup }); + + socket.bind(common.mustCall(() => { + socket.close(); + })); +} + +// TODO: unable to overwrite imports with spies +// { +// // Verify that lookup defaults to dns.lookup(). +// const originalLookup = dns.lookup; + +// dns.lookup = common.mustCall((host, family, callback) => { +// dns.lookup = originalLookup; +// originalLookup(host, family, callback); +// }); + +// const socket = dgram.createSocket({ type: 'udp4' }); + +// socket.bind(common.mustCall(() => { +// socket.close(); +// })); +// } + +{ + // Verify that non-functions throw. + [null, true, false, 0, 1, NaN, '', 'foo', {}, Symbol()].forEach((value) => { + assert.throws(() => { + dgram.createSocket({ type: 'udp4', lookup: value }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "lookup" argument must be of type function.' + + common.invalidArgTypeHelper(value) + }); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-error-message-address.js b/cli/tests/node_compat/test/parallel/test-dgram-error-message-address.js new file mode 100644 index 00000000000000..3bb144a8987f72 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-error-message-address.js @@ -0,0 +1,64 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +// IPv4 Test +const socket_ipv4 = dgram.createSocket('udp4'); + +socket_ipv4.on('listening', common.mustNotCall()); + +socket_ipv4.on('error', common.mustCall(function(e) { + assert.strictEqual(e.port, undefined); + assert.strictEqual(e.message, 'bind EADDRNOTAVAIL 1.1.1.1'); + assert.strictEqual(e.address, '1.1.1.1'); + assert.strictEqual(e.code, 'EADDRNOTAVAIL'); + socket_ipv4.close(); +})); + +socket_ipv4.bind(0, '1.1.1.1'); + +// IPv6 Test +const socket_ipv6 = dgram.createSocket('udp6'); + +socket_ipv6.on('listening', common.mustNotCall()); + +socket_ipv6.on('error', common.mustCall(function(e) { + // EAFNOSUPPORT or EPROTONOSUPPORT means IPv6 is disabled on this system. + const allowed = ['EADDRNOTAVAIL', 'EAFNOSUPPORT', 'EPROTONOSUPPORT']; + assert(allowed.includes(e.code), `'${e.code}' was not one of ${allowed}.`); + assert.strictEqual(e.port, undefined); + assert.strictEqual(e.message, `bind ${e.code} 111::1`); + assert.strictEqual(e.address, '111::1'); + socket_ipv6.close(); +})); + +socket_ipv6.bind(0, '111::1'); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-implicit-bind.js b/cli/tests/node_compat/test/parallel/test-dgram-implicit-bind.js new file mode 100644 index 00000000000000..34167c95b211d5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-implicit-bind.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +const source = dgram.createSocket('udp4'); +const target = dgram.createSocket('udp4'); +let messages = 0; + +target.on('message', common.mustCall(function(buf) { + if (buf.toString() === 'abc') ++messages; + if (buf.toString() === 'def') ++messages; + if (messages === 2) { + source.close(); + target.close(); + } +}, 2)); + +target.on('listening', common.mustCall(function() { + // Second .send() call should not throw a bind error. + const port = this.address().port; + source.send(Buffer.from('abc'), 0, 3, port, '127.0.0.1'); + source.send(Buffer.from('def'), 0, 3, port, '127.0.0.1'); +})); + +target.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-ipv6only.js b/cli/tests/node_compat/test/parallel/test-dgram-ipv6only.js new file mode 100644 index 00000000000000..31f4e1fd9d120f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-ipv6only.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(cmorten): Deno.listenDatagram is currently `0.0.0.0` when you listen to `::`. + +'use strict'; + +const common = require('../common'); +if (!common.hasIPv6) + common.skip('no IPv6 support'); + +const dgram = require('dgram'); + +// This test ensures that dual-stack support is disabled when +// we specify the `ipv6Only` option in `dgram.createSocket()`. +const socket = dgram.createSocket({ + type: 'udp6', + ipv6Only: true, +}); + +socket.bind({ + port: 0, + address: '::', +}, common.mustCall(() => { + const { port } = socket.address(); + const client = dgram.createSocket('udp4'); + + // We can still bind to '0.0.0.0'. + // TODO: uncomment out when Deno allows IPv4 and IPv6 to be bound + // independently + // client.bind({ + // port, + // address: '0.0.0.0', + // }, common.mustCall(() => { + client.close(); + socket.close(); + // })); + + client.on('error', common.mustNotCall()); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-listen-after-bind.js b/cli/tests/node_compat/test/parallel/test-dgram-listen-after-bind.js new file mode 100644 index 00000000000000..3b9fd9771902fe --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-listen-after-bind.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const socket = dgram.createSocket('udp4'); + +socket.bind(); + +let fired = false; +const timer = setTimeout(() => { + socket.close(); +}, 100); + +socket.on('listening', common.mustCall(() => { + clearTimeout(timer); + fired = true; + socket.close(); +})); + +socket.on('close', common.mustCall(() => { + assert(fired, 'listening should fire after bind'); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-msgsize.js b/cli/tests/node_compat/test/parallel/test-dgram-msgsize.js new file mode 100644 index 00000000000000..88587842d9094d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-msgsize.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +// Send a too big datagram. The destination doesn't matter because it's +// not supposed to get sent out anyway. +const buf = Buffer.allocUnsafe(256 * 1024); +const sock = dgram.createSocket('udp4'); +sock.send(buf, 0, buf.length, 12345, '127.0.0.1', common.mustCall(cb)); +function cb(err) { + assert(err instanceof Error); + assert.strictEqual(err.code, 'EMSGSIZE'); + assert.strictEqual(err.address, '127.0.0.1'); + assert.strictEqual(err.port, 12345); + assert.strictEqual(err.message, 'send EMSGSIZE 127.0.0.1:12345'); + sock.close(); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-oob-buffer.js b/cli/tests/node_compat/test/parallel/test-dgram-oob-buffer.js new file mode 100644 index 00000000000000..6ab59420c1f812 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-oob-buffer.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Some operating systems report errors when an UDP message is sent to an +// unreachable host. This error can be reported by sendto() and even by +// recvfrom(). Node should not propagate this error to the user. + +const common = require('../common'); +const dgram = require('dgram'); + +const socket = dgram.createSocket('udp4'); +const buf = Buffer.from([1, 2, 3, 4]); +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + const { address, port } = portGetter.address(); + portGetter.close(common.mustCall(() => { + socket.send(buf, 0, 0, port, address, common.mustNotCall()); + socket.send(buf, 0, 4, port, address, common.mustNotCall()); + socket.send(buf, 1, 3, port, address, common.mustNotCall()); + socket.send(buf, 3, 1, port, address, common.mustNotCall()); + // Since length of zero means nothing, don't error despite OOB. + socket.send(buf, 4, 0, port, address, common.mustNotCall()); + + socket.close(); + })); + })); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-recv-error.js b/cli/tests/node_compat/test/parallel/test-dgram-recv-error.js new file mode 100644 index 00000000000000..11b2633d8878de --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-recv-error.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const { kStateSymbol } = require('internal/dgram'); +const s = dgram.createSocket('udp4'); +const { handle } = s[kStateSymbol]; + +s.on('error', common.mustCall((err) => { + s.close(); + + // Don't check the full error message, as the errno is not important here. + assert.match(String(err), /^Error: recvmsg/); + assert.strictEqual(err.syscall, 'recvmsg'); +})); + +s.on('message', common.mustNotCall('no message should be received.')); +s.bind(common.mustCall(() => handle.onmessage(-1, handle, null, null))); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-bad-arguments.js b/cli/tests/node_compat/test/parallel/test-dgram-send-bad-arguments.js new file mode 100644 index 00000000000000..a465be0984fe0b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-bad-arguments.js @@ -0,0 +1,162 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const buf = Buffer.from('test'); +const host = '127.0.0.1'; +const sock = dgram.createSocket('udp4'); + +function checkArgs(connected) { + // First argument should be a buffer. + assert.throws( + () => { sock.send(); }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer" argument must be of type string or an instance ' + + 'of Buffer, TypedArray, or DataView. Received undefined' + } + ); + + // send(buf, offset, length, port, host) + if (connected) { + assert.throws( + () => { sock.send(buf, 1, 1, -1, host); }, + { + code: 'ERR_SOCKET_DGRAM_IS_CONNECTED', + name: 'Error', + message: 'Already connected' + } + ); + + assert.throws( + () => { sock.send(buf, 1, 1, 0, host); }, + { + code: 'ERR_SOCKET_DGRAM_IS_CONNECTED', + name: 'Error', + message: 'Already connected' + } + ); + + assert.throws( + () => { sock.send(buf, 1, 1, 65536, host); }, + { + code: 'ERR_SOCKET_DGRAM_IS_CONNECTED', + name: 'Error', + message: 'Already connected' + } + ); + + assert.throws( + () => { sock.send(buf, 1234, '127.0.0.1', common.mustNotCall()); }, + { + code: 'ERR_SOCKET_DGRAM_IS_CONNECTED', + name: 'Error', + message: 'Already connected' + } + ); + + const longArray = [1, 2, 3, 4, 5, 6, 7, 8]; + for (const input of ['hello', + Buffer.from('hello'), + Buffer.from('hello world').subarray(0, 5), + Buffer.from('hello world').subarray(4, 9), + Buffer.from('hello world').subarray(6), + new Uint8Array([1, 2, 3, 4, 5]), + new Uint8Array(longArray).subarray(0, 5), + new Uint8Array(longArray).subarray(2, 7), + new Uint8Array(longArray).subarray(3), + new DataView(new ArrayBuffer(5), 0), + new DataView(new ArrayBuffer(6), 1), + new DataView(new ArrayBuffer(7), 1, 5)]) { + assert.throws( + () => { sock.send(input, 6, 0); }, + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"offset" is outside of buffer bounds', + } + ); + + assert.throws( + () => { sock.send(input, 0, 6); }, + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"length" is outside of buffer bounds', + } + ); + + assert.throws( + () => { sock.send(input, 3, 4); }, + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: '"length" is outside of buffer bounds', + } + ); + } + } else { + assert.throws(() => { sock.send(buf, 1, 1, -1, host); }, RangeError); + assert.throws(() => { sock.send(buf, 1, 1, 0, host); }, RangeError); + assert.throws(() => { sock.send(buf, 1, 1, 65536, host); }, RangeError); + } + + // send(buf, port, host) + assert.throws( + () => { sock.send(23, 12345, host); }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer" argument must be of type string or an instance ' + + 'of Buffer, TypedArray, or DataView. Received type number (23)' + } + ); + + // send([buf1, ..], port, host) + assert.throws( + () => { sock.send([buf, 23], 12345, host); }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer list arguments" argument must be of type string ' + + 'or an instance of Buffer, TypedArray, or DataView. ' + + 'Received an instance of Array' + } + ); +} + +checkArgs(); +sock.connect(12345, common.mustCall(() => { + checkArgs(true); + sock.close(); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-empty-address.js b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-empty-address.js new file mode 100644 index 00000000000000..f04945597ceaf0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-empty-address.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.alloc(256, 'x'); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, () => client.send(buf, client.address().port, onMessage)); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js new file mode 100644 index 00000000000000..bb7095b5904f59 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.alloc(256, 'x'); +const offset = 20; +const len = buf.length - offset; + +const onMessage = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, () => client.send(buf, offset, len, + client.address().port, + onMessage)); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length.js b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length.js new file mode 100644 index 00000000000000..03bb52a17e3e1a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer-length.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); +const offset = 20; +const len = buf.length - offset; + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, () => client.send(buf, offset, len, + client.address().port, + '127.0.0.1', + messageSent)); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer.js b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer.js new file mode 100644 index 00000000000000..c486df86b57cc0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-buffer.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, () => client.send(buf, + client.address().port, + common.localhostIPv4, + onMessage)); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js new file mode 100644 index 00000000000000..33bb8d7f4e08fe --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', function() { + const port = this.address().port; + client.send([buf1, buf2], port, messageSent); +}); + +client.on('message', common.mustCall(function onMessage(buf) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer.js b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer.js new file mode 100644 index 00000000000000..3f6e793619650b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-multi-buffer.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustCall((err, bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', () => { + const port = client.address().port; + client.send([buf1, buf2], port, common.localhostIPv4, messageSent); +}); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-callback-recursive.js b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-recursive.js new file mode 100644 index 00000000000000..6752cbf4abfd61 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-callback-recursive.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); +const chunk = 'abc'; +let received = 0; +let sent = 0; +const limit = 10; +let async = false; +let port; + +function onsend() { + if (sent++ < limit) { + client.send(chunk, 0, chunk.length, port, common.localhostIPv4, onsend); + } else { + assert.strictEqual(async, true); + } +} + +client.on('listening', function() { + port = this.address().port; + + process.nextTick(() => { + async = true; + }); + + onsend(); +}); + +client.on('message', (buf, info) => { + received++; + if (received === limit) { + client.close(); + } +}); + +client.on('close', common.mustCall(function() { + assert.strictEqual(received, limit); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-cb-quelches-error.js b/cli/tests/node_compat/test/parallel/test-dgram-send-cb-quelches-error.js new file mode 100644 index 00000000000000..d2fd5af50dcc89 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-cb-quelches-error.js @@ -0,0 +1,47 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(cmorten): uncomment dns module code once dns.setServer() has been +// implemented + +'use strict'; +const common = require('../common'); +const mustCall = common.mustCall; +const assert = require('assert'); +const dgram = require('dgram'); +// const dns = require('dns'); + +const socket = dgram.createSocket('udp4'); +const buffer = Buffer.from('gary busey'); + +// dns.setServers([]); + +socket.once('error', onEvent); + +// assert that: +// * callbacks act as "error" listeners if given. +// * error is never emitter for missing dns entries +// if a callback that handles error is present +// * error is emitted if a callback with no argument is passed +socket.send(buffer, 0, buffer.length, 100, + 'dne.example.com', mustCall(callbackOnly)); + +function callbackOnly(err) { + assert.ok(err); + socket.removeListener('error', onEvent); + socket.on('error', mustCall(onError)); + socket.send(buffer, 0, buffer.length, 100, 'dne.invalid'); +} + +function onEvent(err) { + assert.fail(`Error should not be emitted if there is callback: ${err}`); +} + +function onError(err) { + assert.ok(err); + socket.close(); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-default-host.js b/cli/tests/node_compat/test/parallel/test-dgram-send-default-host.js new file mode 100644 index 00000000000000..694531347cfa33 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-default-host.js @@ -0,0 +1,79 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const toSend = [Buffer.alloc(256, 'x'), + Buffer.alloc(256, 'y'), + Buffer.alloc(256, 'z'), + 'hello']; + +const received = []; +let totalBytesSent = 0; +let totalBytesReceived = 0; +const arrayBufferViewsCount = common.getArrayBufferViews( + Buffer.from('') +).length; + +client.on('listening', common.mustCall(() => { + const port = client.address().port; + + client.send(toSend[0], 0, toSend[0].length, port); + client.send(toSend[1], port); + client.send([toSend[2]], port); + client.send(toSend[3], 0, toSend[3].length, port); + + totalBytesSent += toSend.map((buf) => buf.length) + .reduce((a, b) => a + b, 0); + + for (const msgBuf of common.getArrayBufferViews(toSend[0])) { + client.send(msgBuf, 0, msgBuf.byteLength, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[1])) { + client.send(msgBuf, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[2])) { + client.send([msgBuf], port); + totalBytesSent += msgBuf.byteLength; + } +})); + +client.on('message', common.mustCall((buf, info) => { + received.push(buf.toString()); + totalBytesReceived += info.size; + + if (totalBytesReceived === totalBytesSent) { + client.close(); + } + // For every buffer in `toSend`, we send the raw Buffer, + // as well as every TypedArray in getArrayBufferViews() +}, toSend.length + (toSend.length - 1) * arrayBufferViewsCount)); + +client.on('close', common.mustCall((buf, info) => { + // The replies may arrive out of order -> sort them before checking. + received.sort(); + + const repeated = [...toSend]; + for (let i = 0; i < arrayBufferViewsCount; i++) { + repeated.push(...toSend.slice(0, 3)); + } + + assert.strictEqual(totalBytesSent, totalBytesReceived); + + const expected = repeated.map(String).sort(); + assert.deepStrictEqual(received, expected); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-empty-array.js b/cli/tests/node_compat/test/parallel/test-dgram-send-empty-array.js new file mode 100644 index 00000000000000..6554cb501237ca --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-empty-array.js @@ -0,0 +1,32 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +let interval; + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.alloc(0); + assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); + clearInterval(interval); + client.close(); +})); + +client.on('listening', common.mustCall(function() { + interval = setInterval(function() { + client.send([], client.address().port, common.localhostIPv4); + }, 10); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js b/cli/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js new file mode 100644 index 00000000000000..55ed56e5b7473c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + const port = this.address().port; + + client.on('message', common.mustCall(function onMessage(buffer) { + assert.strictEqual(buffer.length, 0); + clearInterval(interval); + client.close(); + })); + + const buf = Buffer.alloc(0); + const interval = setInterval(function() { + client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall()); + }, 10); +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js b/cli/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js new file mode 100644 index 00000000000000..78789a71a61ce6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + + client.on('message', common.mustCall(callback)); + + const port = this.address().port; + const buf = Buffer.alloc(1); + + const interval = setInterval(function() { + client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall(callback)); + }, 10); + + function callback(firstArg) { + // If client.send() callback, firstArg should be null. + // If client.on('message') listener, firstArg should be a 0-length buffer. + if (firstArg instanceof Buffer) { + assert.strictEqual(firstArg.length, 0); + clearInterval(interval); + client.close(); + } + } +})); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-error.js b/cli/tests/node_compat/test/parallel/test-dgram-send-error.js new file mode 100644 index 00000000000000..409097d082b7de --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-error.js @@ -0,0 +1,77 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const { internalBinding } = require('internal/test/binding'); +const { UV_UNKNOWN } = internalBinding('uv'); +const { getSystemErrorName } = require('util'); +const { kStateSymbol } = require('internal/dgram'); +const mockError = new Error('mock DNS error'); + +function getSocket(callback) { + const socket = dgram.createSocket('udp4'); + + socket.on('message', common.mustNotCall('Should not receive any messages.')); + socket.bind(common.mustCall(() => { + socket[kStateSymbol].handle.lookup = function(address, callback) { + process.nextTick(callback, mockError); + }; + + callback(socket); + })); + return socket; +} + +getSocket((socket) => { + socket.on('error', common.mustCall((err) => { + socket.close(); + assert.strictEqual(err, mockError); + })); + + socket.send('foo', socket.address().port, 'localhost'); +}); + +getSocket((socket) => { + const callback = common.mustCall((err) => { + socket.close(); + assert.strictEqual(err, mockError); + }); + + socket.send('foo', socket.address().port, 'localhost', callback); +}); + +{ + const socket = dgram.createSocket('udp4'); + + socket.on('message', common.mustNotCall('Should not receive any messages.')); + + socket.bind(common.mustCall(() => { + const port = socket.address().port; + const callback = common.mustCall((err) => { + socket.close(); + assert.strictEqual(err.code, 'UNKNOWN'); + assert.strictEqual(getSystemErrorName(err.errno), 'UNKNOWN'); + assert.strictEqual(err.syscall, 'send'); + assert.strictEqual(err.address, common.localhostIPv4); + assert.strictEqual(err.port, port); + assert.strictEqual( + err.message, + `${err.syscall} ${err.code} ${err.address}:${err.port}` + ); + }); + + socket[kStateSymbol].handle.send = function() { + return UV_UNKNOWN; + }; + + socket.send('foo', port, common.localhostIPv4, callback); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-invalid-msg-type.js b/cli/tests/node_compat/test/parallel/test-dgram-send-invalid-msg-type.js new file mode 100644 index 00000000000000..49d79c46ad0b28 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-invalid-msg-type.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test ensures that a TypeError is raised when the argument to `send()` +// or `sendto()` is anything but a Buffer. +// https://github.com/nodejs/node-v0.x-archive/issues/4496 + +const assert = require('assert'); +const dgram = require('dgram'); + +// Should throw but not crash. +const socket = dgram.createSocket('udp4'); +assert.throws(function() { socket.send(true, 0, 1, 1, 'host'); }, TypeError); +assert.throws(function() { socket.sendto(5, 0, 1, 1, 'host'); }, TypeError); +socket.close(); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-multi-buffer-copy.js b/cli/tests/node_compat/test/parallel/test-dgram-send-multi-buffer-copy.js new file mode 100644 index 00000000000000..437c1dc349d6c3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-multi-buffer-copy.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const onMessage = common.mustCall(function(err, bytes) { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', function() { + const toSend = [buf1, buf2]; + client.send(toSend, this.address().port, common.localhostIPv4, onMessage); + toSend.splice(0, 2); +}); + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-send-multi-string-array.js b/cli/tests/node_compat/test/parallel/test-dgram-send-multi-string-array.js new file mode 100644 index 00000000000000..9754c90eb65d62 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-send-multi-string-array.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const socket = dgram.createSocket('udp4'); +const data = ['foo', 'bar', 'baz']; + +socket.on('message', common.mustCall((msg, rinfo) => { + socket.close(); + assert.deepStrictEqual(msg.toString(), data.join('')); +})); + +socket.bind(() => socket.send(data, socket.address().port, 'localhost')); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-socket-buffer-size.js b/cli/tests/node_compat/test/parallel/test-dgram-socket-buffer-size.js new file mode 100644 index 00000000000000..b2fc332626d485 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-socket-buffer-size.js @@ -0,0 +1,178 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const { inspect } = require('util'); +const { internalBinding } = require('internal/test/binding'); +const { + UV_EBADF, + UV_EINVAL, + UV_ENOTSOCK +} = internalBinding('uv'); + +// Note error test amendments from Node due to Deno formatting errors slightly +// differently. +function getExpectedError(type) { + const code = common.isWindows ? 'ENOTSOCK' : 'EBADF'; + const message = common.isWindows ? + 'socket operation on non-socket' : 'bad file descriptor'; + const errno = common.isWindows ? UV_ENOTSOCK : UV_EBADF; + const syscall = `uv_${type}_buffer_size`; + const suffix = common.isWindows ? + 'ENOTSOCK (socket operation on non-socket)' : 'EBADF (bad file descriptor)'; + const error = { + code: 'ERR_SOCKET_BUFFER_SIZE', + name: 'SystemError', + message: `Could not get or set buffer size: ${syscall} returned ${suffix}`, + info: { + code, + message, + errno, + syscall + } + }; + return error; +} + +{ + // Should throw error if the socket is never bound. + const errorObj = getExpectedError('send'); + + const socket = dgram.createSocket('udp4'); + + assert.throws(() => { + socket.setSendBufferSize(8192); + }, (err) => { + assert.strictEqual( + inspect(err).replace(/^ +at .*\n/gm, ""), + `ERR_SOCKET_BUFFER_SIZE [SystemError]: ${errorObj.message}\n` + + " code: 'ERR_SOCKET_BUFFER_SIZE',\n" + + " info: {\n" + + ` errno: ${errorObj.info.errno},\n` + + ` code: '${errorObj.info.code}',\n` + + ` message: '${errorObj.info.message}',\n` + + ` syscall: '${errorObj.info.syscall}'\n` + + " },\n" + + ` errno: [Getter/Setter],\n` + + ` syscall: [Getter/Setter]\n` + + "}" + ); + return true; + }); + + assert.throws(() => { + socket.getSendBufferSize(); + }, errorObj); +} + +{ + const socket = dgram.createSocket('udp4'); + + // Should throw error if the socket is never bound. + const errorObj = getExpectedError('recv'); + + assert.throws(() => { + socket.setRecvBufferSize(8192); + }, errorObj); + + assert.throws(() => { + socket.getRecvBufferSize(); + }, errorObj); +} + +{ + // Should throw error if invalid buffer size is specified + const errorObj = { + code: 'ERR_SOCKET_BAD_BUFFER_SIZE', + name: 'TypeError', + message: /^Buffer size must be a positive integer$/ + }; + + const badBufferSizes = [-1, Infinity, 'Doh!']; + + const socket = dgram.createSocket('udp4'); + + socket.bind(common.mustCall(() => { + badBufferSizes.forEach((badBufferSize) => { + assert.throws(() => { + socket.setRecvBufferSize(badBufferSize); + }, errorObj); + + assert.throws(() => { + socket.setSendBufferSize(badBufferSize); + }, errorObj); + }); + socket.close(); + })); +} + +{ + // Can set and get buffer sizes after binding the socket. + const socket = dgram.createSocket('udp4'); + + socket.bind(common.mustCall(() => { + socket.setRecvBufferSize(10000); + socket.setSendBufferSize(10000); + + // note: linux will double the buffer size + const expectedBufferSize = common.isLinux ? 20000 : 10000; + assert.strictEqual(socket.getRecvBufferSize(), expectedBufferSize); + assert.strictEqual(socket.getSendBufferSize(), expectedBufferSize); + socket.close(); + })); +} + +{ + const info = { + code: 'EINVAL', + message: 'invalid argument', + errno: UV_EINVAL, + syscall: 'uv_recv_buffer_size' + }; + const errorObj = { + code: 'ERR_SOCKET_BUFFER_SIZE', + name: 'SystemError', + message: 'Could not get or set buffer size: uv_recv_buffer_size ' + + 'returned EINVAL (invalid argument)', + info + }; + const socket = dgram.createSocket('udp4'); + socket.bind(common.mustCall(() => { + assert.throws(() => { + socket.setRecvBufferSize(2147483648); + }, errorObj); + socket.close(); + })); +} + +{ + const info = { + code: 'EINVAL', + message: 'invalid argument', + errno: UV_EINVAL, + syscall: 'uv_send_buffer_size' + }; + const errorObj = { + code: 'ERR_SOCKET_BUFFER_SIZE', + name: 'SystemError', + message: 'Could not get or set buffer size: uv_send_buffer_size ' + + 'returned EINVAL (invalid argument)', + info + }; + const socket = dgram.createSocket('udp4'); + socket.bind(common.mustCall(() => { + assert.throws(() => { + socket.setSendBufferSize(2147483648); + }, errorObj); + socket.close(); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-dgram-udp4.js b/cli/tests/node_compat/test/parallel/test-dgram-udp4.js new file mode 100644 index 00000000000000..ebe5c2169e7c78 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-udp4.js @@ -0,0 +1,59 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const message_to_send = 'A message to send'; + +const server = dgram.createSocket('udp4'); +server.on('message', common.mustCall((msg, rinfo) => { + assert.strictEqual(rinfo.address, common.localhostIPv4); + assert.strictEqual(msg.toString(), message_to_send.toString()); + server.send(msg, 0, msg.length, rinfo.port, rinfo.address); +})); +server.on('listening', common.mustCall(() => { + const client = dgram.createSocket('udp4'); + const port = server.address().port; + client.on('message', common.mustCall((msg, rinfo) => { + assert.strictEqual(rinfo.address, common.localhostIPv4); + assert.strictEqual(rinfo.port, port); + assert.strictEqual(msg.toString(), message_to_send.toString()); + client.close(); + server.close(); + })); + client.send(message_to_send, + 0, + message_to_send.length, + port, + 'localhost'); + client.on('close', common.mustCall()); +})); +server.on('close', common.mustCall()); +server.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-udp6-link-local-address.js b/cli/tests/node_compat/test/parallel/test-dgram-udp6-link-local-address.js new file mode 100644 index 00000000000000..c828413a204825 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-udp6-link-local-address.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +if (!common.hasIPv6) + common.skip('no IPv6 support'); + +const assert = require('assert'); +const dgram = require('dgram'); +const os = require('os'); + +const { isWindows } = common; + +function linklocal() { + for (const [ifname, entries] of Object.entries(os.networkInterfaces())) { + for (const { address, family, scopeid } of entries) { + if (family === 'IPv6' && address.startsWith('fe80:')) { + return { address, ifname, scopeid }; + } + } + } +} +const iface = linklocal(); + +if (!iface) + common.skip('cannot find any IPv6 interfaces with a link local address'); + +const address = isWindows ? iface.address : `${iface.address}%${iface.ifname}`; +const message = 'Hello, local world!'; + +// Create a client socket for sending to the link-local address. +const client = dgram.createSocket('udp6'); + +// Create the server socket listening on the link-local address. +const server = dgram.createSocket('udp6'); + +server.on('listening', common.mustCall(() => { + const port = server.address().port; + client.send(message, 0, message.length, port, address); +})); + +server.on('message', common.mustCall((buf, info) => { + const received = buf.toString(); + assert.strictEqual(received, message); + // Check that the sender address is the one bound, + // including the link local scope identifier. + // TODO(cmorten): info.address is missing the link local scope identifier + // assert.strictEqual( + // info.address, + // isWindows ? `${iface.address}%${iface.scopeid}` : address + // ); + server.close(); + client.close(); +}, 1)); + +server.bind({ address }); diff --git a/cli/tests/node_compat/test/parallel/test-dgram-udp6-send-default-host.js b/cli/tests/node_compat/test/parallel/test-dgram-udp6-send-default-host.js new file mode 100644 index 00000000000000..94c9983ea859de --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dgram-udp6-send-default-host.js @@ -0,0 +1,83 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +if (!common.hasIPv6) + common.skip('no IPv6 support'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp6'); + +const toSend = [Buffer.alloc(256, 'x'), + Buffer.alloc(256, 'y'), + Buffer.alloc(256, 'z'), + 'hello']; + +const received = []; +let totalBytesSent = 0; +let totalBytesReceived = 0; +const arrayBufferViewLength = common.getArrayBufferViews( + Buffer.from('') +).length; + +client.on('listening', common.mustCall(() => { + const port = client.address().port; + + client.send(toSend[0], 0, toSend[0].length, port); + client.send(toSend[1], port); + client.send([toSend[2]], port); + client.send(toSend[3], 0, toSend[3].length, port); + + totalBytesSent += toSend.map((buf) => buf.length) + .reduce((a, b) => a + b, 0); + + for (const msgBuf of common.getArrayBufferViews(toSend[0])) { + client.send(msgBuf, 0, msgBuf.byteLength, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[1])) { + client.send(msgBuf, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[2])) { + client.send([msgBuf], port); + totalBytesSent += msgBuf.byteLength; + } +})); + +client.on('message', common.mustCall((buf, info) => { + received.push(buf.toString()); + totalBytesReceived += info.size; + + if (totalBytesReceived === totalBytesSent) { + client.close(); + } + // For every buffer in `toSend`, we send the raw Buffer, + // as well as every TypedArray in getArrayBufferViews() +}, toSend.length + (toSend.length - 1) * arrayBufferViewLength)); + +client.on('close', common.mustCall((buf, info) => { + // The replies may arrive out of order -> sort them before checking. + received.sort(); + + const repeated = [...toSend]; + for (let i = 0; i < arrayBufferViewLength; i++) { + // We get arrayBufferViews only for toSend[0..2]. + repeated.push(...toSend.slice(0, 3)); + } + + assert.strictEqual(totalBytesSent, totalBytesReceived); + + const expected = repeated.map(String).sort(); + assert.deepStrictEqual(received, expected); +})); + +client.bind(0); diff --git a/cli/tests/node_compat/test/parallel/test-diagnostics-channel-has-subscribers.js b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-has-subscribers.js new file mode 100644 index 00000000000000..bc6ff51e5f3c65 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-has-subscribers.js @@ -0,0 +1,17 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const { channel, hasSubscribers } = require('diagnostics_channel'); + +const dc = channel('test'); +assert.ok(!hasSubscribers('test')); + +dc.subscribe(() => {}); +assert.ok(hasSubscribers('test')); diff --git a/cli/tests/node_compat/test/parallel/test-diagnostics-channel-net.js b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-net.js new file mode 100644 index 00000000000000..05405efdea6e38 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-net.js @@ -0,0 +1,32 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const dc = require('diagnostics_channel'); + +const isNetSocket = (socket) => socket instanceof net.Socket; + +dc.subscribe('net.client.socket', common.mustCall(({ socket }) => { + assert.strictEqual(isNetSocket(socket), true); +})); + +dc.subscribe('net.server.socket', common.mustCall(({ socket }) => { + assert.strictEqual(isNetSocket(socket), true); +})); + +const server = net.createServer(common.mustCall((socket) => { + socket.destroy(); + server.close(); +})); + +server.listen(() => { + const { port } = server.address(); + net.connect(port); +}); diff --git a/cli/tests/node_compat/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js new file mode 100644 index 00000000000000..f1edec7098379e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const { Channel } = dc; + +const input = { + foo: 'bar' +}; + +// Should not have named channel +assert.ok(!dc.hasSubscribers('test')); + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel('test'); +assert.ok(channel instanceof Channel); + +// No subscribers yet, should not publish +assert.ok(!channel.hasSubscribers); + +const subscriber = common.mustCall((message, name) => { + assert.strictEqual(name, channel.name); + assert.deepStrictEqual(message, input); +}); + +// Now there's a subscriber, should publish +channel.subscribe(subscriber); +assert.ok(channel.hasSubscribers); + +// The ActiveChannel prototype swap should not fail instanceof +assert.ok(channel instanceof Channel); + +// Should trigger the subscriber once +channel.publish(input); + +// Should not publish after subscriber is unsubscribed +assert.ok(channel.unsubscribe(subscriber)); +assert.ok(!channel.hasSubscribers); + +// unsubscribe() should return false when subscriber is not found +assert.ok(!channel.unsubscribe(subscriber)); + +assert.throws(() => { + channel.subscribe(null); +}, { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/cli/tests/node_compat/test/parallel/test-diagnostics-channel-pub-sub.js b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-pub-sub.js new file mode 100644 index 00000000000000..c4c684c0794ecc --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-pub-sub.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const { Channel } = dc; + +const name = 'test'; +const input = { + foo: 'bar' +}; + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel(name); +assert.ok(channel instanceof Channel); + +// No subscribers yet, should not publish +assert.ok(!channel.hasSubscribers); + +const subscriber = common.mustCall((message, name) => { + assert.strictEqual(name, channel.name); + assert.deepStrictEqual(message, input); +}); + +// Now there's a subscriber, should publish +dc.subscribe(name, subscriber); +assert.ok(channel.hasSubscribers); + +// The ActiveChannel prototype swap should not fail instanceof +assert.ok(channel instanceof Channel); + +// Should trigger the subscriber once +channel.publish(input); + +// Should not publish after subscriber is unsubscribed +assert.ok(dc.unsubscribe(name, subscriber)); +assert.ok(!channel.hasSubscribers); + +// unsubscribe() should return false when subscriber is not found +assert.ok(!dc.unsubscribe(name, subscriber)); + +assert.throws(() => { + dc.subscribe(name, null); +}, { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/cli/tests/node_compat/test/parallel/test-diagnostics-channel-symbol-named.js b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-symbol-named.js new file mode 100644 index 00000000000000..1f241a0d208856 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-symbol-named.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const input = { + foo: 'bar' +}; + +const symbol = Symbol('test'); + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel(symbol); + +// Expect two successful publishes later +channel.subscribe(common.mustCall((message, name) => { + assert.strictEqual(name, symbol); + assert.deepStrictEqual(message, input); +})); + +channel.publish(input); + +{ + assert.throws(() => { + dc.channel(null); + }, /ERR_INVALID_ARG_TYPE/); +} diff --git a/cli/tests/node_compat/test/parallel/test-diagnostics-channel-udp.js b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-udp.js new file mode 100644 index 00000000000000..557562f4fef2ae --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-diagnostics-channel-udp.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const dc = require('diagnostics_channel'); + +const udpSocketChannel = dc.channel('udp.socket'); + +const isUDPSocket = (socket) => socket instanceof dgram.Socket; + +udpSocketChannel.subscribe(common.mustCall(({ socket }) => { + assert.strictEqual(isUDPSocket(socket), true); +})); +const socket = dgram.createSocket('udp4'); +socket.close(); diff --git a/cli/tests/node_compat/test/parallel/test-dns-lookup.js b/cli/tests/node_compat/test/parallel/test-dns-lookup.js new file mode 100644 index 00000000000000..d137586d238793 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns-lookup.js @@ -0,0 +1,179 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +// TODO: enable remaining tests once functionality is implemented. + +const common = require('../common'); +const assert = require('assert'); +// const { internalBinding } = require('internal/test/binding'); +// const cares = internalBinding('cares_wrap'); + +// Stub `getaddrinfo` to *always* error. This has to be done before we load the +// `dns` module to guarantee that the `dns` module uses the stub. +// cares.getaddrinfo = () => internalBinding('uv').UV_ENOMEM; + +const dns = require('dns'); +const dnsPromises = dns.promises; + +{ + const err = { + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", + message: + /^The "hostname" argument must be of type string\. Received type number/, + }; + + assert.throws(() => dns.lookup(1, {}), err); + assert.throws(() => dnsPromises.lookup(1, {}), err); +} + +// This also verifies different expectWarning notations. +// common.expectWarning({ +// // For 'internal/test/binding' module. +// 'internal/test/binding': [ +// 'These APIs are for internal testing only. Do not use them.', +// ], +// // For calling `dns.lookup` with falsy `hostname`. +// 'DeprecationWarning': { +// DEP0118: 'The provided hostname "false" is not a valid ' + +// 'hostname, and is supported in the dns module solely for compatibility.' +// } +// }); + +assert.throws( + () => { + dns.lookup(false, "cb"); + }, + { + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", + } +); + +assert.throws( + () => { + dns.lookup(false, "options", "cb"); + }, + { + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", + } +); + +{ + const err = { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: "The argument 'hints' is invalid. Received 100" + }; + const options = { + hints: 100, + family: 0, + all: false + }; + + assert.throws(() => { dnsPromises.lookup(false, options); }, err); + assert.throws(() => { + dns.lookup(false, options, common.mustNotCall()); + }, err); +} + +{ + const family = 20; + const err = { + code: "ERR_INVALID_ARG_VALUE", + name: "TypeError", + message: `The property 'options.family' must be one of: 0, 4, 6. Received ${family}`, + }; + const options = { + hints: 0, + family, + all: false + }; + + assert.throws(() => { dnsPromises.lookup(false, options); }, err); + assert.throws(() => { + dns.lookup(false, options, common.mustNotCall()); + }, err); +} + +(async function() { + let res; + + res = await dnsPromises.lookup(false, { + hints: 0, + family: 0, + all: true + }); + assert.deepStrictEqual(res, []); + + res = await dnsPromises.lookup('127.0.0.1', { + hints: 0, + family: 4, + all: true + }); + assert.deepStrictEqual(res, [{ address: '127.0.0.1', family: 4 }]); + + res = await dnsPromises.lookup('127.0.0.1', { + hints: 0, + family: 4, + all: false + }); + assert.deepStrictEqual(res, { address: '127.0.0.1', family: 4 }); +})().then(common.mustCall()); + +dns.lookup(false, { + hints: 0, + family: 0, + all: true +}, common.mustSucceed((result, addressType) => { + assert.deepStrictEqual(result, []); + assert.strictEqual(addressType, undefined); +})); + +dns.lookup('127.0.0.1', { + hints: 0, + family: 4, + all: true +}, common.mustSucceed((result, addressType) => { + assert.deepStrictEqual(result, [{ + address: '127.0.0.1', + family: 4 + }]); + assert.strictEqual(addressType, undefined); +})); + +dns.lookup('127.0.0.1', { + hints: 0, + family: 4, + all: false +}, common.mustSucceed((result, addressType) => { + assert.deepStrictEqual(result, '127.0.0.1'); + assert.strictEqual(addressType, 4); +})); + +// let tickValue = 0; + +// Should fail due to stub. +// dns.lookup('example.com', common.mustCall((error, result, addressType) => { +// assert(error); +// assert.strictEqual(tickValue, 1); +// assert.strictEqual(error.code, 'ENOMEM'); +// const descriptor = Object.getOwnPropertyDescriptor(error, 'message'); +// // The error message should be non-enumerable. +// assert.strictEqual(descriptor.enumerable, false); +// })); + +// Make sure that the error callback is called on next tick. +// tickValue = 1; + +// Should fail due to stub. +// assert.rejects(dnsPromises.lookup('example.com'), +// { code: 'ENOMEM', hostname: 'example.com' }); diff --git a/cli/tests/node_compat/test/parallel/test-dns-memory-error.js b/cli/tests/node_compat/test/parallel/test-dns-memory-error.js new file mode 100644 index 00000000000000..7d524414d9937d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns-memory-error.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +// Check that if libuv reports a memory error on a DNS query, that the memory +// error is passed through and not replaced with ENOTFOUND. + +require('../common'); + +const assert = require('assert'); +const errors = require('internal/errors'); +const { internalBinding } = require('internal/test/binding'); + +const { UV_EAI_MEMORY } = internalBinding('uv'); +const memoryError = errors.dnsException(UV_EAI_MEMORY, 'fhqwhgads'); + +assert.strictEqual(memoryError.code, 'EAI_MEMORY'); diff --git a/cli/tests/node_compat/test/parallel/test-dns-multi-channel.js b/cli/tests/node_compat/test/parallel/test-dns-multi-channel.js new file mode 100644 index 00000000000000..65d0a2f3a746e4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns-multi-channel.js @@ -0,0 +1,59 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const dnstools = require('../common/dns'); +const { Resolver } = require('dns'); +const assert = require('assert'); +const dgram = require('dgram'); + +const servers = [ + { + socket: dgram.createSocket('udp4'), + reply: { type: 'A', address: '1.2.3.4', ttl: 123, domain: 'example.org' } + }, + { + socket: dgram.createSocket('udp4'), + reply: { type: 'A', address: '5.6.7.8', ttl: 123, domain: 'example.org' } + }, +]; + +let waiting = servers.length; +for (const { socket, reply } of servers) { + socket.on('message', common.mustCall((msg, { address, port }) => { + const parsed = dnstools.parseDNSPacket(msg); + const domain = parsed.questions[0].domain; + assert.strictEqual(domain, 'example.org'); + + socket.send(dnstools.writeDNSPacket({ + id: parsed.id, + questions: parsed.questions, + answers: [reply], + }), port, address); + })); + + socket.bind(0, common.mustCall(() => { + if (--waiting === 0) ready(); + })); +} + + +function ready() { + const resolvers = servers.map((server) => ({ + server, + resolver: new Resolver() + })); + + for (const { server: { socket, reply }, resolver } of resolvers) { + resolver.setServers([`127.0.0.1:${socket.address().port}`]); + resolver.resolve4('example.org', common.mustSucceed((res) => { + assert.deepStrictEqual(res, [reply.address]); + socket.close(); + })); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-dns-promises-exists.js b/cli/tests/node_compat/test/parallel/test-dns-promises-exists.js new file mode 100644 index 00000000000000..4800d0775a200c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns-promises-exists.js @@ -0,0 +1,40 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const dnsPromises = require('dns/promises'); +const dns = require('dns'); + +assert.strictEqual(dnsPromises, dns.promises); + +assert.strictEqual(dnsPromises.NODATA, dns.NODATA); +assert.strictEqual(dnsPromises.FORMERR, dns.FORMERR); +assert.strictEqual(dnsPromises.SERVFAIL, dns.SERVFAIL); +assert.strictEqual(dnsPromises.NOTFOUND, dns.NOTFOUND); +assert.strictEqual(dnsPromises.NOTIMP, dns.NOTIMP); +assert.strictEqual(dnsPromises.REFUSED, dns.REFUSED); +assert.strictEqual(dnsPromises.BADQUERY, dns.BADQUERY); +assert.strictEqual(dnsPromises.BADNAME, dns.BADNAME); +assert.strictEqual(dnsPromises.BADFAMILY, dns.BADFAMILY); +assert.strictEqual(dnsPromises.BADRESP, dns.BADRESP); +assert.strictEqual(dnsPromises.CONNREFUSED, dns.CONNREFUSED); +assert.strictEqual(dnsPromises.TIMEOUT, dns.TIMEOUT); +assert.strictEqual(dnsPromises.EOF, dns.EOF); +assert.strictEqual(dnsPromises.FILE, dns.FILE); +assert.strictEqual(dnsPromises.NOMEM, dns.NOMEM); +assert.strictEqual(dnsPromises.DESTRUCTION, dns.DESTRUCTION); +assert.strictEqual(dnsPromises.BADSTR, dns.BADSTR); +assert.strictEqual(dnsPromises.BADFLAGS, dns.BADFLAGS); +assert.strictEqual(dnsPromises.NONAME, dns.NONAME); +assert.strictEqual(dnsPromises.BADHINTS, dns.BADHINTS); +assert.strictEqual(dnsPromises.NOTINITIALIZED, dns.NOTINITIALIZED); +assert.strictEqual(dnsPromises.LOADIPHLPAPI, dns.LOADIPHLPAPI); +assert.strictEqual(dnsPromises.ADDRGETNETWORKPARAMS, dns.ADDRGETNETWORKPARAMS); +assert.strictEqual(dnsPromises.CANCELLED, dns.CANCELLED); diff --git a/cli/tests/node_compat/test/parallel/test-dns-resolveany.js b/cli/tests/node_compat/test/parallel/test-dns-resolveany.js new file mode 100644 index 00000000000000..56d533ad99af04 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns-resolveany.js @@ -0,0 +1,78 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO: enable remaining tests once functionality is implemented. + +'use strict'; +const common = require('../common'); +const dnstools = require('../common/dns'); +const dns = require('dns'); +const assert = require('assert'); +const dgram = require('dgram'); +const dnsPromises = dns.promises; + +const answers = [ + { type: 'A', address: '1.2.3.4', /*ttl: 123*/ }, + { type: 'AAAA', address: '::42', /*ttl: 123*/ }, + { + type: 'CAA', + critical: 128, + issue: 'platynum.ch' + }, + { type: 'MX', priority: 42, exchange: 'foobar.com', ttl: 124 }, + { type: 'NS', value: 'foobar.org', ttl: 457 }, + { type: 'PTR', value: 'baz.org', ttl: 987 }, + { + type: 'SOA', + nsname: 'ns1.example.com', + hostmaster: 'admin.example.com', + serial: 156696742, + refresh: 900, + retry: 900, + expire: 1800, + minttl: 60 + }, + { type: 'TXT', entries: [ 'v=spf1 ~all', 'xyz\x00foo' ] }, +]; + +const server = dgram.createSocket('udp4'); + +server.on('message', common.mustCall((msg, { address, port }) => { + const parsed = dnstools.parseDNSPacket(msg); + const domain = parsed.questions[0].domain; + assert.strictEqual(domain, 'example.org'); + + server.send(dnstools.writeDNSPacket({ + id: parsed.id, + questions: parsed.questions, + answers: answers.map((answer) => Object.assign({ domain }, answer)), + }), port, address); +}, /*2*/ 30)); + +server.bind(0, common.mustCall(async () => { + const address = server.address(); + dns.setServers([`127.0.0.1:${address.port}`]); + + validateResults(await dnsPromises.resolveAny('example.org')); + + dns.resolveAny('example.org', common.mustSucceed((res) => { + validateResults(res); + server.close(); + })); +})); + +function validateResults(res) { + // TTL values are only provided for A and AAAA entries. + assert.deepStrictEqual(res.map(maybeRedactTTL), answers.map(maybeRedactTTL)); +} + +function maybeRedactTTL(r) { + const ret = { ...r }; + if (!['A', 'AAAA'].includes(r.type)) + delete ret.ttl; + return ret; +} diff --git a/cli/tests/node_compat/test/parallel/test-dns-resolvens-typeerror.js b/cli/tests/node_compat/test/parallel/test-dns-resolvens-typeerror.js new file mode 100644 index 00000000000000..c59fab974cbb99 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns-resolvens-typeerror.js @@ -0,0 +1,62 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test ensures `dns.resolveNs()` does not raise a C++-land assertion error +// and throw a JavaScript TypeError instead. +// Issue https://github.com/nodejs/node-v0.x-archive/issues/7070 + +const assert = require('assert'); +const dns = require('dns'); +const dnsPromises = dns.promises; + +assert.throws( + () => dnsPromises.resolveNs([]), // bad name + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "name" argument must be of type string/ + } +); +assert.throws( + () => dns.resolveNs([]), // bad name + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "name" argument must be of type string/ + } +); +assert.throws( + () => dns.resolveNs(''), // bad callback + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } +); diff --git a/cli/tests/node_compat/test/parallel/test-dns-setservers-type-check.js b/cli/tests/node_compat/test/parallel/test-dns-setservers-type-check.js new file mode 100644 index 00000000000000..f318df39cc720d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns-setservers-type-check.js @@ -0,0 +1,127 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { addresses } = require('../common/internet'); +const assert = require('assert'); +const dns = require('dns'); +const resolver = new dns.promises.Resolver(); +const dnsPromises = dns.promises; +const promiseResolver = new dns.promises.Resolver(); + +{ + [ + null, + undefined, + Number(addresses.DNS4_SERVER), + addresses.DNS4_SERVER, + { + address: addresses.DNS4_SERVER + }, + ].forEach((val) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "servers" argument must be an instance of Array.' + + common.invalidArgTypeHelper(val) + }; + assert.throws( + () => { + dns.setServers(val); + }, errObj + ); + assert.throws( + () => { + resolver.setServers(val); + }, errObj + ); + assert.throws( + () => { + dnsPromises.setServers(val); + }, errObj + ); + assert.throws( + () => { + promiseResolver.setServers(val); + }, errObj + ); + }); +} + +{ + [ + [null], + [undefined], + [Number(addresses.DNS4_SERVER)], + [ + { + address: addresses.DNS4_SERVER + }, + ], + ].forEach((val) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "servers[0]" argument must be of type string.' + + common.invalidArgTypeHelper(val[0]) + }; + assert.throws( + () => { + dns.setServers(val); + }, errObj + ); + assert.throws( + () => { + resolver.setServers(val); + }, errObj + ); + assert.throws( + () => { + dnsPromises.setServers(val); + }, errObj + ); + assert.throws( + () => { + promiseResolver.setServers(val); + }, errObj + ); + }); +} + +// This test for 'dns/promises' +{ + const { + setServers + } = require('dns/promises'); + + // This should not throw any error. + (async () => { + setServers([ '127.0.0.1' ]); + })().then(common.mustCall()); + + [ + [null], + [undefined], + [Number(addresses.DNS4_SERVER)], + [ + { + address: addresses.DNS4_SERVER + }, + ], + ].forEach((val) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "servers[0]" argument must be of type string.' + + common.invalidArgTypeHelper(val[0]) + }; + assert.throws(() => { + setServers(val); + }, errObj); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-dns.js b/cli/tests/node_compat/test/parallel/test-dns.js new file mode 100644 index 00000000000000..e56f7ca40a94cd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-dns.js @@ -0,0 +1,471 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// TODO: enable remaining tests once functionality is implemented. + +const common = require('../common'); +const dnstools = require('../common/dns'); +const assert = require('assert'); + +const dns = require('dns'); +const dnsPromises = dns.promises; +const dgram = require('dgram'); + +// TODO(cmorten): currently don't expose defaults +// const existing = dns.getServers(); +// assert(existing.length > 0); + +// Verify that setServers() handles arrays with holes and other oddities +{ + const servers = []; + + servers[0] = '127.0.0.1'; + servers[2] = '0.0.0.0'; + dns.setServers(servers); + + assert.deepStrictEqual(dns.getServers(), ['127.0.0.1', '0.0.0.0']); +} + +{ + const servers = ['127.0.0.1', '192.168.1.1']; + + servers[3] = '127.1.0.1'; + servers[4] = '127.1.0.1'; + servers[5] = '127.1.1.1'; + + Object.defineProperty(servers, 2, { + enumerable: true, + get: () => { + servers.length = 3; + return '0.0.0.0'; + } + }); + + dns.setServers(servers); + assert.deepStrictEqual(dns.getServers(), [ + '127.0.0.1', + '192.168.1.1', + '0.0.0.0', + ]); +} + +{ + // Various invalidities, all of which should throw a clean error. + const invalidServers = [ + ' ', + '\n', + '\0', + '1'.repeat(3 * 4), + // Check for REDOS issues. + ':'.repeat(100000), + '['.repeat(100000), + '['.repeat(100000) + ']'.repeat(100000) + 'a', + ]; + invalidServers.forEach((serv) => { + assert.throws( + () => { + dns.setServers([serv]); + }, + { + name: 'TypeError', + code: 'ERR_INVALID_IP_ADDRESS' + } + ); + }); +} + +const goog = [ + '8.8.8.8', + '8.8.4.4', +]; +dns.setServers(goog); +assert.deepStrictEqual(dns.getServers(), goog); +assert.throws(() => dns.setServers(['foobar']), { + code: 'ERR_INVALID_IP_ADDRESS', + name: 'TypeError', + message: 'Invalid IP address: foobar' +}); +assert.throws(() => dns.setServers(['127.0.0.1:va']), { + code: 'ERR_INVALID_IP_ADDRESS', + name: 'TypeError', + message: 'Invalid IP address: 127.0.0.1:va' +}); +assert.deepStrictEqual(dns.getServers(), goog); + +const goog6 = [ + '2001:4860:4860::8888', + '2001:4860:4860::8844', +]; +dns.setServers(goog6); +assert.deepStrictEqual(dns.getServers(), goog6); + +goog6.push('4.4.4.4'); +dns.setServers(goog6); +assert.deepStrictEqual(dns.getServers(), goog6); + +const ports = [ + '4.4.4.4:53', + '[2001:4860:4860::8888]:53', + '103.238.225.181:666', + '[fe80::483a:5aff:fee6:1f04]:666', + '[fe80::483a:5aff:fee6:1f04]', +]; +const portsExpected = [ + '4.4.4.4', + '2001:4860:4860::8888', + '103.238.225.181:666', + '[fe80::483a:5aff:fee6:1f04]:666', + 'fe80::483a:5aff:fee6:1f04', +]; +dns.setServers(ports); +assert.deepStrictEqual(dns.getServers(), portsExpected); + +dns.setServers([]); +assert.deepStrictEqual(dns.getServers(), []); + +{ + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "rrtype" argument must be of type string. ' + + 'Received an instance of Array' + }; + assert.throws(() => { + dns.resolve('example.com', [], common.mustNotCall()); + }, errObj); + assert.throws(() => { + dnsPromises.resolve('example.com', []); + }, errObj); +} +{ + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "name" argument must be of type string. ' + + 'Received undefined' + }; + assert.throws(() => { + dnsPromises.resolve(); + }, errObj); +} + +// dns.lookup should accept only falsey and string values +{ + const errorReg = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "hostname" argument must be of type string\. Received .*/ + }; + + assert.throws(() => dns.lookup({}, common.mustNotCall()), errorReg); + + assert.throws(() => dns.lookup([], common.mustNotCall()), errorReg); + + assert.throws(() => dns.lookup(true, common.mustNotCall()), errorReg); + + assert.throws(() => dns.lookup(1, common.mustNotCall()), errorReg); + + assert.throws(() => dns.lookup(common.mustNotCall(), common.mustNotCall()), + errorReg); + + assert.throws(() => dnsPromises.lookup({}), errorReg); + assert.throws(() => dnsPromises.lookup([]), errorReg); + assert.throws(() => dnsPromises.lookup(true), errorReg); + assert.throws(() => dnsPromises.lookup(1), errorReg); + assert.throws(() => dnsPromises.lookup(common.mustNotCall()), errorReg); +} + +// dns.lookup should accept falsey values +{ + const checkCallback = (err, address, family) => { + assert.ifError(err); + assert.strictEqual(address, null); + assert.strictEqual(family, 4); + }; + + ['', null, undefined, 0, NaN].forEach(async (value) => { + const res = await dnsPromises.lookup(value); + assert.deepStrictEqual(res, { address: null, family: 4 }); + dns.lookup(value, common.mustCall(checkCallback)); + }); +} + +{ + // Make sure that dns.lookup throws if hints does not represent a valid flag. + // (dns.V4MAPPED | dns.ADDRCONFIG | dns.ALL) + 1 is invalid because: + // - it's different from dns.V4MAPPED and dns.ADDRCONFIG and dns.ALL. + // - it's different from any subset of them bitwise ored. + // - it's different from 0. + // - it's an odd number different than 1, and thus is invalid, because + // flags are either === 1 or even. + const hints = (dns.V4MAPPED | dns.ADDRCONFIG | dns.ALL) + 1; + const err = { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: /The argument 'hints' is invalid\. Received \d+/ + }; + + assert.throws(() => { + dnsPromises.lookup('nodejs.org', { hints }); + }, err); + assert.throws(() => { + dns.lookup('nodejs.org', { hints }, common.mustNotCall()); + }, err); +} + +assert.throws(() => dns.lookup("nodejs.org"), { + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", +}); + +assert.throws(() => dns.lookup("nodejs.org", 4), { + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", +}); + +dns.lookup('', { family: 4, hints: 0 }, common.mustCall()); + +dns.lookup('', { + family: 6, + hints: dns.ADDRCONFIG +}, common.mustCall()); + +dns.lookup('', { hints: dns.V4MAPPED }, common.mustCall()); + +dns.lookup('', { + hints: dns.ADDRCONFIG | dns.V4MAPPED +}, common.mustCall()); + +dns.lookup('', { + hints: dns.ALL +}, common.mustCall()); + +dns.lookup('', { + hints: dns.V4MAPPED | dns.ALL +}, common.mustCall()); + +dns.lookup('', { + hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL +}, common.mustCall()); + +(async function() { + await dnsPromises.lookup('', { family: 4, hints: 0 }); + await dnsPromises.lookup('', { family: 6, hints: dns.ADDRCONFIG }); + await dnsPromises.lookup('', { hints: dns.V4MAPPED }); + await dnsPromises.lookup('', { hints: dns.ADDRCONFIG | dns.V4MAPPED }); + await dnsPromises.lookup('', { hints: dns.ALL }); + await dnsPromises.lookup('', { hints: dns.V4MAPPED | dns.ALL }); + await dnsPromises.lookup('', { + hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL + }); +})().then(common.mustCall()); + +// { +// const err = { +// code: 'ERR_MISSING_ARGS', +// name: 'TypeError', +// message: 'The "address", "port", and "callback" arguments must be ' + +// 'specified' +// }; + +// assert.throws(() => dns.lookupService('0.0.0.0'), err); +// err.message = 'The "address" and "port" arguments must be specified'; +// assert.throws(() => dnsPromises.lookupService('0.0.0.0'), err); +// } + +// { +// const invalidAddress = 'fasdfdsaf'; +// const err = { +// code: 'ERR_INVALID_ARG_VALUE', +// name: 'TypeError', +// message: `The argument 'address' is invalid. Received '${invalidAddress}'` +// }; + +// assert.throws(() => { +// dnsPromises.lookupService(invalidAddress, 0); +// }, err); + +// assert.throws(() => { +// dns.lookupService(invalidAddress, 0, common.mustNotCall()); +// }, err); +// } + +// const portErr = (port) => { +// const err = { +// code: 'ERR_SOCKET_BAD_PORT', +// message: +// `Port should be >= 0 and < 65536. Received ${port}.`, +// name: 'RangeError' +// }; + +// assert.throws(() => { +// dnsPromises.lookupService('0.0.0.0', port); +// }, err); + +// assert.throws(() => { +// dns.lookupService('0.0.0.0', port, common.mustNotCall()); +// }, err); +// }; +// portErr(null); +// portErr(undefined); +// portErr(65538); +// portErr('test'); + +// assert.throws(() => { +// dns.lookupService('0.0.0.0', 80, null); +// }, { +// code: 'ERR_INVALID_ARG_TYPE', +// name: 'TypeError' +// }); + +{ + dns.resolveMx('foo.onion', function(err) { + assert.deepStrictEqual(err.code, 'ENOTFOUND'); + assert.deepStrictEqual(err.syscall, 'queryMx'); + assert.deepStrictEqual(err.hostname, 'foo.onion'); + assert.deepStrictEqual(err.message, 'queryMx ENOTFOUND foo.onion'); + }); +} + +{ + const cases = [ + { + method: "resolveAny", + answers: [ + { type: "A", address: "1.2.3.4" /*ttl: 3333333333*/ }, + { type: "AAAA", address: "::42" /*ttl: 3333333333*/ }, + { type: "MX", priority: 42, exchange: "foobar.com", ttl: 3333333333 }, + { type: "NS", value: "foobar.org", ttl: 3333333333 }, + { type: "PTR", value: "baz.org", ttl: 3333333333 }, + { + type: "SOA", + nsname: "ns1.example.com", + hostmaster: "admin.example.com", + serial: 3210987654, + refresh: 900, + retry: 900, + expire: 1800, + minttl: 3333333333, + }, + ], + }, + + // TODO(cmorten): support ttl option + // { + // method: "resolve4", + // options: { ttl: true }, + // answers: [{ type: "A", address: "1.2.3.4", ttl: 3333333333 }], + // }, + + // { + // method: "resolve6", + // options: { ttl: true }, + // answers: [{ type: "AAAA", address: "::42", ttl: 3333333333 }], + // }, + + { + method: "resolveSoa", + answers: [ + { + type: "SOA", + nsname: "ns1.example.com", + hostmaster: "admin.example.com", + serial: 3210987654, + refresh: 900, + retry: 900, + expire: 1800, + minttl: 3333333333, + }, + ], + }, + ]; + + const server = dgram.createSocket('udp4'); + + server.on('message', common.mustCall((msg, { address, port }) => { + const parsed = dnstools.parseDNSPacket(msg); + const domain = parsed.questions[0].domain; + assert.strictEqual(domain, 'example.org'); + + server.send(dnstools.writeDNSPacket({ + id: parsed.id, + questions: parsed.questions, + answers: cases[0].answers.map( + (answer) => Object.assign({ domain }, answer) + ), + }), port, address); + // Don't have "ANY" query type available so calls greatly increased with + // polyfill method. + }, /*cases.length * 2*/ 32)); + + server.bind(0, common.mustCall(() => { + const address = server.address(); + dns.setServers([`127.0.0.1:${address.port}`]); + + function validateResults(res) { + if (!Array.isArray(res)) + res = [res]; + + assert.deepStrictEqual(res.map(tweakEntry), + cases[0].answers.map(tweakEntry)); + } + + function tweakEntry(r) { + const ret = { ...r }; + + const { method } = cases[0]; + + // TTL values are only provided for A and AAAA entries. + if (!['A', 'AAAA'].includes(ret.type) && !/^resolve(4|6)?$/.test(method)) + delete ret.ttl; + + if (method !== 'resolveAny') + delete ret.type; + + return ret; + } + + (async function nextCase() { + if (cases.length === 0) + return server.close(); + + const { method, options } = cases[0]; + + validateResults(await dnsPromises[method]('example.org', options)); + + dns[method]('example.org', options, common.mustSucceed((res) => { + validateResults(res); + cases.shift(); + nextCase(); + })); + })().then(common.mustCall()); + + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-eval-strict-referenceerror.js b/cli/tests/node_compat/test/parallel/test-eval-strict-referenceerror.js new file mode 100644 index 00000000000000..f63fb2840287e5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-eval-strict-referenceerror.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +/* eslint-disable strict */ +require('../common'); + +// In Node.js 0.10, a bug existed that caused strict functions to not capture +// their environment when evaluated. When run in 0.10 `test()` fails with a +// `ReferenceError`. See https://github.com/nodejs/node/issues/2245 for details. + +const assert = require('assert'); + +function test() { + + const code = [ + 'var foo = {m: 1};', + '', + 'function bar() {', + '\'use strict\';', + 'return foo; // foo isn\'t captured in 0.10', + '};', + ].join('\n'); + + eval(code); + + return bar(); // eslint-disable-line no-undef + +} + +assert.deepStrictEqual(test(), { m: 1 }); diff --git a/cli/tests/node_compat/test/parallel/test-eval.js b/cli/tests/node_compat/test/parallel/test-eval.js new file mode 100644 index 00000000000000..22738c0970d712 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-eval.js @@ -0,0 +1,14 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Verify that eval is allowed by default. +assert.strictEqual(eval('"eval"'), 'eval'); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-add-listeners.js b/cli/tests/node_compat/test/parallel/test-event-emitter-add-listeners.js new file mode 100644 index 00000000000000..28853cff224b4b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-add-listeners.js @@ -0,0 +1,93 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +{ + const ee = new EventEmitter(); + const events_new_listener_emitted = []; + const listeners_new_listener_emitted = []; + + // Sanity check + assert.strictEqual(ee.addListener, ee.on); + + ee.on('newListener', function(event, listener) { + // Don't track newListener listeners. + if (event === 'newListener') + return; + + events_new_listener_emitted.push(event); + listeners_new_listener_emitted.push(listener); + }); + + const hello = common.mustCall(function(a, b) { + assert.strictEqual(a, 'a'); + assert.strictEqual(b, 'b'); + }); + + ee.once('newListener', function(name, listener) { + assert.strictEqual(name, 'hello'); + assert.strictEqual(listener, hello); + assert.deepStrictEqual(this.listeners('hello'), []); + }); + + ee.on('hello', hello); + ee.once('foo', assert.fail); + assert.deepStrictEqual(['hello', 'foo'], events_new_listener_emitted); + assert.deepStrictEqual([hello, assert.fail], listeners_new_listener_emitted); + + ee.emit('hello', 'a', 'b'); +} + +// Just make sure that this doesn't throw: +{ + const f = new EventEmitter(); + + f.setMaxListeners(0); +} + +{ + const listen1 = () => {}; + const listen2 = () => {}; + const ee = new EventEmitter(); + + ee.once('newListener', function() { + assert.deepStrictEqual(ee.listeners('hello'), []); + ee.once('newListener', function() { + assert.deepStrictEqual(ee.listeners('hello'), []); + }); + ee.on('hello', listen2); + }); + ee.on('hello', listen1); + // The order of listeners on an event is not always the order in which the + // listeners were added. + assert.deepStrictEqual(ee.listeners('hello'), [listen2, listen1]); +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-check-listener-leaks.js b/cli/tests/node_compat/test/parallel/test-event-emitter-check-listener-leaks.js new file mode 100644 index 00000000000000..977667abda3034 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-check-listener-leaks.js @@ -0,0 +1,110 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); + +// default +{ + const e = new events.EventEmitter(); + + for (let i = 0; i < 10; i++) { + e.on('default', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.default, 'warned')); + e.on('default', common.mustNotCall()); + assert.ok(e._events.default.warned); + + // symbol + const symbol = Symbol('symbol'); + e.setMaxListeners(1); + e.on(symbol, common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events[symbol], 'warned')); + e.on(symbol, common.mustNotCall()); + assert.ok(Object.hasOwn(e._events[symbol], 'warned')); + + // specific + e.setMaxListeners(5); + for (let i = 0; i < 5; i++) { + e.on('specific', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.specific, 'warned')); + e.on('specific', common.mustNotCall()); + assert.ok(e._events.specific.warned); + + // only one + e.setMaxListeners(1); + e.on('only one', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events['only one'], 'warned')); + e.on('only one', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events['only one'], 'warned')); + + // unlimited + e.setMaxListeners(0); + for (let i = 0; i < 1000; i++) { + e.on('unlimited', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.unlimited, 'warned')); +} + +// process-wide +{ + events.EventEmitter.defaultMaxListeners = 42; + const e = new events.EventEmitter(); + + for (let i = 0; i < 42; ++i) { + e.on('fortytwo', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.fortytwo, 'warned')); + e.on('fortytwo', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.fortytwo, 'warned')); + delete e._events.fortytwo.warned; + + events.EventEmitter.defaultMaxListeners = 44; + e.on('fortytwo', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events.fortytwo, 'warned')); + e.on('fortytwo', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.fortytwo, 'warned')); +} + +// But _maxListeners still has precedence over defaultMaxListeners +{ + events.EventEmitter.defaultMaxListeners = 42; + const e = new events.EventEmitter(); + e.setMaxListeners(1); + e.on('uno', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events.uno, 'warned')); + e.on('uno', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.uno, 'warned')); + + // chainable + assert.strictEqual(e, e.setMaxListeners(1)); +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-emit-context.js b/cli/tests/node_compat/test/parallel/test-event-emitter-emit-context.js new file mode 100644 index 00000000000000..b18c3d2ed0a9d1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-emit-context.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +// Test emit called by other context +const EE = new EventEmitter(); + +// Works as expected if the context has no `constructor.name` +{ + const ctx = Object.create(null); + assert.throws( + () => EE.emit.call(ctx, 'error', new Error('foo')), + common.expectsError({ name: 'Error', message: 'foo' }) + ); +} + +assert.strictEqual(EE.emit.call({}, 'foo'), false); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-error-monitor.js b/cli/tests/node_compat/test/parallel/test-event-emitter-error-monitor.js new file mode 100644 index 00000000000000..d708393d9aa9c9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-error-monitor.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const EE = new EventEmitter(); +const theErr = new Error('MyError'); + +EE.on( + EventEmitter.errorMonitor, + common.mustCall(function onErrorMonitor(e) { + assert.strictEqual(e, theErr); + }, 3) +); + +// Verify with no error listener +assert.throws( + () => EE.emit('error', theErr), theErr +); + +// Verify with error listener +EE.once('error', common.mustCall((e) => assert.strictEqual(e, theErr))); +EE.emit('error', theErr); + + +// Verify it works with once +process.nextTick(() => EE.emit('error', theErr)); +assert.rejects(EventEmitter.once(EE, 'notTriggered'), theErr); + +// Only error events trigger error monitor +EE.on('aEvent', common.mustCall()); +EE.emit('aEvent'); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-errors.js b/cli/tests/node_compat/test/parallel/test-event-emitter-errors.js new file mode 100644 index 00000000000000..8ab863f893e498 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-errors.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); +const util = require('util'); + +const EE = new EventEmitter(); + +assert.throws( + () => EE.emit('error', 'Accepts a string'), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: "Unhandled error. ('Accepts a string')" + } +); + +assert.throws( + () => EE.emit('error', { message: 'Error!' }), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: "Unhandled error. ({ message: 'Error!' })" + } +); + +assert.throws( + () => EE.emit('error', { + message: 'Error!', + [util.inspect.custom]() { throw new Error(); } + }), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: 'Unhandled error. ([object Object])' + } +); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-get-max-listeners.js b/cli/tests/node_compat/test/parallel/test-event-emitter-get-max-listeners.js new file mode 100644 index 00000000000000..7af05126a89e06 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-get-max-listeners.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const emitter = new EventEmitter(); + +assert.strictEqual(emitter.getMaxListeners(), EventEmitter.defaultMaxListeners); + +emitter.setMaxListeners(0); +assert.strictEqual(emitter.getMaxListeners(), 0); + +emitter.setMaxListeners(3); +assert.strictEqual(emitter.getMaxListeners(), 3); + +// https://github.com/nodejs/node/issues/523 - second call should not throw. +const recv = {}; +EventEmitter.prototype.on.call(recv, 'event', () => {}); +EventEmitter.prototype.on.call(recv, 'event', () => {}); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-invalid-listener.js b/cli/tests/node_compat/test/parallel/test-event-emitter-invalid-listener.js new file mode 100644 index 00000000000000..c5c948c625b686 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-invalid-listener.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const eventsMethods = ['on', 'once', 'removeListener', 'prependOnceListener']; + +// Verify that the listener must be a function for events methods +for (const method of eventsMethods) { + assert.throws(() => { + const ee = new EventEmitter(); + ee[method]('foo', null); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "listener" argument must be of type function. ' + + 'Received null' + }, `event.${method}('foo', null) should throw the proper error`); +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-listener-count.js b/cli/tests/node_compat/test/parallel/test-event-emitter-listener-count.js new file mode 100644 index 00000000000000..17aa9e58aac9cb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-listener-count.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const emitter = new EventEmitter(); +emitter.on('foo', () => {}); +emitter.on('foo', () => {}); +emitter.on('baz', () => {}); +// Allow any type +emitter.on(123, () => {}); + +assert.strictEqual(EventEmitter.listenerCount(emitter, 'foo'), 2); +assert.strictEqual(emitter.listenerCount('foo'), 2); +assert.strictEqual(emitter.listenerCount('bar'), 0); +assert.strictEqual(emitter.listenerCount('baz'), 1); +assert.strictEqual(emitter.listenerCount(123), 1); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-listeners-side-effects.js b/cli/tests/node_compat/test/parallel/test-event-emitter-listeners-side-effects.js new file mode 100644 index 00000000000000..d6b1b5ac3583bf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-listeners-side-effects.js @@ -0,0 +1,67 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const EventEmitter = require('events').EventEmitter; + +const e = new EventEmitter(); +let fl; // foo listeners + +fl = e.listeners('foo'); +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 0); +assert(!(e._events instanceof Object)); +assert.deepStrictEqual(Object.keys(e._events), []); + +e.on('foo', assert.fail); +fl = e.listeners('foo'); +assert.strictEqual(e._events.foo, assert.fail); +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 1); +assert.strictEqual(fl[0], assert.fail); + +e.listeners('bar'); + +e.on('foo', assert.ok); +fl = e.listeners('foo'); + +assert(Array.isArray(e._events.foo)); +assert.strictEqual(e._events.foo.length, 2); +assert.strictEqual(e._events.foo[0], assert.fail); +assert.strictEqual(e._events.foo[1], assert.ok); + +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 2); +assert.strictEqual(fl[0], assert.fail); +assert.strictEqual(fl[1], assert.ok); + +console.log('ok'); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-listeners.js b/cli/tests/node_compat/test/parallel/test-event-emitter-listeners.js new file mode 100644 index 00000000000000..9ddc4cde39d0a3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-listeners.js @@ -0,0 +1,131 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); +const events = require('events'); + +function listener() {} + +function listener2() {} + +function listener3() { + return 0; +} + +function listener4() { + return 1; +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const fooListeners = ee.listeners('foo'); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); + ee.removeAllListeners('foo'); + assert.deepStrictEqual(ee.listeners('foo'), []); + assert.deepStrictEqual(fooListeners, [listener]); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const eeListenersCopy = ee.listeners('foo'); + assert.deepStrictEqual(eeListenersCopy, [listener]); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); + eeListenersCopy.push(listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); + assert.deepStrictEqual(eeListenersCopy, [listener, listener2]); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const eeListenersCopy = ee.listeners('foo'); + ee.on('foo', listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener, listener2]); + assert.deepStrictEqual(eeListenersCopy, [listener]); +} + +{ + const ee = new events.EventEmitter(); + ee.once('foo', listener); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + ee.once('foo', listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener, listener2]); +} + +{ + const ee = new events.EventEmitter(); + ee._events = undefined; + assert.deepStrictEqual(ee.listeners('foo'), []); +} + +{ + class TestStream extends events.EventEmitter {} + const s = new TestStream(); + assert.deepStrictEqual(s.listeners('foo'), []); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const wrappedListener = ee.rawListeners('foo'); + assert.strictEqual(wrappedListener.length, 1); + assert.strictEqual(wrappedListener[0], listener); + assert.notStrictEqual(wrappedListener, ee.rawListeners('foo')); + ee.once('foo', listener); + const wrappedListeners = ee.rawListeners('foo'); + assert.strictEqual(wrappedListeners.length, 2); + assert.strictEqual(wrappedListeners[0], listener); + assert.notStrictEqual(wrappedListeners[1], listener); + assert.strictEqual(wrappedListeners[1].listener, listener); + assert.notStrictEqual(wrappedListeners, ee.rawListeners('foo')); + ee.emit('foo'); + assert.strictEqual(wrappedListeners.length, 2); + assert.strictEqual(wrappedListeners[1].listener, listener); +} + +{ + const ee = new events.EventEmitter(); + ee.once('foo', listener3); + ee.on('foo', listener4); + const rawListeners = ee.rawListeners('foo'); + assert.strictEqual(rawListeners.length, 2); + assert.strictEqual(rawListeners[0](), 0); + const rawListener = ee.rawListeners('foo'); + assert.strictEqual(rawListener.length, 1); + assert.strictEqual(rawListener[0](), 1); +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-null.js b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-null.js new file mode 100644 index 00000000000000..0a17f5e719088e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-null.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +const e = new events.EventEmitter(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, null); + assert.ok(warning.message.includes( + '2 null listeners added to [EventEmitter].')); +})); + +e.on(null, () => {}); +e.on(null, () => {}); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js new file mode 100644 index 00000000000000..2bc5832e5e8790 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js @@ -0,0 +1,32 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +const symbol = Symbol('symbol'); + +const e = new events.EventEmitter(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, symbol); + assert.ok(warning.message.includes( + '2 Symbol(symbol) listeners added to [EventEmitter].')); +})); + +e.on(symbol, () => {}); +e.on(symbol, () => {}); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning.js b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning.js new file mode 100644 index 00000000000000..f1a391402fef3b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners-warning.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +class FakeInput extends events.EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +const e = new FakeInput(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, 'event-type'); + assert.ok(warning.message.includes( + '2 event-type listeners added to [FakeInput].')); +})); + +e.on('event-type', () => {}); +e.on('event-type', () => {}); // Trigger warning. +e.on('event-type', () => {}); // Verify that warning is emitted only once. diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners.js b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners.js new file mode 100644 index 00000000000000..1245c6b9285fb9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-max-listeners.js @@ -0,0 +1,80 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const { inspect } = require('util'); +const e = new events.EventEmitter(); + +e.on('maxListeners', common.mustCall()); + +// Should not corrupt the 'maxListeners' queue. +e.setMaxListeners(42); + +const throwsObjs = [NaN, -1, 'and even this']; + +for (const obj of throwsObjs) { + assert.throws( + () => e.setMaxListeners(obj), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "n" is out of range. ' + + `It must be a non-negative number. Received ${inspect(obj)}` + } + ); + + assert.throws( + () => events.defaultMaxListeners = obj, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "defaultMaxListeners" is out of range. ' + + `It must be a non-negative number. Received ${inspect(obj)}` + } + ); +} + +e.emit('maxListeners'); + +{ + const { EventEmitter, defaultMaxListeners } = events; + for (const obj of throwsObjs) { + assert.throws(() => EventEmitter.setMaxListeners(obj), { + code: 'ERR_OUT_OF_RANGE', + }); + } + + // FIXME(bartlomieju): + // assert.throws( + // () => EventEmitter.setMaxListeners(defaultMaxListeners, 'INVALID_EMITTER'), + // { code: 'ERR_INVALID_ARG_TYPE' } + // ); +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-method-names.js b/cli/tests/node_compat/test/parallel/test-event-emitter-method-names.js new file mode 100644 index 00000000000000..45da20b9a4dc0e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-method-names.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +const E = events.EventEmitter.prototype; +assert.strictEqual(E.constructor.name, 'EventEmitter'); +assert.strictEqual(E.on, E.addListener); // Same method. +assert.strictEqual(E.off, E.removeListener); // Same method. +Object.getOwnPropertyNames(E).forEach(function(name) { + if (name === 'constructor' || name === 'on' || name === 'off') return; + if (typeof E[name] !== 'function') return; + assert.strictEqual(E[name].name, name); +}); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-modify-in-emit.js b/cli/tests/node_compat/test/parallel/test-event-emitter-modify-in-emit.js new file mode 100644 index 00000000000000..ac1fe667b59250 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-modify-in-emit.js @@ -0,0 +1,87 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +let callbacks_called = []; + +const e = new events.EventEmitter(); + +function callback1() { + callbacks_called.push('callback1'); + e.on('foo', callback2); + e.on('foo', callback3); + e.removeListener('foo', callback1); +} + +function callback2() { + callbacks_called.push('callback2'); + e.removeListener('foo', callback2); +} + +function callback3() { + callbacks_called.push('callback3'); + e.removeListener('foo', callback3); +} + +e.on('foo', callback1); +assert.strictEqual(e.listeners('foo').length, 1); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 2); +assert.deepStrictEqual(['callback1'], callbacks_called); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 0); +assert.deepStrictEqual(['callback1', 'callback2', 'callback3'], + callbacks_called); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 0); +assert.deepStrictEqual(['callback1', 'callback2', 'callback3'], + callbacks_called); + +e.on('foo', callback1); +e.on('foo', callback2); +assert.strictEqual(e.listeners('foo').length, 2); +e.removeAllListeners('foo'); +assert.strictEqual(e.listeners('foo').length, 0); + +// Verify that removing callbacks while in emit allows emits to propagate to +// all listeners +callbacks_called = []; + +e.on('foo', callback2); +e.on('foo', callback3); +assert.strictEqual(e.listeners('foo').length, 2); +e.emit('foo'); +assert.deepStrictEqual(['callback2', 'callback3'], callbacks_called); +assert.strictEqual(e.listeners('foo').length, 0); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-no-error-provided-to-error-event.js b/cli/tests/node_compat/test/parallel/test-event-emitter-no-error-provided-to-error-event.js new file mode 100644 index 00000000000000..8ab7aec44e8f6a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-no-error-provided-to-error-event.js @@ -0,0 +1,65 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +/* TODO(uki00a): Uncomment this block when the 'domain' module is implemented. +const domain = require('domain'); + +{ + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert(er instanceof Error, 'error created'); + })); + e.emit('error'); +} + +for (const arg of [false, null, undefined]) { + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert(er instanceof Error, 'error created'); + })); + e.emit('error', arg); +} + +for (const arg of [42, 'fortytwo', true]) { + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert.strictEqual(er, arg); + })); + e.emit('error', arg); +} +*/ diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-num-args.js b/cli/tests/node_compat/test/parallel/test-event-emitter-num-args.js new file mode 100644 index 00000000000000..af8f264946c848 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-num-args.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +const e = new events.EventEmitter(); +const num_args_emitted = []; + +e.on('numArgs', function() { + const numArgs = arguments.length; + num_args_emitted.push(numArgs); +}); + +e.on('foo', function() { + num_args_emitted.push(arguments.length); +}); + +e.on('foo', function() { + num_args_emitted.push(arguments.length); +}); + +e.emit('numArgs'); +e.emit('numArgs', null); +e.emit('numArgs', null, null); +e.emit('numArgs', null, null, null); +e.emit('numArgs', null, null, null, null); +e.emit('numArgs', null, null, null, null, null); + +e.emit('foo', null, null, null, null); + +process.on('exit', function() { + assert.deepStrictEqual(num_args_emitted, [0, 1, 2, 3, 4, 5, 4, 4]); +}); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-once.js b/cli/tests/node_compat/test/parallel/test-event-emitter-once.js new file mode 100644 index 00000000000000..dcfafba089430f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-once.js @@ -0,0 +1,77 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const e = new EventEmitter(); + +e.once('hello', common.mustCall()); + +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); + +function remove() { + assert.fail('once->foo should not be emitted'); +} + +e.once('foo', remove); +e.removeListener('foo', remove); +e.emit('foo'); + +e.once('e', common.mustCall(function() { + e.emit('e'); +})); + +e.once('e', common.mustCall()); + +e.emit('e'); + +{ + // once() has different code paths based on the number of arguments being + // emitted. Verify that all of the cases are covered. + const maxArgs = 4; + + for (let i = 0; i <= maxArgs; ++i) { + const ee = new EventEmitter(); + const args = ['foo']; + + for (let j = 0; j < i; ++j) + args.push(j); + + ee.once('foo', common.mustCall((...params) => { + assert.deepStrictEqual(params, args.slice(1)); + })); + + EventEmitter.prototype.emit.apply(ee, args); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-prepend.js b/cli/tests/node_compat/test/parallel/test-event-emitter-prepend.js new file mode 100644 index 00000000000000..b0672274252e11 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-prepend.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const myEE = new EventEmitter(); +let m = 0; +// This one comes last. +myEE.on('foo', common.mustCall(() => assert.strictEqual(m, 2))); + +// This one comes second. +myEE.prependListener('foo', common.mustCall(() => assert.strictEqual(m++, 1))); + +// This one comes first. +myEE.prependOnceListener('foo', + common.mustCall(() => assert.strictEqual(m++, 0))); + +myEE.emit('foo'); + +// Test fallback if prependListener is undefined. +const stream = require('stream'); + +delete EventEmitter.prototype.prependListener; + +function Writable() { + this.writable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +// FIXME(bartlomieju): +// const w = new Writable(); +// const r = new Readable(); +// r.pipe(w); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-remove-all-listeners.js b/cli/tests/node_compat/test/parallel/test-event-emitter-remove-all-listeners.js new file mode 100644 index 00000000000000..75e04999c0ed04 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-remove-all-listeners.js @@ -0,0 +1,130 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); + + +function expect(expected) { + const actual = []; + process.on('exit', function() { + assert.deepStrictEqual(actual.sort(), expected.sort()); + }); + function listener(name) { + actual.push(name); + } + return common.mustCall(listener, expected.length); +} + +{ + const ee = new events.EventEmitter(); + const noop = common.mustNotCall(); + ee.on('foo', noop); + ee.on('bar', noop); + ee.on('baz', noop); + ee.on('baz', noop); + const fooListeners = ee.listeners('foo'); + const barListeners = ee.listeners('bar'); + const bazListeners = ee.listeners('baz'); + ee.on('removeListener', expect(['bar', 'baz', 'baz'])); + ee.removeAllListeners('bar'); + ee.removeAllListeners('baz'); + assert.deepStrictEqual(ee.listeners('foo'), [noop]); + assert.deepStrictEqual(ee.listeners('bar'), []); + assert.deepStrictEqual(ee.listeners('baz'), []); + // After calling removeAllListeners(), + // the old listeners array should stay unchanged. + assert.deepStrictEqual(fooListeners, [noop]); + assert.deepStrictEqual(barListeners, [noop]); + assert.deepStrictEqual(bazListeners, [noop, noop]); + // After calling removeAllListeners(), + // new listeners arrays is different from the old. + assert.notStrictEqual(ee.listeners('bar'), barListeners); + assert.notStrictEqual(ee.listeners('baz'), bazListeners); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', common.mustNotCall()); + ee.on('bar', common.mustNotCall()); + // Expect LIFO order + ee.on('removeListener', expect(['foo', 'bar', 'removeListener'])); + ee.on('removeListener', expect(['foo', 'bar'])); + ee.removeAllListeners(); + assert.deepStrictEqual([], ee.listeners('foo')); + assert.deepStrictEqual([], ee.listeners('bar')); +} + +{ + const ee = new events.EventEmitter(); + ee.on('removeListener', common.mustNotCall()); + // Check for regression where removeAllListeners() throws when + // there exists a 'removeListener' listener, but there exists + // no listeners for the provided event type. + ee.removeAllListeners.bind(ee, 'foo'); +} + +{ + const ee = new events.EventEmitter(); + let expectLength = 2; + ee.on('removeListener', function(name, noop) { + assert.strictEqual(expectLength--, this.listeners('baz').length); + }); + ee.on('baz', common.mustNotCall()); + ee.on('baz', common.mustNotCall()); + ee.on('baz', common.mustNotCall()); + assert.strictEqual(ee.listeners('baz').length, expectLength + 1); + ee.removeAllListeners('baz'); + assert.strictEqual(ee.listeners('baz').length, 0); +} + +{ + const ee = new events.EventEmitter(); + assert.deepStrictEqual(ee, ee.removeAllListeners()); +} + +{ + const ee = new events.EventEmitter(); + ee._events = undefined; + assert.strictEqual(ee, ee.removeAllListeners()); +} + +{ + const ee = new events.EventEmitter(); + const symbol = Symbol('symbol'); + const noop = common.mustNotCall(); + ee.on(symbol, noop); + + ee.on('removeListener', common.mustCall((...args) => { + assert.deepStrictEqual(args, [symbol, noop]); + })); + + ee.removeAllListeners(); +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-remove-listeners.js b/cli/tests/node_compat/test/parallel/test-event-emitter-remove-listeners.js new file mode 100644 index 00000000000000..027660a88169fb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-remove-listeners.js @@ -0,0 +1,177 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +function listener1() {} + +function listener2() {} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('removeListener', common.mustNotCall()); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([listener1], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('hello', listener2); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + + function remove1() { + assert.fail('remove1 should not have been called'); + } + + function remove2() { + assert.fail('remove2 should not have been called'); + } + + ee.on('removeListener', common.mustCall(function(name, cb) { + if (cb !== remove1) return; + this.removeListener('quux', remove2); + this.emit('quux'); + }, 2)); + ee.on('quux', remove1); + ee.on('quux', remove2); + ee.removeListener('quux', remove1); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('hello', listener2); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + const listener3 = common.mustCall(() => { + ee.removeListener('hello', listener4); + }, 2); + const listener4 = common.mustCall(); + + ee.on('hello', listener3); + ee.on('hello', listener4); + + // listener4 will still be called although it is removed by listener 3. + ee.emit('hello'); + // This is so because the internal listener array at time of emit + // was [listener3,listener4] + + // Internal listener array [listener3] + ee.emit('hello'); +} + +{ + const ee = new EventEmitter(); + + ee.once('hello', listener1); + ee.on('removeListener', common.mustCall((eventName, listener) => { + assert.strictEqual(eventName, 'hello'); + assert.strictEqual(listener, listener1); + })); + ee.emit('hello'); +} + +{ + const ee = new EventEmitter(); + + assert.deepStrictEqual(ee, ee.removeListener('foo', () => {})); +} + +{ + const ee = new EventEmitter(); + const listener = () => {}; + ee._events = undefined; + const e = ee.removeListener('foo', listener); + assert.strictEqual(e, ee); +} + +{ + const ee = new EventEmitter(); + + ee.on('foo', listener1); + ee.on('foo', listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener1, listener2]); + + ee.removeListener('foo', listener1); + assert.strictEqual(ee._events.foo, listener2); + + ee.on('foo', listener1); + assert.deepStrictEqual(ee.listeners('foo'), [listener2, listener1]); + + ee.removeListener('foo', listener1); + assert.strictEqual(ee._events.foo, listener2); +} diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-set-max-listeners-side-effects.js b/cli/tests/node_compat/test/parallel/test-event-emitter-set-max-listeners-side-effects.js new file mode 100644 index 00000000000000..ab5b095f45cfba --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-set-max-listeners-side-effects.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +const e = new events.EventEmitter(); + +assert(!(e._events instanceof Object)); +assert.deepStrictEqual(Object.keys(e._events), []); +e.setMaxListeners(5); +assert.deepStrictEqual(Object.keys(e._events), []); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-special-event-names.js b/cli/tests/node_compat/test/parallel/test-event-emitter-special-event-names.js new file mode 100644 index 00000000000000..1a589221e08b09 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-special-event-names.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const ee = new EventEmitter(); +const handler = () => {}; + +assert.deepStrictEqual(ee.eventNames(), []); + +assert.strictEqual(ee._events.hasOwnProperty, undefined); +assert.strictEqual(ee._events.toString, undefined); + +ee.on('__proto__', handler); +ee.on('__defineGetter__', handler); +ee.on('toString', handler); + +assert.deepStrictEqual(ee.eventNames(), [ + '__proto__', + '__defineGetter__', + 'toString', +]); + +assert.deepStrictEqual(ee.listeners('__proto__'), [handler]); +assert.deepStrictEqual(ee.listeners('__defineGetter__'), [handler]); +assert.deepStrictEqual(ee.listeners('toString'), [handler]); + +ee.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); +})); +ee.emit('__proto__', 1); + +process.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); +})); +process.emit('__proto__', 1); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-subclass.js b/cli/tests/node_compat/test/parallel/test-event-emitter-subclass.js new file mode 100644 index 00000000000000..030104150e86b7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-subclass.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events').EventEmitter; + +Object.setPrototypeOf(MyEE.prototype, EventEmitter.prototype); +Object.setPrototypeOf(MyEE, EventEmitter); + +function MyEE(cb) { + this.once(1, cb); + this.emit(1); + this.removeAllListeners(); + EventEmitter.call(this); +} + +const myee = new MyEE(common.mustCall()); + +Object.setPrototypeOf(ErrorEE.prototype, EventEmitter.prototype); +Object.setPrototypeOf(ErrorEE, EventEmitter); +function ErrorEE() { + this.emit('error', new Error('blerg')); +} + +assert.throws(function() { + new ErrorEE(); +}, /blerg/); + +process.on('exit', function() { + assert(!(myee._events instanceof Object)); + assert.deepStrictEqual(Object.keys(myee._events), []); + console.log('ok'); +}); + + +function MyEE2() { + EventEmitter.call(this); +} + +MyEE2.prototype = new EventEmitter(); + +const ee1 = new MyEE2(); +const ee2 = new MyEE2(); + +ee1.on('x', () => {}); + +assert.strictEqual(ee2.listenerCount('x'), 0); diff --git a/cli/tests/node_compat/test/parallel/test-event-emitter-symbols.js b/cli/tests/node_compat/test/parallel/test-event-emitter-symbols.js new file mode 100644 index 00000000000000..b9dc9eaf3413a9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-event-emitter-symbols.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const ee = new EventEmitter(); +const foo = Symbol('foo'); +const listener = common.mustCall(); + +ee.on(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), [listener]); + +ee.emit(foo); + +ee.removeAllListeners(); +assert.deepStrictEqual(ee.listeners(foo), []); + +ee.on(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), [listener]); + +ee.removeListener(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), []); diff --git a/cli/tests/node_compat/test/parallel/test-events-list.js b/cli/tests/node_compat/test/parallel/test-events-list.js new file mode 100644 index 00000000000000..fc5ef71a87a1e0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-events-list.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const EE = new EventEmitter(); +const m = () => {}; +EE.on('foo', () => {}); +assert.deepStrictEqual(['foo'], EE.eventNames()); +EE.on('bar', m); +assert.deepStrictEqual(['foo', 'bar'], EE.eventNames()); +EE.removeListener('bar', m); +assert.deepStrictEqual(['foo'], EE.eventNames()); +const s = Symbol('s'); +EE.on(s, m); +assert.deepStrictEqual(['foo', s], EE.eventNames()); +EE.removeListener(s, m); +assert.deepStrictEqual(['foo'], EE.eventNames()); diff --git a/cli/tests/node_compat/test/parallel/test-events-on-async-iterator.js b/cli/tests/node_compat/test/parallel/test-events-on-async-iterator.js new file mode 100644 index 00000000000000..795edebfd60498 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-events-on-async-iterator.js @@ -0,0 +1,399 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { on, EventEmitter } = require('events'); +const { + NodeEventTarget +} = require('internal/event_target'); + +async function basic() { + const ee = new EventEmitter(); + process.nextTick(() => { + ee.emit('foo', 'bar'); + // 'bar' is a spurious event, we are testing + // that it does not show up in the iterable + ee.emit('bar', 24); + ee.emit('foo', 42); + }); + + const iterable = on(ee, 'foo'); + + const expected = [['bar'], [42]]; + + for await (const event of iterable) { + const current = expected.shift(); + + assert.deepStrictEqual(current, event); + + if (expected.length === 0) { + break; + } + } + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function invalidArgType() { + assert.throws(() => on({}, 'foo'), common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + })); +} + +async function error() { + const ee = new EventEmitter(); + const _err = new Error('kaboom'); + process.nextTick(() => { + ee.emit('error', _err); + }); + + const iterable = on(ee, 'foo'); + let looped = false; + let thrown = false; + + try { + // eslint-disable-next-line no-unused-vars + for await (const event of iterable) { + looped = true; + } + } catch (err) { + thrown = true; + assert.strictEqual(err, _err); + } + assert.strictEqual(thrown, true); + assert.strictEqual(looped, false); +} + +async function errorDelayed() { + const ee = new EventEmitter(); + const _err = new Error('kaboom'); + process.nextTick(() => { + ee.emit('foo', 42); + ee.emit('error', _err); + }); + + const iterable = on(ee, 'foo'); + const expected = [[42]]; + let thrown = false; + + try { + for await (const event of iterable) { + const current = expected.shift(); + assert.deepStrictEqual(current, event); + } + } catch (err) { + thrown = true; + assert.strictEqual(err, _err); + } + assert.strictEqual(thrown, true); + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function throwInLoop() { + const ee = new EventEmitter(); + const _err = new Error('kaboom'); + + process.nextTick(() => { + ee.emit('foo', 42); + }); + + try { + for await (const event of on(ee, 'foo')) { + assert.deepStrictEqual(event, [42]); + throw _err; + } + } catch (err) { + assert.strictEqual(err, _err); + } + + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function next() { + const ee = new EventEmitter(); + const iterable = on(ee, 'foo'); + + process.nextTick(function() { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); + iterable.return(); + }); + + const results = await Promise.all([ + iterable.next(), + iterable.next(), + iterable.next(), + ]); + + assert.deepStrictEqual(results, [{ + value: ['bar'], + done: false + }, { + value: [42], + done: false + }, { + value: undefined, + done: true + }]); + + assert.deepStrictEqual(await iterable.next(), { + value: undefined, + done: true + }); +} + +async function nextError() { + const ee = new EventEmitter(); + const iterable = on(ee, 'foo'); + const _err = new Error('kaboom'); + process.nextTick(function() { + ee.emit('error', _err); + }); + const results = await Promise.allSettled([ + iterable.next(), + iterable.next(), + iterable.next(), + ]); + assert.deepStrictEqual(results, [{ + status: 'rejected', + reason: _err + }, { + status: 'fulfilled', + value: { + value: undefined, + done: true + } + }, { + status: 'fulfilled', + value: { + value: undefined, + done: true + } + }]); + assert.strictEqual(ee.listeners('error').length, 0); +} + +async function iterableThrow() { + const ee = new EventEmitter(); + const iterable = on(ee, 'foo'); + + process.nextTick(() => { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); // lost in the queue + iterable.throw(_err); + }); + + const _err = new Error('kaboom'); + let thrown = false; + + assert.throws(() => { + // No argument + iterable.throw(); + }, { + message: 'The "EventEmitter.AsyncIterator" property must be' + + ' an instance of Error. Received undefined', + name: 'TypeError' + }); + + const expected = [['bar'], [42]]; + + try { + for await (const event of iterable) { + assert.deepStrictEqual(event, expected.shift()); + } + } catch (err) { + thrown = true; + assert.strictEqual(err, _err); + } + assert.strictEqual(thrown, true); + assert.strictEqual(expected.length, 0); + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function eventTarget() { + const et = new EventTarget(); + const tick = () => et.dispatchEvent(new Event('tick')); + const interval = setInterval(tick, 0); + let count = 0; + for await (const [ event ] of on(et, 'tick')) { + count++; + assert.strictEqual(event.type, 'tick'); + if (count >= 5) { + break; + } + } + assert.strictEqual(count, 5); + clearInterval(interval); +} + +async function errorListenerCount() { + const et = new EventEmitter(); + on(et, 'foo'); + assert.strictEqual(et.listenerCount('error'), 1); +} + +async function nodeEventTarget() { + const et = new NodeEventTarget(); + const tick = () => et.dispatchEvent(new Event('tick')); + const interval = setInterval(tick, 0); + let count = 0; + for await (const [ event] of on(et, 'tick')) { + count++; + assert.strictEqual(event.type, 'tick'); + if (count >= 5) { + break; + } + } + assert.strictEqual(count, 5); + clearInterval(interval); +} + +async function abortableOnBefore() { + const ee = new EventEmitter(); + const abortedSignal = AbortSignal.abort(); + [1, {}, null, false, 'hi'].forEach((signal) => { + assert.throws(() => on(ee, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + assert.throws(() => on(ee, 'foo', { signal: abortedSignal }), { + name: 'AbortError' + }); +} + +async function eventTargetAbortableOnBefore() { + const et = new EventTarget(); + const abortedSignal = AbortSignal.abort(); + [1, {}, null, false, 'hi'].forEach((signal) => { + assert.throws(() => on(et, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + assert.throws(() => on(et, 'foo', { signal: abortedSignal }), { + name: 'AbortError' + }); +} + +async function abortableOnAfter() { + const ee = new EventEmitter(); + const ac = new AbortController(); + + const i = setInterval(() => ee.emit('foo', 'foo'), 10); + + async function foo() { + for await (const f of on(ee, 'foo', { signal: ac.signal })) { + assert.strictEqual(f, 'foo'); + } + } + + foo().catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })).finally(() => { + clearInterval(i); + }); + + process.nextTick(() => ac.abort()); +} + +async function eventTargetAbortableOnAfter() { + const et = new EventTarget(); + const ac = new AbortController(); + + const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); + + async function foo() { + for await (const f of on(et, 'foo', { signal: ac.signal })) { + assert(f); + } + } + + foo().catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })).finally(() => { + clearInterval(i); + }); + + process.nextTick(() => ac.abort()); +} + +async function eventTargetAbortableOnAfter2() { + const et = new EventTarget(); + const ac = new AbortController(); + + const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); + + async function foo() { + for await (const f of on(et, 'foo', { signal: ac.signal })) { + assert(f); + // Cancel after a single event has been triggered. + ac.abort(); + } + } + + foo().catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })).finally(() => { + clearInterval(i); + }); +} + +async function abortableOnAfterDone() { + const ee = new EventEmitter(); + const ac = new AbortController(); + + const i = setInterval(() => ee.emit('foo', 'foo'), 1); + let count = 0; + + async function foo() { + for await (const f of on(ee, 'foo', { signal: ac.signal })) { + assert.strictEqual(f[0], 'foo'); + if (++count === 5) + break; + } + ac.abort(); // No error will occur + } + + foo().finally(() => { + clearInterval(i); + }); +} + +async function run() { + const funcs = [ + basic, + invalidArgType, + error, + errorDelayed, + throwInLoop, + next, + nextError, + iterableThrow, + eventTarget, + errorListenerCount, + nodeEventTarget, + abortableOnBefore, + abortableOnAfter, + eventTargetAbortableOnBefore, + eventTargetAbortableOnAfter, + eventTargetAbortableOnAfter2, + abortableOnAfterDone, + ]; + + for (const fn of funcs) { + await fn(); + } +} + +run().then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-events-once.js b/cli/tests/node_compat/test/parallel/test-events-once.js new file mode 100644 index 00000000000000..7236f983006fb7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-events-once.js @@ -0,0 +1,272 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(cjihrig): kEvents is an internally used symbol in Node.js. It is not +// implemented in Deno, so parts of this test must be removed in order to pass. + +'use strict'; +// Flags: --no-warnings + +const common = require('../common'); +const { once, EventEmitter } = require('events'); +const { + strictEqual, + deepStrictEqual, + fail, + rejects, +} = require('assert'); + +async function onceAnEvent() { + const ee = new EventEmitter(); + + process.nextTick(() => { + ee.emit('myevent', 42); + }); + + const [value] = await once(ee, 'myevent'); + strictEqual(value, 42); + strictEqual(ee.listenerCount('error'), 0); + strictEqual(ee.listenerCount('myevent'), 0); +} + +async function onceAnEventWithNullOptions() { + const ee = new EventEmitter(); + + process.nextTick(() => { + ee.emit('myevent', 42); + }); + + const [value] = await once(ee, 'myevent', null); + strictEqual(value, 42); +} + + +async function onceAnEventWithTwoArgs() { + const ee = new EventEmitter(); + + process.nextTick(() => { + ee.emit('myevent', 42, 24); + }); + + const value = await once(ee, 'myevent'); + deepStrictEqual(value, [42, 24]); +} + +async function catchesErrors() { + const ee = new EventEmitter(); + + const expected = new Error('kaboom'); + let err; + process.nextTick(() => { + ee.emit('error', expected); + }); + + try { + await once(ee, 'myevent'); + } catch (_e) { + err = _e; + } + strictEqual(err, expected); + strictEqual(ee.listenerCount('error'), 0); + strictEqual(ee.listenerCount('myevent'), 0); +} + +async function catchesErrorsWithAbortSignal() { + const ee = new EventEmitter(); + const ac = new AbortController(); + const signal = ac.signal; + + const expected = new Error('boom'); + let err; + process.nextTick(() => { + ee.emit('error', expected); + }); + + try { + const promise = once(ee, 'myevent', { signal }); + strictEqual(ee.listenerCount('error'), 1); + + await promise; + } catch (e) { + err = e; + } + strictEqual(err, expected); + strictEqual(ee.listenerCount('error'), 0); + strictEqual(ee.listenerCount('myevent'), 0); +} + +async function stopListeningAfterCatchingError() { + const ee = new EventEmitter(); + + const expected = new Error('kaboom'); + let err; + process.nextTick(() => { + ee.emit('error', expected); + ee.emit('myevent', 42, 24); + }); + + try { + await once(ee, 'myevent'); + } catch (_e) { + err = _e; + } + process.removeAllListeners('multipleResolves'); + strictEqual(err, expected); + strictEqual(ee.listenerCount('error'), 0); + strictEqual(ee.listenerCount('myevent'), 0); +} + +async function onceError() { + const ee = new EventEmitter(); + + const expected = new Error('kaboom'); + process.nextTick(() => { + ee.emit('error', expected); + }); + + const promise = once(ee, 'error'); + strictEqual(ee.listenerCount('error'), 1); + const [ err ] = await promise; + strictEqual(err, expected); + strictEqual(ee.listenerCount('error'), 0); + strictEqual(ee.listenerCount('myevent'), 0); +} + +async function onceWithEventTarget() { + const et = new EventTarget(); + const event = new Event('myevent'); + process.nextTick(() => { + et.dispatchEvent(event); + }); + const [ value ] = await once(et, 'myevent'); + strictEqual(value, event); +} + +async function onceWithEventTargetError() { + const et = new EventTarget(); + const error = new Event('error'); + process.nextTick(() => { + et.dispatchEvent(error); + }); + + const [ err ] = await once(et, 'error'); + strictEqual(err, error); +} + +async function prioritizesEventEmitter() { + const ee = new EventEmitter(); + ee.addEventListener = fail; + ee.removeAllListeners = fail; + process.nextTick(() => ee.emit('foo')); + await once(ee, 'foo'); +} + +async function abortSignalBefore() { + const ee = new EventEmitter(); + ee.on('error', common.mustNotCall()); + const abortedSignal = AbortSignal.abort(); + + await Promise.all([1, {}, 'hi', null, false].map((signal) => { + return rejects(once(ee, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + })); + + return rejects(once(ee, 'foo', { signal: abortedSignal }), { + name: 'AbortError' + }); +} + +async function abortSignalAfter() { + const ee = new EventEmitter(); + const ac = new AbortController(); + ee.on('error', common.mustNotCall()); + const r = rejects(once(ee, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); + process.nextTick(() => ac.abort()); + return r; +} + +async function abortSignalAfterEvent() { + const ee = new EventEmitter(); + const ac = new AbortController(); + process.nextTick(() => { + ee.emit('foo'); + ac.abort(); + }); + const promise = once(ee, 'foo', { signal: ac.signal }); + await promise; +} + +async function abortSignalRemoveListener() { + const ee = new EventEmitter(); + const ac = new AbortController(); + + try { + process.nextTick(() => ac.abort()); + await once(ee, 'test', { signal: ac.signal }); + } catch { + strictEqual(ee.listeners('test').length, 0); + strictEqual(ee.listeners('error').length, 0); + } +} + +async function eventTargetAbortSignalBefore() { + const et = new EventTarget(); + const abortedSignal = AbortSignal.abort(); + + await Promise.all([1, {}, 'hi', null, false].map((signal) => { + return rejects(once(et, 'foo', { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + })); + + return rejects(once(et, 'foo', { signal: abortedSignal }), { + name: 'AbortError' + }); +} + +async function eventTargetAbortSignalAfter() { + const et = new EventTarget(); + const ac = new AbortController(); + const r = rejects(once(et, 'foo', { signal: ac.signal }), { + name: 'AbortError' + }); + process.nextTick(() => ac.abort()); + return r; +} + +async function eventTargetAbortSignalAfterEvent() { + const et = new EventTarget(); + const ac = new AbortController(); + process.nextTick(() => { + et.dispatchEvent(new Event('foo')); + ac.abort(); + }); + await once(et, 'foo', { signal: ac.signal }); +} + +Promise.all([ + onceAnEvent(), + onceAnEventWithNullOptions(), + onceAnEventWithTwoArgs(), + catchesErrors(), + catchesErrorsWithAbortSignal(), + stopListeningAfterCatchingError(), + onceError(), + onceWithEventTarget(), + onceWithEventTargetError(), + prioritizesEventEmitter(), + abortSignalBefore(), + abortSignalAfter(), + abortSignalAfterEvent(), + abortSignalRemoveListener(), + eventTargetAbortSignalBefore(), + eventTargetAbortSignalAfter(), + eventTargetAbortSignalAfterEvent(), +]).then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-events-uncaught-exception-stack.js b/cli/tests/node_compat/test/parallel/test-events-uncaught-exception-stack.js new file mode 100644 index 00000000000000..ec0efff65adb01 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-events-uncaught-exception-stack.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +// Tests that the error stack where the exception was thrown is *not* appended. + +process.on('uncaughtException', common.mustCall((err) => { + const lines = err.stack.split('\n'); + assert.strictEqual(lines[0], 'Error'); + lines.slice(1).forEach((line) => { + assert.match(line, /^ {4}at/); + }); +})); + +new EventEmitter().emit('error', new Error()); diff --git a/cli/tests/node_compat/test/parallel/test-eventtarget-brandcheck.js b/cli/tests/node_compat/test/parallel/test-eventtarget-brandcheck.js new file mode 100644 index 00000000000000..e02128a05173d7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-eventtarget-brandcheck.js @@ -0,0 +1,104 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); + +const { + Event, + CustomEvent, + EventTarget, + NodeEventTarget, +} = require('internal/event_target'); + +[ + 'target', + 'currentTarget', + 'srcElement', + 'type', + 'cancelable', + 'defaultPrevented', + 'timeStamp', + 'returnValue', + 'bubbles', + 'composed', + 'eventPhase', +].forEach((i) => { + assert.throws(() => Reflect.get(Event.prototype, i, {}), { + code: 'ERR_INVALID_THIS', + }); +}); + +[ + 'stopImmediatePropagation', + 'preventDefault', + 'composedPath', + 'cancelBubble', + 'stopPropagation', +].forEach((i) => { + assert.throws(() => Reflect.apply(Event.prototype[i], [], {}), { + code: 'ERR_INVALID_THIS', + }); +}); + +[ + 'target', + 'currentTarget', + 'srcElement', + 'type', + 'cancelable', + 'defaultPrevented', + 'timeStamp', + 'returnValue', + 'bubbles', + 'composed', + 'eventPhase', + 'detail', +].forEach((i) => { + assert.throws(() => Reflect.get(CustomEvent.prototype, i, {}), { + code: 'ERR_INVALID_THIS', + }); +}); + +[ + 'stopImmediatePropagation', + 'preventDefault', + 'composedPath', + 'cancelBubble', + 'stopPropagation', +].forEach((i) => { + assert.throws(() => Reflect.apply(CustomEvent.prototype[i], [], {}), { + code: 'ERR_INVALID_THIS', + }); +}); + +['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach((i) => { + assert.throws(() => Reflect.apply(EventTarget.prototype[i], [], {}), { + code: 'ERR_INVALID_THIS', + }); +}); + +[ + 'setMaxListeners', + 'getMaxListeners', + 'eventNames', + 'listenerCount', + 'off', + 'removeListener', + 'on', + 'addListener', + 'once', + 'emit', + 'removeAllListeners', +].forEach((i) => { + assert.throws(() => Reflect.apply(NodeEventTarget.prototype[i], [], {}), { + code: 'ERR_INVALID_THIS', + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-exception-handler.js b/cli/tests/node_compat/test/parallel/test-exception-handler.js new file mode 100644 index 00000000000000..a7739ad844880e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-exception-handler.js @@ -0,0 +1,47 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const MESSAGE = 'catch me if you can'; + +process.on('uncaughtException', common.mustCall((e) => { + console.log('uncaught exception! 1'); + assert.strictEqual(MESSAGE, e.message); +})); + +process.on('uncaughtException', common.mustCall((e) => { + console.log('uncaught exception! 2'); + assert.strictEqual(MESSAGE, e.message); +})); + +setTimeout(() => { + throw new Error(MESSAGE); +}, 10); diff --git a/cli/tests/node_compat/test/parallel/test-exception-handler2.js b/cli/tests/node_compat/test/parallel/test-exception-handler2.js new file mode 100644 index 00000000000000..474c286c5a1bd0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-exception-handler2.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.on('uncaughtException', function(err) { + console.log(`Caught exception: ${err}`); +}); + +setTimeout(common.mustCall(function() { + console.log('This will still run.'); +}), 50); + +// Intentionally cause an exception, but don't catch it. +nonexistentFunc(); // eslint-disable-line no-undef +assert.fail('This will not run.'); diff --git a/cli/tests/node_compat/test/parallel/test-file-read-noexist.js b/cli/tests/node_compat/test/parallel/test-file-read-noexist.js new file mode 100644 index 00000000000000..491e5d029e66ba --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-file-read-noexist.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); + +const filename = fixtures.path('does_not_exist.txt'); +fs.readFile(filename, 'latin1', common.mustCall(function(err, content) { + assert.ok(err); + assert.strictEqual(err.code, 'ENOENT'); +})); diff --git a/cli/tests/node_compat/test/parallel/test-file-write-stream.js b/cli/tests/node_compat/test/parallel/test-file-write-stream.js new file mode 100644 index 00000000000000..fb67530a61b61d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-file-write-stream.js @@ -0,0 +1,91 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const fn = path.join(tmpdir.path, 'write.txt'); +tmpdir.refresh(); +const file = fs.createWriteStream(fn, { + highWaterMark: 10 +}); + +const EXPECTED = '012345678910'; + +const callbacks = { + open: -1, + drain: -2, + close: -1 +}; + +file + .on('open', function(fd) { + console.error('open!'); + callbacks.open++; + assert.strictEqual(typeof fd, 'number'); + }) + .on('drain', function() { + console.error('drain!', callbacks.drain); + callbacks.drain++; + if (callbacks.drain === -1) { + assert.strictEqual(fs.readFileSync(fn, 'utf8'), EXPECTED); + file.write(EXPECTED); + } else if (callbacks.drain === 0) { + assert.strictEqual(fs.readFileSync(fn, 'utf8'), EXPECTED + EXPECTED); + file.end(); + } + }) + .on('close', function() { + console.error('close!'); + assert.strictEqual(file.bytesWritten, EXPECTED.length * 2); + + callbacks.close++; + file.write('should not work anymore', common.expectsError({ + code: 'ERR_STREAM_WRITE_AFTER_END', + name: 'Error', + message: 'write after end' + })); + file.on('error', common.mustNotCall()); + + fs.unlinkSync(fn); + }); + +for (let i = 0; i < 11; i++) { + file.write(`${i}`); +} + +process.on('exit', function() { + for (const k in callbacks) { + assert.strictEqual(callbacks[k], 0, `${k} count off by ${callbacks[k]}`); + } + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-file-write-stream2.js b/cli/tests/node_compat/test/parallel/test-file-write-stream2.js new file mode 100644 index 00000000000000..2a48e736bcf3c1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-file-write-stream2.js @@ -0,0 +1,116 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + + +const filepath = path.join(tmpdir.path, 'write.txt'); + +const EXPECTED = '012345678910'; + +const cb_expected = 'write open drain write drain close '; +let cb_occurred = ''; + +let countDrains = 0; + + +process.on('exit', function() { + removeTestFile(); + if (cb_occurred !== cb_expected) { + console.log(' Test callback events missing or out of order:'); + console.log(` expected: ${cb_expected}`); + console.log(` occurred: ${cb_occurred}`); + assert.strictEqual( + cb_occurred, cb_expected, + `events missing or out of order: "${cb_occurred}" !== "${cb_expected}"`); + } else { + console.log('ok'); + } +}); + +function removeTestFile() { + try { + fs.unlinkSync(filepath); + } catch { + // Continue regardless of error. + } +} + + +tmpdir.refresh(); + +// Drain at 0, return false at 10. +const file = fs.createWriteStream(filepath, { + highWaterMark: 11 +}); + +file.on('open', function(fd) { + console.error('open'); + cb_occurred += 'open '; + assert.strictEqual(typeof fd, 'number'); +}); + +file.on('drain', function() { + console.error('drain'); + cb_occurred += 'drain '; + ++countDrains; + if (countDrains === 1) { + console.error('drain=1, write again'); + assert.strictEqual(fs.readFileSync(filepath, 'utf8'), EXPECTED); + console.error(`ondrain write ret= ${file.write(EXPECTED)}`); + cb_occurred += 'write '; + } else if (countDrains === 2) { + console.error('second drain, end'); + assert.strictEqual(fs.readFileSync(filepath, 'utf8'), EXPECTED + EXPECTED); + file.end(); + } +}); + +file.on('close', function() { + cb_occurred += 'close '; + assert.strictEqual(file.bytesWritten, EXPECTED.length * 2); + file.write('should not work anymore', (err) => { + assert.ok(err.message.includes('write after end')); + }); +}); + +for (let i = 0; i < 11; i++) { + const ret = file.write(String(i)); + console.error(`${i} ${ret}`); + + // Return false when i hits 10 + assert.strictEqual(ret, i !== 10); +} +cb_occurred += 'write '; diff --git a/cli/tests/node_compat/test/parallel/test-file-write-stream3.js b/cli/tests/node_compat/test/parallel/test-file-write-stream3.js new file mode 100644 index 00000000000000..07704d814edd06 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-file-write-stream3.js @@ -0,0 +1,221 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + + +const filepath = path.join(tmpdir.path, 'write_pos.txt'); + + +const cb_expected = 'write open close write open close write open close '; +let cb_occurred = ''; + +const fileDataInitial = 'abcdefghijklmnopqrstuvwxyz'; + +const fileDataExpected_1 = 'abcdefghijklmnopqrstuvwxyz'; +const fileDataExpected_2 = 'abcdefghij123456qrstuvwxyz'; +const fileDataExpected_3 = 'abcdefghij\u2026\u2026qrstuvwxyz'; + + +process.on('exit', function() { + if (cb_occurred !== cb_expected) { + console.log(' Test callback events missing or out of order:'); + console.log(` expected: ${cb_expected}`); + console.log(` occurred: ${cb_occurred}`); + assert.strictEqual( + cb_occurred, cb_expected, + `events missing or out of order: "${cb_occurred}" !== "${cb_expected}"`); + } +}); + + +tmpdir.refresh(); + + +function run_test_1() { + const options = {}; + const file = fs.createWriteStream(filepath, options); + console.log(' (debug: start ', file.start); + console.log(' (debug: pos ', file.pos); + + file.on('open', function(fd) { + cb_occurred += 'open '; + }); + + file.on('close', function() { + cb_occurred += 'close '; + console.log(' (debug: bytesWritten ', file.bytesWritten); + console.log(' (debug: start ', file.start); + console.log(' (debug: pos ', file.pos); + assert.strictEqual(file.bytesWritten, buffer.length); + const fileData = fs.readFileSync(filepath, 'utf8'); + console.log(' (debug: file data ', fileData); + console.log(' (debug: expected ', fileDataExpected_1); + assert.strictEqual(fileData, fileDataExpected_1); + + run_test_2(); + }); + + file.on('error', function(err) { + cb_occurred += 'error '; + console.log(' (debug: err event ', err); + throw err; + }); + + const buffer = Buffer.from(fileDataInitial); + file.write(buffer); + cb_occurred += 'write '; + + file.end(); +} + + +function run_test_2() { + + const buffer = Buffer.from('123456'); + + const options = { start: 10, + flags: 'r+' }; + const file = fs.createWriteStream(filepath, options); + console.log(' (debug: start ', file.start); + console.log(' (debug: pos ', file.pos); + + file.on('open', function(fd) { + cb_occurred += 'open '; + }); + + file.on('close', function() { + cb_occurred += 'close '; + console.log(' (debug: bytesWritten ', file.bytesWritten); + console.log(' (debug: start ', file.start); + console.log(' (debug: pos ', file.pos); + assert.strictEqual(file.bytesWritten, buffer.length); + const fileData = fs.readFileSync(filepath, 'utf8'); + console.log(' (debug: file data ', fileData); + console.log(' (debug: expected ', fileDataExpected_2); + assert.strictEqual(fileData, fileDataExpected_2); + + run_test_3(); + }); + + file.on('error', function(err) { + cb_occurred += 'error '; + console.log(' (debug: err event ', err); + throw err; + }); + + file.write(buffer); + cb_occurred += 'write '; + + file.end(); +} + + +function run_test_3() { + + const data = '\u2026\u2026'; // 3 bytes * 2 = 6 bytes in UTF-8 + + const options = { start: 10, + flags: 'r+' }; + const file = fs.createWriteStream(filepath, options); + console.log(' (debug: start ', file.start); + console.log(' (debug: pos ', file.pos); + + file.on('open', function(fd) { + cb_occurred += 'open '; + }); + + file.on('close', function() { + cb_occurred += 'close '; + console.log(' (debug: bytesWritten ', file.bytesWritten); + console.log(' (debug: start ', file.start); + console.log(' (debug: pos ', file.pos); + assert.strictEqual(file.bytesWritten, data.length * 3); + const fileData = fs.readFileSync(filepath, 'utf8'); + console.log(' (debug: file data ', fileData); + console.log(' (debug: expected ', fileDataExpected_3); + assert.strictEqual(fileData, fileDataExpected_3); + + run_test_4(); + run_test_5(); + }); + + file.on('error', function(err) { + cb_occurred += 'error '; + console.log(' (debug: err event ', err); + throw err; + }); + + file.write(data, 'utf8'); + cb_occurred += 'write '; + + file.end(); +} + + +const run_test_4 = common.mustCall(function() { + // Error: start must be >= zero + const fn = () => { + fs.createWriteStream(filepath, { start: -5, flags: 'r+' }); + }; + // Verify the range of values using a common integer verifier. + // Limit Number.MAX_SAFE_INTEGER + const err = { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "start" is out of range. ' + + `It must be >= 0 && <= ${Number.MAX_SAFE_INTEGER}. Received -5`, + name: 'RangeError' + }; + assert.throws(fn, err); +}); + + +const run_test_5 = common.mustCall(function() { + // Error: start must be <= 2 ** 53 - 1 + const fn = () => { + fs.createWriteStream(filepath, { start: 2 ** 53, flags: 'r+' }); + }; + // Verify the range of values using a common integer verifier. + // Limit Number.MAX_SAFE_INTEGER + const err = { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "start" is out of range. It must be ' + + `>= 0 && <= ${Number.MAX_SAFE_INTEGER}. ` + + 'Received 9_007_199_254_740_992', + name: 'RangeError' + }; + assert.throws(fn, err); +}); + +run_test_1(); diff --git a/cli/tests/node_compat/test/parallel/test-file-write-stream4.js b/cli/tests/node_compat/test/parallel/test-file-write-stream4.js new file mode 100644 index 00000000000000..7d696e801c9bd6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-file-write-stream4.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Test that 'close' emits once and not twice when `emitClose: true` is set. +// Refs: https://github.com/nodejs/node/issues/31366 + +const common = require('../common'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filepath = path.join(tmpdir.path, 'write_pos.txt'); + +const fileReadStream = fs.createReadStream(process.execPath); +const fileWriteStream = fs.createWriteStream(filepath, { + emitClose: true +}); + +fileReadStream.pipe(fileWriteStream); +fileWriteStream.on('close', common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-fs-access.js b/cli/tests/node_compat/test/parallel/test-fs-access.js new file mode 100644 index 00000000000000..ed0fc94706e136 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-access.js @@ -0,0 +1,246 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +// This tests that fs.access and fs.accessSync works as expected +// and the errors thrown from these APIs include the desired properties + +const common = require('../common'); +if (!common.isWindows && process.getuid() === 0) + common.skip('as this test should not be run as `root`'); + +if (common.isIBMi) + common.skip('IBMi has a different access permission mechanism'); + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const { internalBinding } = require('internal/test/binding'); +const { UV_ENOENT } = internalBinding('uv'); + +const tmpdir = require('../common/tmpdir'); +const doesNotExist = path.join(tmpdir.path, '__this_should_not_exist'); +const readOnlyFile = path.join(tmpdir.path, 'read_only_file'); +const readWriteFile = path.join(tmpdir.path, 'read_write_file'); + +function createFileWithPerms(file, mode) { + fs.writeFileSync(file, ''); + fs.chmodSync(file, mode); +} + +tmpdir.refresh(); +createFileWithPerms(readOnlyFile, 0o444); +createFileWithPerms(readWriteFile, 0o666); + +// On non-Windows supported platforms, fs.access(readOnlyFile, W_OK, ...) +// always succeeds if node runs as the super user, which is sometimes the +// case for tests running on our continuous testing platform agents. +// +// In this case, this test tries to change its process user id to a +// non-superuser user so that the test that checks for write access to a +// read-only file can be more meaningful. +// +// The change of user id is done after creating the fixtures files for the same +// reason: the test may be run as the superuser within a directory in which +// only the superuser can create files, and thus it may need superuser +// privileges to create them. +// +// There's not really any point in resetting the process' user id to 0 after +// changing it to 'nobody', since in the case that the test runs without +// superuser privilege, it is not possible to change its process user id to +// superuser. +// +// It can prevent the test from removing files created before the change of user +// id, but that's fine. In this case, it is the responsibility of the +// continuous integration platform to take care of that. +let hasWriteAccessForReadonlyFile = false; +if (!common.isWindows && process.getuid() === 0) { + hasWriteAccessForReadonlyFile = true; + try { + process.setuid('nobody'); + hasWriteAccessForReadonlyFile = false; + } catch { + // Continue regardless of error. + } +} + +assert.strictEqual(typeof fs.F_OK, 'number'); +assert.strictEqual(typeof fs.R_OK, 'number'); +assert.strictEqual(typeof fs.W_OK, 'number'); +assert.strictEqual(typeof fs.X_OK, 'number'); + +const throwNextTick = (e) => { process.nextTick(() => { throw e; }); }; + +fs.access(__filename, common.mustCall(function(...args) { + assert.deepStrictEqual(args, [null]); +})); +fs.promises.access(__filename) + .then(common.mustCall()) + .catch(throwNextTick); +fs.access(__filename, fs.R_OK, common.mustCall(function(...args) { + assert.deepStrictEqual(args, [null]); +})); +fs.promises.access(__filename, fs.R_OK) + .then(common.mustCall()) + .catch(throwNextTick); +fs.access(readOnlyFile, fs.R_OK, common.mustCall(function(...args) { + assert.deepStrictEqual(args, [null]); +})); +fs.promises.access(readOnlyFile, fs.R_OK) + .then(common.mustCall()) + .catch(throwNextTick); + +{ + const expectedError = (err) => { + assert.notStrictEqual(err, null); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.path, doesNotExist); + }; + fs.access(doesNotExist, common.mustCall(expectedError)); + fs.promises.access(doesNotExist) + .then(common.mustNotCall(), common.mustCall(expectedError)) + .catch(throwNextTick); +} + +{ + function expectedError(err) { + assert.strictEqual(this, undefined); + if (hasWriteAccessForReadonlyFile) { + assert.ifError(err); + } else { + assert.notStrictEqual(err, null); + assert.strictEqual(err.path, readOnlyFile); + } + } + fs.access(readOnlyFile, fs.W_OK, common.mustCall(expectedError)); + fs.promises.access(readOnlyFile, fs.W_OK) + .then(common.mustNotCall(), common.mustCall(expectedError)) + .catch(throwNextTick); +} + +{ + const expectedError = (err) => { + assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); + assert.ok(err instanceof TypeError); + return true; + }; + assert.throws( + () => { fs.access(100, fs.F_OK, common.mustNotCall()); }, + expectedError + ); + + fs.promises.access(100, fs.F_OK) + .then(common.mustNotCall(), common.mustCall(expectedError)) + .catch(throwNextTick); +} + +assert.throws( + () => { + fs.access(__filename, fs.F_OK); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws( + () => { + fs.access(__filename, fs.F_OK, common.mustNotMutateObjectDeep({})); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +// Regular access should not throw. +fs.accessSync(__filename); +const mode = fs.R_OK | fs.W_OK; +fs.accessSync(readWriteFile, mode); + +// Invalid modes should throw. +[ + false, + 1n, + { [Symbol.toPrimitive]() { return fs.R_OK; } }, + [1], + 'r', +].forEach((mode, i) => { + console.log(mode, i); + assert.throws( + () => fs.access(readWriteFile, mode, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + message: /"mode" argument.+integer/ + } + ); + assert.throws( + () => fs.accessSync(readWriteFile, mode), + { + code: 'ERR_INVALID_ARG_TYPE', + message: /"mode" argument.+integer/ + } + ); +}); + +// Out of range modes should throw +[ + -1, + 8, + Infinity, + NaN, +].forEach((mode, i) => { + console.log(mode, i); + assert.throws( + () => fs.access(readWriteFile, mode, common.mustNotCall()), + { + code: 'ERR_OUT_OF_RANGE', + message: /"mode".+It must be an integer >= 0 && <= 7/ + } + ); + assert.throws( + () => fs.accessSync(readWriteFile, mode), + { + code: 'ERR_OUT_OF_RANGE', + message: /"mode".+It must be an integer >= 0 && <= 7/ + } + ); +}); + +assert.throws( + () => { fs.accessSync(doesNotExist); }, + (err) => { + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.path, doesNotExist); + assert.strictEqual( + err.message, + `ENOENT: no such file or directory, access '${doesNotExist}'` + ); + assert.strictEqual(err.constructor, Error); + assert.strictEqual(err.syscall, 'access'); + assert.strictEqual(err.errno, UV_ENOENT); + return true; + } +); + +assert.throws( + () => { fs.accessSync(Buffer.from(doesNotExist)); }, + (err) => { + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.path, doesNotExist); + assert.strictEqual( + err.message, + `ENOENT: no such file or directory, access '${doesNotExist}'` + ); + assert.strictEqual(err.constructor, Error); + assert.strictEqual(err.syscall, 'access'); + assert.strictEqual(err.errno, UV_ENOENT); + return true; + } +); diff --git a/cli/tests/node_compat/test/parallel/test-fs-append-file-sync.js b/cli/tests/node_compat/test/parallel/test-fs-append-file-sync.js new file mode 100644 index 00000000000000..19ba266cfdfec2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-append-file-sync.js @@ -0,0 +1,115 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const join = require('path').join; +const fs = require('fs'); + +const currentFileData = 'ABCD'; +const m = 0o600; +const num = 220; +const data = '南越国是前203年至前111年存在于岭南地区的一个国家,国都位于番禺,疆域包括今天中国的广东、' + + '广西两省区的大部份地区,福建省、湖南、贵州、云南的一小部份地区和越南的北部。' + + '南越国是秦朝灭亡后,由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。' + + '前196年和前179年,南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' + + '南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。南越国共存在93年,' + + '历经五代君主。南越国是岭南地区的第一个有记载的政权国家,采用封建制和郡县制并存的制度,' + + '它的建立保证了秦末乱世岭南地区社会秩序的稳定,有效的改善了岭南地区落后的政治、##济现状。\n'; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test that empty file will be created and have content added. +const filename = join(tmpdir.path, 'append-sync.txt'); + +fs.appendFileSync(filename, data); + +const fileData = fs.readFileSync(filename); + +assert.strictEqual(Buffer.byteLength(data), fileData.length); + +// Test that appends data to a non empty file. +const filename2 = join(tmpdir.path, 'append-sync2.txt'); +fs.writeFileSync(filename2, currentFileData); + +fs.appendFileSync(filename2, data); + +const fileData2 = fs.readFileSync(filename2); + +assert.strictEqual(Buffer.byteLength(data) + currentFileData.length, + fileData2.length); + +// Test that appendFileSync accepts buffers. +const filename3 = join(tmpdir.path, 'append-sync3.txt'); +fs.writeFileSync(filename3, currentFileData); + +const buf = Buffer.from(data, 'utf8'); +fs.appendFileSync(filename3, buf); + +const fileData3 = fs.readFileSync(filename3); + +assert.strictEqual(buf.length + currentFileData.length, fileData3.length); + +const filename4 = join(tmpdir.path, 'append-sync4.txt'); +fs.writeFileSync(filename4, currentFileData, common.mustNotMutateObjectDeep({ mode: m })); + +[ + true, false, 0, 1, Infinity, () => {}, {}, [], undefined, null, +].forEach((value) => { + assert.throws( + () => fs.appendFileSync(filename4, value, common.mustNotMutateObjectDeep({ mode: m })), + { message: /data/, code: 'ERR_INVALID_ARG_TYPE' } + ); +}); +fs.appendFileSync(filename4, `${num}`, common.mustNotMutateObjectDeep({ mode: m })); + +// Windows permissions aren't Unix. +if (!common.isWindows) { + const st = fs.statSync(filename4); + assert.strictEqual(st.mode & 0o700, m); +} + +const fileData4 = fs.readFileSync(filename4); + +assert.strictEqual(Buffer.byteLength(String(num)) + currentFileData.length, + fileData4.length); + +// Test that appendFile accepts file descriptors. +const filename5 = join(tmpdir.path, 'append-sync5.txt'); +fs.writeFileSync(filename5, currentFileData); + +const filename5fd = fs.openSync(filename5, 'a+', 0o600); +fs.appendFileSync(filename5fd, data); +fs.closeSync(filename5fd); + +const fileData5 = fs.readFileSync(filename5); + +assert.strictEqual(Buffer.byteLength(data) + currentFileData.length, + fileData5.length); diff --git a/cli/tests/node_compat/test/parallel/test-fs-append-file.js b/cli/tests/node_compat/test/parallel/test-fs-append-file.js new file mode 100644 index 00000000000000..41c6be6848dd3d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-append-file.js @@ -0,0 +1,202 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const join = require('path').join; + +const tmpdir = require('../common/tmpdir'); + +const currentFileData = 'ABCD'; + +const s = '南越国是前203年至前111年存在于岭南地区的一个国家,国都位于番禺,疆域包括今天中国的广东、' + + '广西两省区的大部份地区,福建省、湖南、贵州、云南的一小部份地区和越南的北部。' + + '南越国是秦朝灭亡后,由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。' + + '前196年和前179年,南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' + + '南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。南越国共存在93年,' + + '历经五代君主。南越国是岭南地区的第一个有记载的政权国家,采用封建制和郡县制并存的制度,' + + '它的建立保证了秦末乱世岭南地区社会秩序的稳定,有效的改善了岭南地区落后的政治、##济现状。\n'; + +tmpdir.refresh(); + +const throwNextTick = (e) => { process.nextTick(() => { throw e; }); }; + +// Test that empty file will be created and have content added (callback API). +{ + const filename = join(tmpdir.path, 'append.txt'); + + fs.appendFile(filename, s, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s), buffer.length); + })); + })); +} + +// Test that empty file will be created and have content added (promise API). +{ + const filename = join(tmpdir.path, 'append-promise.txt'); + + fs.promises.appendFile(filename, s) + .then(common.mustCall(() => fs.promises.readFile(filename))) + .then((buffer) => { + assert.strictEqual(Buffer.byteLength(s), buffer.length); + }) + .catch(throwNextTick); +} + +// Test that appends data to a non-empty file (callback API). +{ + const filename = join(tmpdir.path, 'append-non-empty.txt'); + fs.writeFileSync(filename, currentFileData); + + fs.appendFile(filename, s, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, + buffer.length); + })); + })); +} + +// Test that appends data to a non-empty file (promise API). +{ + const filename = join(tmpdir.path, 'append-non-empty-promise.txt'); + fs.writeFileSync(filename, currentFileData); + + fs.promises.appendFile(filename, s) + .then(common.mustCall(() => fs.promises.readFile(filename))) + .then((buffer) => { + assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, + buffer.length); + }) + .catch(throwNextTick); +} + +// Test that appendFile accepts buffers (callback API). +{ + const filename = join(tmpdir.path, 'append-buffer.txt'); + fs.writeFileSync(filename, currentFileData); + + const buf = Buffer.from(s, 'utf8'); + + fs.appendFile(filename, buf, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(buf.length + currentFileData.length, buffer.length); + })); + })); +} + +// Test that appendFile accepts buffers (promises API). +{ + const filename = join(tmpdir.path, 'append-buffer-promises.txt'); + fs.writeFileSync(filename, currentFileData); + + const buf = Buffer.from(s, 'utf8'); + + fs.promises.appendFile(filename, buf) + .then(common.mustCall(() => fs.promises.readFile(filename))) + .then((buffer) => { + assert.strictEqual(buf.length + currentFileData.length, buffer.length); + }) + .catch(throwNextTick); +} + +// Test that appendFile does not accept invalid data type (callback API). +[false, 5, {}, null, undefined].forEach(async (data) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + message: /"data"|"buffer"/ + }; + const filename = join(tmpdir.path, 'append-invalid-data.txt'); + + assert.throws( + () => fs.appendFile(filename, data, common.mustNotCall()), + errObj + ); + + assert.throws( + () => fs.appendFileSync(filename, data), + errObj + ); + + await assert.rejects( + fs.promises.appendFile(filename, data), + errObj + ); + // The filename shouldn't exist if throwing error. + assert.throws( + () => fs.statSync(filename), + { + code: 'ENOENT', + message: /no such file or directory/ + } + ); +}); + +// Test that appendFile accepts file descriptors (callback API). +{ + const filename = join(tmpdir.path, 'append-descriptors.txt'); + fs.writeFileSync(filename, currentFileData); + + fs.open(filename, 'a+', common.mustSucceed((fd) => { + fs.appendFile(fd, s, common.mustSucceed(() => { + fs.close(fd, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, + buffer.length); + })); + })); + })); + })); +} + +// FIXME(F3n67u): fs.promises.appendFile support FileHandle +// Test that appendFile accepts file descriptors (promises API). +// { +// const filename = join(tmpdir.path, 'append-descriptors-promises.txt'); +// fs.writeFileSync(filename, currentFileData); + +// let fd; +// fs.promises.open(filename, 'a+') +// .then(common.mustCall((fileDescriptor) => { +// fd = fileDescriptor; +// return fs.promises.appendFile(fd, s); +// })) +// .then(common.mustCall(() => fd.close())) +// .then(common.mustCall(() => fs.promises.readFile(filename))) +// .then(common.mustCall((buffer) => { +// assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, +// buffer.length); +// })) +// .catch(throwNextTick); +// } + +assert.throws( + () => fs.appendFile(join(tmpdir.path, 'append6.txt'), console.log), + { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/cli/tests/node_compat/test/parallel/test-fs-chmod-mask.js b/cli/tests/node_compat/test/parallel/test-fs-chmod-mask.js new file mode 100644 index 00000000000000..f11567c7ec0f6c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-chmod-mask.js @@ -0,0 +1,106 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This tests that the lower bits of mode > 0o777 still works in fs APIs. + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +// TODO(f3n67u): fs.chmod is not supported in Windows +if (common.isWindows) { + return; +} + +let mode; +// On Windows chmod is only able to manipulate write permission +if (common.isWindows) { + mode = 0o444; // read-only +} else { + mode = 0o777; +} + +const maskToIgnore = 0o10000; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function test(mode, asString) { + const suffix = asString ? 'str' : 'num'; + const input = asString ? + (mode | maskToIgnore).toString(8) : (mode | maskToIgnore); + + { + const file = path.join(tmpdir.path, `chmod-async-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + + fs.chmod(file, input, common.mustSucceed(() => { + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + })); + } + + { + const file = path.join(tmpdir.path, `chmodSync-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + + fs.chmodSync(file, input); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + } + + // TODO(f3n67u): implement fs.fchmod + // { + // const file = path.join(tmpdir.path, `fchmod-async-${suffix}.txt`); + // fs.writeFileSync(file, 'test', 'utf-8'); + // fs.open(file, 'w', common.mustSucceed((fd) => { + // fs.fchmod(fd, input, common.mustSucceed(() => { + // assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + // fs.close(fd, assert.ifError); + // })); + // })); + // } + + // TODO(f3n67u): implement fs.fchmodSync + // { + // const file = path.join(tmpdir.path, `fchmodSync-${suffix}.txt`); + // fs.writeFileSync(file, 'test', 'utf-8'); + // const fd = fs.openSync(file, 'w'); + + // fs.fchmodSync(fd, input); + // assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + + // fs.close(fd, assert.ifError); + // } + + // TODO(f3n67u): implement fs.lchmod + // if (fs.lchmod) { + // const link = path.join(tmpdir.path, `lchmod-src-${suffix}`); + // const file = path.join(tmpdir.path, `lchmod-dest-${suffix}`); + // fs.writeFileSync(file, 'test', 'utf-8'); + // fs.symlinkSync(file, link); + + // fs.lchmod(link, input, common.mustSucceed(() => { + // assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode); + // })); + // } + + // TODO(f3n67u): implement fs.lchmodSync + // if (fs.lchmodSync) { + // const link = path.join(tmpdir.path, `lchmodSync-src-${suffix}`); + // const file = path.join(tmpdir.path, `lchmodSync-dest-${suffix}`); + // fs.writeFileSync(file, 'test', 'utf-8'); + // fs.symlinkSync(file, link); + + // fs.lchmodSync(link, input); + // assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode); + // } +} + +test(mode, true); +test(mode, false); diff --git a/cli/tests/node_compat/test/parallel/test-fs-chmod.js b/cli/tests/node_compat/test/parallel/test-fs-chmod.js new file mode 100644 index 00000000000000..b5f524f6424c31 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-chmod.js @@ -0,0 +1,167 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +let mode_async; +let mode_sync; + +// Need to hijack fs.open/close to make sure that things +// get closed once they're opened. +fs._open = fs.open; +fs._openSync = fs.openSync; +fs.open = open; +fs.openSync = openSync; +fs._close = fs.close; +fs._closeSync = fs.closeSync; +fs.close = close; +fs.closeSync = closeSync; + +let openCount = 0; + +function open() { + openCount++; + return fs._open.apply(fs, arguments); +} + +function openSync() { + openCount++; + return fs._openSync.apply(fs, arguments); +} + +function close() { + openCount--; + return fs._close.apply(fs, arguments); +} + +function closeSync() { + openCount--; + return fs._closeSync.apply(fs, arguments); +} + +// TODO(f3n67u): fs.chmod is not supported in Windows +if (common.isWindows) { + return; +} + +// On Windows chmod is only able to manipulate write permission +if (common.isWindows) { + mode_async = 0o400; // read-only + mode_sync = 0o600; // read-write +} else { + mode_async = 0o777; + mode_sync = 0o644; +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const file1 = path.join(tmpdir.path, 'a.js'); +const file2 = path.join(tmpdir.path, 'a1.js'); + +// Create file1. +fs.closeSync(fs.openSync(file1, 'w')); + +fs.chmod(file1, mode_async.toString(8), common.mustSucceed(() => { + if (common.isWindows) { + assert.ok((fs.statSync(file1).mode & 0o777) & mode_async); + } else { + assert.strictEqual(fs.statSync(file1).mode & 0o777, mode_async); + } + + fs.chmodSync(file1, mode_sync); + if (common.isWindows) { + assert.ok((fs.statSync(file1).mode & 0o777) & mode_sync); + } else { + assert.strictEqual(fs.statSync(file1).mode & 0o777, mode_sync); + } +})); + +// TODO(f3n67u): implement fs.fchmod +// fs.open(file2, 'w', common.mustSucceed((fd) => { +// fs.fchmod(fd, mode_async.toString(8), common.mustSucceed(() => { +// if (common.isWindows) { +// assert.ok((fs.fstatSync(fd).mode & 0o777) & mode_async); +// } else { +// assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode_async); +// } + +// assert.throws( +// () => fs.fchmod(fd, {}), +// { +// code: 'ERR_INVALID_ARG_TYPE', +// } +// ); + +// fs.fchmodSync(fd, mode_sync); +// if (common.isWindows) { +// assert.ok((fs.fstatSync(fd).mode & 0o777) & mode_sync); +// } else { +// assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode_sync); +// } + +// fs.close(fd, assert.ifError); +// })); +// })); + + +// TODO(f3n67u): implement fs.lchmod +// // lchmod +// if (fs.lchmod) { +// const link = path.join(tmpdir.path, 'symbolic-link'); + +// fs.symlinkSync(file2, link); + +// fs.lchmod(link, mode_async, common.mustSucceed(() => { +// assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode_async); + +// fs.lchmodSync(link, mode_sync); +// assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode_sync); + +// })); +// } + +[false, 1, {}, [], null, undefined].forEach((input) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "path" argument must be of type string or an instance ' + + 'of Buffer or URL.' + + common.invalidArgTypeHelper(input) + }; + assert.throws(() => fs.chmod(input, 1, common.mustNotCall()), errObj); + assert.throws(() => fs.chmodSync(input, 1), errObj); +}); + +process.on('exit', function() { + assert.strictEqual(openCount, 0); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-chown-type-check.js b/cli/tests/node_compat/test/parallel/test-fs-chown-type-check.js new file mode 100644 index 00000000000000..a015fc18267202 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-chown-type-check.js @@ -0,0 +1,60 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.chown(i, 1, 1, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync(i, 1, 1), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); + +[false, 'test', {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.chown('not_a_file_that_exists', i, 1, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chown('not_a_file_that_exists', 1, i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync('not_a_file_that_exists', i, 1), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync('not_a_file_that_exists', 1, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-copyfile.js b/cli/tests/node_compat/test/parallel/test-fs-copyfile.js new file mode 100644 index 00000000000000..94f49363689eeb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-copyfile.js @@ -0,0 +1,176 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const { internalBinding } = require('internal/test/binding'); +const { + UV_ENOENT, + UV_EEXIST +} = internalBinding('uv'); +const path = require('path'); +const src = fixtures.path('a.js'); +const dest = path.join(tmpdir.path, 'copyfile.out'); +const { + COPYFILE_EXCL, + COPYFILE_FICLONE, + COPYFILE_FICLONE_FORCE, + UV_FS_COPYFILE_EXCL, + UV_FS_COPYFILE_FICLONE, + UV_FS_COPYFILE_FICLONE_FORCE +} = fs.constants; + +function verify(src, dest) { + const srcData = fs.readFileSync(src, 'utf8'); + const srcStat = fs.statSync(src); + const destData = fs.readFileSync(dest, 'utf8'); + const destStat = fs.statSync(dest); + + assert.strictEqual(srcData, destData); + assert.strictEqual(srcStat.mode, destStat.mode); + assert.strictEqual(srcStat.size, destStat.size); +} + +tmpdir.refresh(); + +// Verify that flags are defined. +assert.strictEqual(typeof COPYFILE_EXCL, 'number'); +assert.strictEqual(typeof COPYFILE_FICLONE, 'number'); +assert.strictEqual(typeof COPYFILE_FICLONE_FORCE, 'number'); +assert.strictEqual(typeof UV_FS_COPYFILE_EXCL, 'number'); +assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE, 'number'); +assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE_FORCE, 'number'); +assert.strictEqual(COPYFILE_EXCL, UV_FS_COPYFILE_EXCL); +assert.strictEqual(COPYFILE_FICLONE, UV_FS_COPYFILE_FICLONE); +assert.strictEqual(COPYFILE_FICLONE_FORCE, UV_FS_COPYFILE_FICLONE_FORCE); + +// Verify that files are overwritten when no flags are provided. +fs.writeFileSync(dest, '', 'utf8'); +const result = fs.copyFileSync(src, dest); +assert.strictEqual(result, undefined); +verify(src, dest); + +// Verify that files are overwritten with default flags. +fs.copyFileSync(src, dest, 0); +verify(src, dest); + +// Verify that UV_FS_COPYFILE_FICLONE can be used. +fs.unlinkSync(dest); +fs.copyFileSync(src, dest, UV_FS_COPYFILE_FICLONE); +verify(src, dest); + +// Verify that COPYFILE_FICLONE_FORCE can be used. +try { + fs.unlinkSync(dest); + fs.copyFileSync(src, dest, COPYFILE_FICLONE_FORCE); + verify(src, dest); +} catch (err) { + assert.strictEqual(err.syscall, 'copyfile'); + assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' || + err.code === 'ENOSYS' || err.code === 'EXDEV'); + assert.strictEqual(err.path, src); + assert.strictEqual(err.dest, dest); +} + +// Copies asynchronously. +tmpdir.refresh(); // Don't use unlinkSync() since the last test may fail. +fs.copyFile(src, dest, common.mustSucceed(() => { + verify(src, dest); + + // Copy asynchronously with flags. + fs.copyFile(src, dest, COPYFILE_EXCL, common.mustCall((err) => { + if (err.code === 'ENOENT') { // Could be ENOENT or EEXIST + assert.strictEqual(err.message, + 'ENOENT: no such file or directory, copyfile ' + + `'${src}' -> '${dest}'`); + assert.strictEqual(err.errno, UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'copyfile'); + } else { + assert.strictEqual(err.message, + 'EEXIST: file already exists, copyfile ' + + `'${src}' -> '${dest}'`); + assert.strictEqual(err.errno, UV_EEXIST); + assert.strictEqual(err.code, 'EEXIST'); + assert.strictEqual(err.syscall, 'copyfile'); + } + })); +})); + +// Throws if callback is not a function. +assert.throws(() => { + fs.copyFile(src, dest, 0, 0); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}); + +// Throws if the source path is not a string. +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.copyFile(i, dest, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /src/ + } + ); + assert.throws( + () => fs.copyFile(src, i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /dest/ + } + ); + assert.throws( + () => fs.copyFileSync(i, dest), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /src/ + } + ); + assert.throws( + () => fs.copyFileSync(src, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /dest/ + } + ); +}); + +assert.throws(() => { + fs.copyFileSync(src, dest, 'r'); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /mode/ +}); + +assert.throws(() => { + fs.copyFileSync(src, dest, 8); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "mode" is out of range. It must be an integer ' + + '>= 0 && <= 7. Received 8' +}); + +assert.throws(() => { + fs.copyFile(src, dest, 'r', common.mustNotCall()); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /mode/ +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-empty-readStream.js b/cli/tests/node_compat/test/parallel/test-fs-empty-readStream.js new file mode 100644 index 00000000000000..086798caad6f5a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-empty-readStream.js @@ -0,0 +1,57 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const emptyFile = fixtures.path('empty.txt'); + +fs.open(emptyFile, 'r', common.mustSucceed((fd) => { + const read = fs.createReadStream(emptyFile, { fd }); + + read.once('data', common.mustNotCall('data event should not emit')); + + read.once('end', common.mustCall()); +})); + +fs.open(emptyFile, 'r', common.mustSucceed((fd) => { + const read = fs.createReadStream(emptyFile, { fd }); + + read.pause(); + + read.once('data', common.mustNotCall('data event should not emit')); + + read.once('end', common.mustNotCall('end event should not emit')); + + setTimeout(common.mustCall(() => { + assert.strictEqual(read.isPaused(), true); + }), common.platformTimeout(50)); +})); diff --git a/cli/tests/node_compat/test/parallel/test-fs-mkdir.js b/cli/tests/node_compat/test/parallel/test-fs-mkdir.js new file mode 100644 index 00000000000000..5a3897e91c0cce --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-mkdir.js @@ -0,0 +1,379 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +let dirc = 0; +function nextdir() { + return `test${++dirc}`; +} + +// fs.mkdir creates directory using assigned path +{ + const pathname = path.join(tmpdir.path, nextdir()); + + fs.mkdir(pathname, common.mustCall(function(err) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + })); +} + +// fs.mkdir creates directory with assigned mode value +{ + const pathname = path.join(tmpdir.path, nextdir()); + + fs.mkdir(pathname, 0o777, common.mustCall(function(err) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + })); +} + +// fs.mkdir creates directory with mode passed as an options object +{ + const pathname = path.join(tmpdir.path, nextdir()); + + fs.mkdir(pathname, common.mustNotMutateObjectDeep({ mode: 0o777 }), common.mustCall(function(err) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + })); +} + +// fs.mkdirSync creates directory with mode passed as an options object +{ + const pathname = path.join(tmpdir.path, nextdir()); + + fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ mode: 0o777 })); + + assert.strictEqual(fs.existsSync(pathname), true); +} + +// mkdirSync successfully creates directory from given path +{ + const pathname = path.join(tmpdir.path, nextdir()); + + fs.mkdirSync(pathname); + + const exists = fs.existsSync(pathname); + assert.strictEqual(exists, true); +} + +// mkdirSync and mkdir require path to be a string, buffer or url. +// Anything else generates an error. +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.mkdir(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.mkdirSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); + +// mkdirpSync when both top-level, and sub-folders do not exist. +{ + const pathname = path.join(tmpdir.path, nextdir(), nextdir()); + + fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true })); + + const exists = fs.existsSync(pathname); + assert.strictEqual(exists, true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); +} + +// mkdirpSync when folder already exists. +{ + const pathname = path.join(tmpdir.path, nextdir(), nextdir()); + + fs.mkdirSync(pathname, { recursive: true }); + // Should not cause an error. + fs.mkdirSync(pathname, { recursive: true }); + + const exists = fs.existsSync(pathname); + assert.strictEqual(exists, true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); +} + +// mkdirpSync ../ +{ + const pathname = `${tmpdir.path}/${nextdir()}/../${nextdir()}/${nextdir()}`; + fs.mkdirSync(pathname, { recursive: true }); + const exists = fs.existsSync(pathname); + assert.strictEqual(exists, true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); +} + +// mkdirpSync when path is a file. +{ + const pathname = path.join(tmpdir.path, nextdir(), nextdir()); + + fs.mkdirSync(path.dirname(pathname)); + fs.writeFileSync(pathname, '', 'utf8'); + + assert.throws( + () => { fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true })); }, + { + code: 'EEXIST', + message: /EEXIST: .*mkdir/, + name: 'Error', + syscall: 'mkdir', + } + ); +} + +// mkdirpSync when part of the path is a file. +{ + const filename = path.join(tmpdir.path, nextdir(), nextdir()); + const pathname = path.join(filename, nextdir(), nextdir()); + + fs.mkdirSync(path.dirname(filename)); + fs.writeFileSync(filename, '', 'utf8'); + + assert.throws( + () => { fs.mkdirSync(pathname, { recursive: true }); }, + { + code: 'ENOTDIR', + message: /ENOTDIR: .*mkdir/, + name: 'Error', + syscall: 'mkdir', + path: pathname // See: https://github.com/nodejs/node/issues/28015 + } + ); +} + +// `mkdirp` when folder does not yet exist. +{ + const pathname = path.join(tmpdir.path, nextdir(), nextdir()); + + fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + })); +} + +// `mkdirp` when path is a file. +{ + const pathname = path.join(tmpdir.path, nextdir(), nextdir()); + + fs.mkdirSync(path.dirname(pathname)); + fs.writeFileSync(pathname, '', 'utf8'); + fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => { + assert.strictEqual(err.code, 'EEXIST'); + // TODO(wafuwafu13): Enable this + // assert.strictEqual(err.syscall, 'mkdir'); + assert.strictEqual(fs.statSync(pathname).isDirectory(), false); + })); +} + +// `mkdirp` when part of the path is a file. +{ + const filename = path.join(tmpdir.path, nextdir(), nextdir()); + const pathname = path.join(filename, nextdir(), nextdir()); + + fs.mkdirSync(path.dirname(filename)); + fs.writeFileSync(filename, '', 'utf8'); + fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => { + // TODO(wafuwafu13): Enable this + // assert.strictEqual(err.code, 'ENOTDIR'); + // assert.strictEqual(err.syscall, 'mkdir'); + assert.strictEqual(fs.existsSync(pathname), false); + // See: https://github.com/nodejs/node/issues/28015 + // The path field varies slightly in Windows errors, vs., other platforms + // see: https://github.com/libuv/libuv/issues/2661, for this reason we + // use startsWith() rather than comparing to the full "pathname". + // TODO(wafuwafu13): Enable this + // assert(err.path.startsWith(filename)); + })); +} + +// mkdirpSync dirname loop +// XXX: windows and smartos have issues removing a directory that you're in. +if (common.isMainThread && (common.isLinux || common.isOSX)) { + const pathname = path.join(tmpdir.path, nextdir()); + fs.mkdirSync(pathname); + process.chdir(pathname); + fs.rmdirSync(pathname); + assert.throws( + () => { fs.mkdirSync('X', common.mustNotMutateObjectDeep({ recursive: true })); }, + { + code: 'ENOENT', + message: /ENOENT: .*mkdir/, + name: 'Error', + syscall: 'mkdir', + } + ); + fs.mkdir('X', common.mustNotMutateObjectDeep({ recursive: true }), (err) => { + assert.strictEqual(err.code, 'ENOENT'); + // TODO(wafuwafu13): Enable this + // assert.strictEqual(err.syscall, 'mkdir'); + }); +} + +// mkdirSync and mkdir require options.recursive to be a boolean. +// Anything else generates an error. +{ + const pathname = path.join(tmpdir.path, nextdir()); + ['', 1, {}, [], null, Symbol('test'), () => {}].forEach((recursive) => { + const received = common.invalidArgTypeHelper(recursive); + assert.throws( + () => fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive }), common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.recursive" property must be of type boolean.' + + received + } + ); + assert.throws( + () => fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive })), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.recursive" property must be of type boolean.' + + received + } + ); + }); +} + +// `mkdirp` returns first folder created, when all folders are new. +{ + const dir1 = nextdir(); + const dir2 = nextdir(); + const firstPathCreated = path.join(tmpdir.path, dir1); + const pathname = path.join(tmpdir.path, dir1, dir2); + + fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err, path) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + // TODO(wafuwafu13): Enable this + // assert.strictEqual(path, firstPathCreated); + })); +} + +// `mkdirp` returns first folder created, when last folder is new. +{ + const dir1 = nextdir(); + const dir2 = nextdir(); + const pathname = path.join(tmpdir.path, dir1, dir2); + fs.mkdirSync(path.join(tmpdir.path, dir1)); + fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err, path) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + // TODO(wafuwafu13): Enable this + // assert.strictEqual(path, pathname); + })); +} + +// `mkdirp` returns undefined, when no new folders are created. +{ + const dir1 = nextdir(); + const dir2 = nextdir(); + const pathname = path.join(tmpdir.path, dir1, dir2); + fs.mkdirSync(path.join(tmpdir.path, dir1, dir2), common.mustNotMutateObjectDeep({ recursive: true })); + fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err, path) { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + assert.strictEqual(path, undefined); + })); +} + +// `mkdirp.sync` returns first folder created, when all folders are new. +{ + const dir1 = nextdir(); + const dir2 = nextdir(); + const firstPathCreated = path.join(tmpdir.path, dir1); + const pathname = path.join(tmpdir.path, dir1, dir2); + const p = fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true })); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + // TODO(wafuwafu13): Enable this + // assert.strictEqual(p, firstPathCreated); +} + +// `mkdirp.sync` returns first folder created, when last folder is new. +{ + const dir1 = nextdir(); + const dir2 = nextdir(); + const pathname = path.join(tmpdir.path, dir1, dir2); + fs.mkdirSync(path.join(tmpdir.path, dir1), common.mustNotMutateObjectDeep({ recursive: true })); + const p = fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true })); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + // TODO(wafuwafu13): Enable this + // assert.strictEqual(p, pathname); +} + +// `mkdirp.sync` returns undefined, when no new folders are created. +{ + const dir1 = nextdir(); + const dir2 = nextdir(); + const pathname = path.join(tmpdir.path, dir1, dir2); + fs.mkdirSync(path.join(tmpdir.path, dir1, dir2), common.mustNotMutateObjectDeep({ recursive: true })); + const p = fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true })); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + assert.strictEqual(p, undefined); +} + +// `mkdirp.promises` returns first folder created, when all folders are new. +{ + const dir1 = nextdir(); + const dir2 = nextdir(); + const firstPathCreated = path.join(tmpdir.path, dir1); + const pathname = path.join(tmpdir.path, dir1, dir2); + async function testCase() { + const p = await fs.promises.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true })); + assert.strictEqual(fs.existsSync(pathname), true); + assert.strictEqual(fs.statSync(pathname).isDirectory(), true); + // TODO(wafuwafu13): Enable this + // assert.strictEqual(p, firstPathCreated); + } + testCase(); +} + +// Keep the event loop alive so the async mkdir() requests +// have a chance to run (since they don't ref the event loop). +process.nextTick(() => {}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-open-flags.js b/cli/tests/node_compat/test/parallel/test-fs-open-flags.js new file mode 100644 index 00000000000000..adea1459d3373d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-open-flags.js @@ -0,0 +1,101 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); + +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +// 0 if not found in fs.constants +const { O_APPEND = 0, + O_CREAT = 0, + O_EXCL = 0, + O_RDONLY = 0, + O_RDWR = 0, + O_SYNC = 0, + O_DSYNC = 0, + O_TRUNC = 0, + O_WRONLY = 0 } = fs.constants; + +const { stringToFlags } = require('internal/fs/utils'); + +assert.strictEqual(stringToFlags('r'), O_RDONLY); +assert.strictEqual(stringToFlags('r+'), O_RDWR); +assert.strictEqual(stringToFlags('rs+'), O_RDWR | O_SYNC); +assert.strictEqual(stringToFlags('sr+'), O_RDWR | O_SYNC); +assert.strictEqual(stringToFlags('w'), O_TRUNC | O_CREAT | O_WRONLY); +assert.strictEqual(stringToFlags('w+'), O_TRUNC | O_CREAT | O_RDWR); +assert.strictEqual(stringToFlags('a'), O_APPEND | O_CREAT | O_WRONLY); +assert.strictEqual(stringToFlags('a+'), O_APPEND | O_CREAT | O_RDWR); + +assert.strictEqual(stringToFlags('wx'), O_TRUNC | O_CREAT | O_WRONLY | O_EXCL); +assert.strictEqual(stringToFlags('xw'), O_TRUNC | O_CREAT | O_WRONLY | O_EXCL); +assert.strictEqual(stringToFlags('wx+'), O_TRUNC | O_CREAT | O_RDWR | O_EXCL); +assert.strictEqual(stringToFlags('xw+'), O_TRUNC | O_CREAT | O_RDWR | O_EXCL); +assert.strictEqual(stringToFlags('ax'), O_APPEND | O_CREAT | O_WRONLY | O_EXCL); +assert.strictEqual(stringToFlags('xa'), O_APPEND | O_CREAT | O_WRONLY | O_EXCL); +assert.strictEqual(stringToFlags('as'), O_APPEND | O_CREAT | O_WRONLY | O_SYNC); +assert.strictEqual(stringToFlags('sa'), O_APPEND | O_CREAT | O_WRONLY | O_SYNC); +assert.strictEqual(stringToFlags('ax+'), O_APPEND | O_CREAT | O_RDWR | O_EXCL); +assert.strictEqual(stringToFlags('xa+'), O_APPEND | O_CREAT | O_RDWR | O_EXCL); +assert.strictEqual(stringToFlags('as+'), O_APPEND | O_CREAT | O_RDWR | O_SYNC); +assert.strictEqual(stringToFlags('sa+'), O_APPEND | O_CREAT | O_RDWR | O_SYNC); + +('+ +a +r +w rw wa war raw r++ a++ w++ x +x x+ rx rx+ wxx wax xwx xxx') + .split(' ') + .forEach(function(flags) { + assert.throws( + () => stringToFlags(flags), + { code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' } + ); + }); + +assert.throws( + () => stringToFlags({}), + { code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' } +); + +assert.throws( + () => stringToFlags(true), + { code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' } +); + +if (common.isLinux || common.isOSX) { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const file = path.join(tmpdir.path, 'a.js'); + fs.copyFileSync(fixtures.path('a.js'), file); + fs.open(file, O_DSYNC, common.mustSucceed((fd) => { + fs.closeSync(fd); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-open-mode-mask.js b/cli/tests/node_compat/test/parallel/test-fs-open-mode-mask.js new file mode 100644 index 00000000000000..d6ce372a466722 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-open-mode-mask.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This tests that the lower bits of mode > 0o777 still works in fs.open(). + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const mode = common.isWindows ? 0o444 : 0o644; + +const maskToIgnore = 0o10000; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function test(mode, asString) { + const suffix = asString ? 'str' : 'num'; + const input = asString ? + (mode | maskToIgnore).toString(8) : (mode | maskToIgnore); + + { + const file = path.join(tmpdir.path, `openSync-${suffix}.txt`); + const fd = fs.openSync(file, 'w+', input); + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.closeSync(fd); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + } + + { + const file = path.join(tmpdir.path, `open-${suffix}.txt`); + fs.open(file, 'w+', input, common.mustSucceed((fd) => { + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.closeSync(fd); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + })); + } +} + +test(mode, true); +test(mode, false); diff --git a/cli/tests/node_compat/test/parallel/test-fs-open-no-close.js b/cli/tests/node_compat/test/parallel/test-fs-open-no-close.js new file mode 100644 index 00000000000000..125c4835f38503 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-open-no-close.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Refs: https://github.com/nodejs/node/issues/34266 +// Failing to close a file should not keep the event loop open. + +const common = require('../common'); +const assert = require('assert'); + +const fs = require('fs'); + +const debuglog = (arg) => { + console.log(new Date().toLocaleString(), arg); +}; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +let openFd; + +fs.open(`${tmpdir.path}/dummy`, 'wx+', common.mustCall((err, fd) => { + debuglog('fs open() callback'); + assert.ifError(err); + openFd = fd; +})); +debuglog('waiting for callback'); + +process.on('beforeExit', common.mustCall(() => { + if (openFd) { + fs.closeSync(openFd); + } +})); diff --git a/cli/tests/node_compat/test/parallel/test-fs-open-numeric-flags.js b/cli/tests/node_compat/test/parallel/test-fs-open-numeric-flags.js new file mode 100644 index 00000000000000..9e0161ca1ba99c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-open-numeric-flags.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// O_WRONLY without O_CREAT shall fail with ENOENT +const pathNE = path.join(tmpdir.path, 'file-should-not-exist'); +assert.throws( + () => fs.openSync(pathNE, fs.constants.O_WRONLY), + (e) => e.code === 'ENOENT' +); diff --git a/cli/tests/node_compat/test/parallel/test-fs-open.js b/cli/tests/node_compat/test/parallel/test-fs-open.js new file mode 100644 index 00000000000000..631e96a2ef7852 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-open.js @@ -0,0 +1,128 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +let caughtException = false; + +try { + // Should throw ENOENT, not EBADF + // see https://github.com/joyent/node/pull/1228 + fs.openSync('/8hvftyuncxrt/path/to/file/that/does/not/exist', 'r'); +} catch (e) { + assert.strictEqual(e.code, 'ENOENT'); + caughtException = true; +} +assert.strictEqual(caughtException, true); + +fs.openSync(__filename); + +fs.open(__filename, common.mustSucceed()); + +fs.open(__filename, 'r', common.mustSucceed()); + +// TODO(wafuwafu13): Support 'rs' flag +// fs.open(__filename, 'rs', common.mustSucceed()); + +fs.open(__filename, 'r', 0, common.mustSucceed()); + +fs.open(__filename, 'r', null, common.mustSucceed()); + +async function promise() { + await fs.promises.open(__filename); + await fs.promises.open(__filename, 'r'); +} + +promise().then(common.mustCall()).catch(common.mustNotCall()); + +assert.throws( + () => fs.open(__filename, 'r', 'boom', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError' + } +); + +for (const extra of [[], ['r'], ['r', 0], ['r', 0, 'bad callback']]) { + assert.throws( + () => fs.open(__filename, ...extra), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +} + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.open(i, 'r', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.openSync(i, 'r', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.rejects( + fs.promises.open(i, 'r'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); + +// Check invalid modes. +[false, [], {}].forEach((mode) => { + assert.throws( + () => fs.open(__filename, 'r', mode, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ); + assert.throws( + () => fs.openSync(__filename, 'r', mode, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ); + assert.rejects( + fs.promises.open(__filename, 'r', mode), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-opendir.js b/cli/tests/node_compat/test/parallel/test-fs-opendir.js new file mode 100644 index 00000000000000..75c4aa074d5e0b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-opendir.js @@ -0,0 +1,300 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); + +const testDir = tmpdir.path; +const files = ['empty', 'files', 'for', 'just', 'testing']; + +// Make sure tmp directory is clean +tmpdir.refresh(); + +// Create the necessary files +files.forEach(function(filename) { + fs.closeSync(fs.openSync(path.join(testDir, filename), 'w')); +}); + +function assertDirent(dirent) { + assert(dirent instanceof fs.Dirent); + assert.strictEqual(dirent.isFile(), true); + assert.strictEqual(dirent.isDirectory(), false); + // TODO(wafuwafu13): Support these method + // assert.strictEqual(dirent.isSocket(), false); + // assert.strictEqual(dirent.isBlockDevice(), false); + // assert.strictEqual(dirent.isCharacterDevice(), false); + // assert.strictEqual(dirent.isFIFO(), false); + assert.strictEqual(dirent.isSymbolicLink(), false); +} + +// NOTE: this error doesn't occur in Deno +const dirclosedError = { + code: 'ERR_DIR_CLOSED' +}; + +// NOTE: this error doesn't occur in Deno +const dirconcurrentError = { + code: 'ERR_DIR_CONCURRENT_OPERATION' +}; + +const invalidCallbackObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}; + +// Check the opendir Sync version +{ + const dir = fs.opendirSync(testDir); + const entries = files.map(() => { + const dirent = dir.readSync(); + assertDirent(dirent); + return dirent.name; + }); + assert.deepStrictEqual(files, entries.sort()); + + // dir.read should return null when no more entries exist + assert.strictEqual(dir.readSync(), null); + + // check .path + assert.strictEqual(dir.path, testDir); + + dir.closeSync(); + + // assert.throws(() => dir.readSync(), dirclosedError); + // assert.throws(() => dir.closeSync(), dirclosedError); +} + +// Check the opendir async version +fs.opendir(testDir, common.mustSucceed((dir) => { + let sync = true; + dir.read(common.mustSucceed((dirent) => { + assert(!sync); + + // Order is operating / file system dependent + assert(files.includes(dirent.name), `'files' should include ${dirent}`); + assertDirent(dirent); + + let syncInner = true; + dir.read(common.mustSucceed((dirent) => { + assert(!syncInner); + + dir.close(common.mustSucceed()); + })); + syncInner = false; + })); + sync = false; +})); + +// opendir() on file should throw ENOTDIR +assert.throws(function() { + fs.opendirSync(__filename); +}, /Error: ENOTDIR: not a directory/); + +assert.throws(function() { + fs.opendir(__filename); +}, /TypeError \[ERR_INVALID_ARG_TYPE\]: The "callback" argument must be of type function/); + +fs.opendir(__filename, common.mustCall(function(e) { + assert.strictEqual(e.code, 'ENOTDIR'); +})); + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.opendir(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.opendirSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); + +// Promise-based tests +async function doPromiseTest() { + // Check the opendir Promise version + const dir = await fs.promises.opendir(testDir); + const entries = []; + + let i = files.length; + while (i--) { + const dirent = await dir.read(); + entries.push(dirent.name); + assertDirent(dirent); + } + + assert.deepStrictEqual(files, entries.sort()); + + // dir.read should return null when no more entries exist + assert.strictEqual(await dir.read(), null); + + await dir.close(); +} +doPromiseTest().then(common.mustCall()); + +// Async iterator +async function doAsyncIterTest() { + const entries = []; + for await (const dirent of await fs.promises.opendir(testDir)) { + entries.push(dirent.name); + assertDirent(dirent); + } + + assert.deepStrictEqual(files, entries.sort()); + + // Automatically closed during iterator +} +doAsyncIterTest().then(common.mustCall()); + +// Async iterators should do automatic cleanup + +async function doAsyncIterBreakTest() { + const dir = await fs.promises.opendir(testDir); + for await (const dirent of dir) { // eslint-disable-line no-unused-vars + break; + } + + // await assert.rejects(async () => dir.read(), dirclosedError); +} +doAsyncIterBreakTest().then(common.mustCall()); + +async function doAsyncIterReturnTest() { + const dir = await fs.promises.opendir(testDir); + await (async function() { + for await (const dirent of dir) { + return; + } + })(); + + // await assert.rejects(async () => dir.read(), dirclosedError); +} +doAsyncIterReturnTest().then(common.mustCall()); + +async function doAsyncIterThrowTest() { + const dir = await fs.promises.opendir(testDir); + try { + for await (const dirent of dir) { // eslint-disable-line no-unused-vars + throw new Error('oh no'); + } + } catch (err) { + if (err.message !== 'oh no') { + throw err; + } + } + + // await assert.rejects(async () => dir.read(), dirclosedError); +} +doAsyncIterThrowTest().then(common.mustCall()); + +// Check error thrown on invalid values of bufferSize +for (const bufferSize of [-1, 0, 0.5, 1.5, Infinity, NaN]) { + assert.throws( + () => fs.opendirSync(testDir, common.mustNotMutateObjectDeep({ bufferSize })), + { + code: 'ERR_OUT_OF_RANGE' + }); +} +for (const bufferSize of ['', '1', null]) { + assert.throws( + () => fs.opendirSync(testDir, common.mustNotMutateObjectDeep({ bufferSize })), + { + code: 'ERR_INVALID_ARG_TYPE' + }); +} + +// Check that passing a positive integer as bufferSize works +{ + const dir = fs.opendirSync(testDir, common.mustNotMutateObjectDeep({ bufferSize: 1024 })); + assertDirent(dir.readSync()); + dir.close(); +} + +// TODO(wafuwafu13): enable this +// // Check that when passing a string instead of function - throw an exception +// async function doAsyncIterInvalidCallbackTest() { +// const dir = await fs.promises.opendir(testDir); +// assert.throws(() => dir.close('not function'), invalidCallbackObj); +// } +// doAsyncIterInvalidCallbackTest().then(common.mustCall()); + +// Check first call to close() - should not report an error. +async function doAsyncIterDirClosedTest() { + const dir = await fs.promises.opendir(testDir); + await dir.close(); + // await assert.rejects(() => dir.close(), dirclosedError); +} +doAsyncIterDirClosedTest().then(common.mustCall()); + +// Check that readSync() and closeSync() during read() throw exceptions +async function doConcurrentAsyncAndSyncOps() { + const dir = await fs.promises.opendir(testDir); + const promise = dir.read(); + + // assert.throws(() => dir.closeSync(), dirconcurrentError); + // assert.throws(() => dir.readSync(), dirconcurrentError); + + await promise; + dir.closeSync(); +} +doConcurrentAsyncAndSyncOps().then(common.mustCall()); + +// TODO(wafuwafu13): enable this +// // Check read throw exceptions on invalid callback +// { +// const dir = fs.opendirSync(testDir); +// assert.throws(() => dir.read('INVALID_CALLBACK'), /ERR_INVALID_ARG_TYPE/); +// } + +// Check that concurrent read() operations don't do weird things. +async function doConcurrentAsyncOps() { + const dir = await fs.promises.opendir(testDir); + const promise1 = dir.read(); + const promise2 = dir.read(); + + assertDirent(await promise1); + assertDirent(await promise2); + dir.closeSync(); +} +doConcurrentAsyncOps().then(common.mustCall()); + +// Check that concurrent read() + close() operations don't do weird things. +async function doConcurrentAsyncMixedOps() { + const dir = await fs.promises.opendir(testDir); + const promise1 = dir.read(); + const promise2 = dir.close(); + + assertDirent(await promise1); + await promise2; +} +doConcurrentAsyncMixedOps().then(common.mustCall()); + +// Check if directory already closed - the callback should pass an error. +{ + const dir = fs.opendirSync(testDir); + dir.closeSync(); + dir.close(common.mustCall((error) => { + // assert.strictEqual(error.code, dirclosedError.code); + })); +} + +// Check if directory already closed - throw an promise exception. +{ + const dir = fs.opendirSync(testDir); + dir.closeSync(); + // assert.rejects(dir.close(), dirclosedError).then(common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-autoClose.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-autoClose.js new file mode 100644 index 00000000000000..7e7e3e0795c921 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-autoClose.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const writeFile = path.join(tmpdir.path, 'write-autoClose.txt'); +tmpdir.refresh(); + +const file = fs.createWriteStream(writeFile, { autoClose: true }); + +file.on('finish', common.mustCall(() => { + assert.strictEqual(file.destroyed, false); +})); +file.end('asd'); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-concurrent-reads.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-concurrent-reads.js new file mode 100644 index 00000000000000..fd4490d45bfa26 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-concurrent-reads.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); + +// Test that concurrent file read streams don’t interfere with each other’s +// contents, and that the chunks generated by the reads only retain a +// 'reasonable' amount of memory. + +// Refs: https://github.com/nodejs/node/issues/21967 + +const filename = fixtures.path('loop.js'); // Some small non-homogeneous file. +const content = fs.readFileSync(filename); + +const N = 2000; +let started = 0; +let done = 0; + +const arrayBuffers = new Set(); + +function startRead() { + ++started; + const chunks = []; + fs.createReadStream(filename) + .on('data', (chunk) => { + chunks.push(chunk); + arrayBuffers.add(chunk.buffer); + }) + .on('end', common.mustCall(() => { + if (started < N) + startRead(); + assert.deepStrictEqual(Buffer.concat(chunks), content); + if (++done === N) { + const retainedMemory = + [...arrayBuffers].map((ab) => ab.byteLength).reduce((a, b) => a + b); + assert(retainedMemory / (N * content.length) <= 3, + `Retaining ${retainedMemory} bytes in ABs for ${N} ` + + `chunks of size ${content.length}`); + } + })); +} + +// Don’t start the reads all at once – that way we would have to allocate +// a large amount of memory upfront. +for (let i = 0; i < 6; ++i) + startRead(); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-double-close.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-double-close.js new file mode 100644 index 00000000000000..e0e2222a71cd18 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-double-close.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const fs = require('fs'); + +{ + const s = fs.createReadStream(__filename); + + s.close(common.mustCall()); + s.close(common.mustCall()); +} + +{ + const s = fs.createReadStream(__filename); + + // This is a private API, but it is worth testing. close calls this + s.destroy(null, common.mustCall()); + s.destroy(null, common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-encoding.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-encoding.js new file mode 100644 index 00000000000000..a23bebc32d185d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-encoding.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const stream = require('stream'); +const fixtures = require('../common/fixtures'); +const encoding = 'base64'; + +const example = fixtures.path('x.txt'); +const assertStream = new stream.Writable({ + write: function(chunk, enc, next) { + const expected = Buffer.from('xyz'); + assert(chunk.equals(expected)); + } +}); +assertStream.setDefaultEncoding(encoding); +fs.createReadStream(example, encoding).pipe(assertStream); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-fd.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-fd.js new file mode 100644 index 00000000000000..9e895dcec567d6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-fd.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const file = path.join(tmpdir.path, '/read_stream_fd_test.txt'); +const input = 'hello world'; + +let output = ''; +tmpdir.refresh(); +fs.writeFileSync(file, input); + +const fd = fs.openSync(file, 'r'); +const stream = fs.createReadStream(null, { fd: fd, encoding: 'utf8' }); + +assert.strictEqual(stream.path, undefined); + +stream.on('data', common.mustCallAtLeast((data) => { + output += data; +})); + +process.on('exit', () => { + assert.strictEqual(output, input); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-inherit.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-inherit.js new file mode 100644 index 00000000000000..a498c5ade68c27 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-inherit.js @@ -0,0 +1,212 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('elipses.txt'); +const rangeFile = fixtures.path('x.txt'); + +{ + let paused = false; + + const file = fs.ReadStream(fn); + + file.on('open', common.mustCall(function(fd) { + file.length = 0; + assert.strictEqual(typeof fd, 'number'); + assert.ok(file.readable); + + // GH-535 + file.pause(); + file.resume(); + file.pause(); + file.resume(); + })); + + file.on('data', common.mustCallAtLeast(function(data) { + assert.ok(data instanceof Buffer); + assert.ok(!paused); + file.length += data.length; + + paused = true; + file.pause(); + + setTimeout(function() { + paused = false; + file.resume(); + }, 10); + })); + + + file.on('end', common.mustCall()); + + + file.on('close', common.mustCall(function() { + assert.strictEqual(file.length, 30000); + })); +} + +{ + const file = fs.createReadStream(fn, Object.create({ encoding: 'utf8' })); + file.length = 0; + file.on('data', function(data) { + assert.strictEqual(typeof data, 'string'); + file.length += data.length; + + for (let i = 0; i < data.length; i++) { + // http://www.fileformat.info/info/unicode/char/2026/index.htm + assert.strictEqual(data[i], '\u2026'); + } + }); + + file.on('close', common.mustCall(function() { + assert.strictEqual(file.length, 10000); + })); +} + +{ + const options = Object.create({ bufferSize: 1, start: 1, end: 2 }); + const file = fs.createReadStream(rangeFile, options); + assert.strictEqual(file.start, 1); + assert.strictEqual(file.end, 2); + let contentRead = ''; + file.on('data', function(data) { + contentRead += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(contentRead, 'yz'); + })); +} + +{ + const options = Object.create({ bufferSize: 1, start: 1 }); + const file = fs.createReadStream(rangeFile, options); + assert.strictEqual(file.start, 1); + file.data = ''; + file.on('data', function(data) { + file.data += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(file.data, 'yz\n'); + })); +} + +// https://github.com/joyent/node/issues/2320 +{ + const options = Object.create({ bufferSize: 1.23, start: 1 }); + const file = fs.createReadStream(rangeFile, options); + assert.strictEqual(file.start, 1); + file.data = ''; + file.on('data', function(data) { + file.data += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(file.data, 'yz\n'); + })); +} + +{ + const message = + 'The value of "start" is out of range. It must be <= "end" (here: 2).' + + ' Received 10'; + + assert.throws( + () => { + fs.createReadStream(rangeFile, Object.create({ start: 10, end: 2 })); + }, + { + code: 'ERR_OUT_OF_RANGE', + message, + name: 'RangeError' + }); +} + +{ + const options = Object.create({ start: 0, end: 0 }); + const stream = fs.createReadStream(rangeFile, options); + assert.strictEqual(stream.start, 0); + assert.strictEqual(stream.end, 0); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual(stream.data, 'x'); + })); +} + +// Pause and then resume immediately. +{ + const pauseRes = fs.createReadStream(rangeFile); + pauseRes.pause(); + pauseRes.resume(); +} + +{ + let data = ''; + let file = + fs.createReadStream(rangeFile, Object.create({ autoClose: false })); + assert.strictEqual(file.autoClose, false); + file.on('data', (chunk) => { data += chunk; }); + file.on('end', common.mustCall(function() { + process.nextTick(common.mustCall(function() { + assert(!file.closed); + assert(!file.destroyed); + assert.strictEqual(data, 'xyz\n'); + fileNext(); + })); + })); + + function fileNext() { + // This will tell us if the fd is usable again or not. + file = fs.createReadStream(null, Object.create({ fd: file.fd, start: 0 })); + file.data = ''; + file.on('data', function(data) { + file.data += data; + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(file.data, 'xyz\n'); + })); + } + process.on('exit', function() { + assert(file.closed); + assert(file.destroyed); + }); +} + +// Just to make sure autoClose won't close the stream because of error. +{ + const options = Object.create({ fd: 13337, autoClose: false }); + const file = fs.createReadStream(null, options); + file.on('data', common.mustNotCall()); + file.on('error', common.mustCall()); + process.on('exit', function() { + assert(!file.closed); + assert(!file.destroyed); + assert(file.fd); + }); +} + +// Make sure stream is destroyed when file does not exist. +{ + const file = fs.createReadStream('/path/to/file/that/does/not/exist'); + file.on('data', common.mustNotCall()); + file.on('error', common.mustCall()); + + process.on('exit', function() { + assert(file.closed); + assert(file.destroyed); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-patch-open.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-patch-open.js new file mode 100644 index 00000000000000..b2b12ad53af2b0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-patch-open.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const fs = require('fs'); + +common.expectWarning( + 'DeprecationWarning', + 'ReadStream.prototype.open() is deprecated', 'DEP0135'); +const s = fs.createReadStream('asd') + // We don't care about errors in this test. + .on('error', () => {}); +s.open(); + +process.nextTick(() => { + // Allow overriding open(). + fs.ReadStream.prototype.open = common.mustCall(); + fs.createReadStream('asd'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-resume.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-resume.js new file mode 100644 index 00000000000000..f999f4b860fadb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-resume.js @@ -0,0 +1,59 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +const fs = require('fs'); + +const file = fixtures.path('x.txt'); +let data = ''; +let first = true; + +const stream = fs.createReadStream(file); +stream.setEncoding('utf8'); +stream.on('data', common.mustCallAtLeast(function(chunk) { + data += chunk; + if (first) { + first = false; + stream.resume(); + } +})); + +process.nextTick(function() { + stream.pause(); + setTimeout(function() { + stream.resume(); + }, 100); +}); + +process.on('exit', function() { + assert.strictEqual(data, 'xyz\n'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream-throw-type-error.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream-throw-type-error.js new file mode 100644 index 00000000000000..f7ee1a087abb4f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream-throw-type-error.js @@ -0,0 +1,84 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); + +// This test ensures that appropriate TypeError is thrown by createReadStream +// when an argument with invalid type is passed + +const example = fixtures.path('x.txt'); +// Should not throw. +fs.createReadStream(example, undefined); +fs.createReadStream(example, null); +fs.createReadStream(example, 'utf8'); +fs.createReadStream(example, { encoding: 'utf8' }); + +const createReadStreamErr = (path, opt, error) => { + assert.throws(() => { + fs.createReadStream(path, opt); + }, error); +}; + +const typeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}; + +const rangeError = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError' +}; + +[123, 0, true, false].forEach((opts) => + createReadStreamErr(example, opts, typeError) +); + +// Case 0: Should not throw if either start or end is undefined +[{}, { start: 0 }, { end: Infinity }].forEach((opts) => + fs.createReadStream(example, opts) +); + +// Case 1: Should throw TypeError if either start or end is not of type 'number' +[ + { start: 'invalid' }, + { end: 'invalid' }, + { start: 'invalid', end: 'invalid' }, +].forEach((opts) => createReadStreamErr(example, opts, typeError)); + +// Case 2: Should throw RangeError if either start or end is NaN +[{ start: NaN }, { end: NaN }, { start: NaN, end: NaN }].forEach((opts) => + createReadStreamErr(example, opts, rangeError) +); + +// Case 3: Should throw RangeError if either start or end is negative +[{ start: -1 }, { end: -1 }, { start: -1, end: -1 }].forEach((opts) => + createReadStreamErr(example, opts, rangeError) +); + +// Case 4: Should throw RangeError if either start or end is fractional +[{ start: 0.1 }, { end: 0.1 }, { start: 0.1, end: 0.1 }].forEach((opts) => + createReadStreamErr(example, opts, rangeError) +); + +// Case 5: Should not throw if both start and end are whole numbers +fs.createReadStream(example, { start: 1, end: 5 }); + +// Case 6: Should throw RangeError if start is greater than end +createReadStreamErr(example, { start: 5, end: 1 }, rangeError); + +// Case 7: Should throw RangeError if start or end is not safe integer +const NOT_SAFE_INTEGER = 2 ** 53; +[ + { start: NOT_SAFE_INTEGER, end: Infinity }, + { start: 0, end: NOT_SAFE_INTEGER }, +].forEach((opts) => + createReadStreamErr(example, opts, rangeError) +); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-stream.js b/cli/tests/node_compat/test/parallel/test-fs-read-stream.js new file mode 100644 index 00000000000000..6c1c512fbfd787 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-stream.js @@ -0,0 +1,284 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +const child_process = require('child_process'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('elipses.txt'); +const rangeFile = fixtures.path('x.txt'); + +function test1(options) { + let paused = false; + let bytesRead = 0; + + const file = fs.createReadStream(fn, options); + const fileSize = fs.statSync(fn).size; + + assert.strictEqual(file.bytesRead, 0); + + file.on('open', common.mustCall(function(fd) { + file.length = 0; + assert.strictEqual(typeof fd, 'number'); + assert.strictEqual(file.bytesRead, 0); + assert.ok(file.readable); + + // GH-535 + file.pause(); + file.resume(); + file.pause(); + file.resume(); + })); + + file.on('data', function(data) { + assert.ok(data instanceof Buffer); + assert.ok(data.byteOffset % 8 === 0); + assert.ok(!paused); + file.length += data.length; + + bytesRead += data.length; + assert.strictEqual(file.bytesRead, bytesRead); + + paused = true; + file.pause(); + + setTimeout(function() { + paused = false; + file.resume(); + }, 10); + }); + + + file.on('end', common.mustCall(function(chunk) { + assert.strictEqual(bytesRead, fileSize); + assert.strictEqual(file.bytesRead, fileSize); + })); + + + file.on('close', common.mustCall(function() { + assert.strictEqual(bytesRead, fileSize); + assert.strictEqual(file.bytesRead, fileSize); + })); + + process.on('exit', function() { + assert.strictEqual(file.length, 30000); + }); +} + +test1({}); +test1({ + fs: { + open: common.mustCall(fs.open), + read: common.mustCallAtLeast(fs.read, 1), + close: common.mustCall(fs.close), + } +}); + +{ + const file = fs.createReadStream(fn, common.mustNotMutateObjectDeep({ encoding: 'utf8' })); + file.length = 0; + file.on('data', function(data) { + assert.strictEqual(typeof data, 'string'); + file.length += data.length; + + for (let i = 0; i < data.length; i++) { + // http://www.fileformat.info/info/unicode/char/2026/index.htm + assert.strictEqual(data[i], '\u2026'); + } + }); + + file.on('close', common.mustCall()); + + process.on('exit', function() { + assert.strictEqual(file.length, 10000); + }); +} + +{ + const file = + fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ bufferSize: 1, start: 1, end: 2 })); + let contentRead = ''; + file.on('data', function(data) { + contentRead += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function(data) { + assert.strictEqual(contentRead, 'yz'); + })); +} + +{ + const file = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ bufferSize: 1, start: 1 })); + file.data = ''; + file.on('data', function(data) { + file.data += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(file.data, 'yz\n'); + })); +} + +{ + // Ref: https://github.com/nodejs/node-v0.x-archive/issues/2320 + const file = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ bufferSize: 1.23, start: 1 })); + file.data = ''; + file.on('data', function(data) { + file.data += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(file.data, 'yz\n'); + })); +} + +assert.throws( + () => { + fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ start: 10, end: 2 })); + }, + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "start" is out of range. It must be <= "end"' + + ' (here: 2). Received 10', + name: 'RangeError' + }); + +{ + const stream = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ start: 0, end: 0 })); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual(stream.data, 'x'); + })); +} + +{ + // Verify that end works when start is not specified. + const stream = new fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ end: 1 })); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual(stream.data, 'xy'); + })); +} + +if (!common.isWindows) { + // Verify that end works when start is not specified, and we do not try to + // use positioned reads. This makes sure that this keeps working for + // non-seekable file descriptors. + tmpdir.refresh(); + const filename = `${tmpdir.path}/foo.pipe`; + const mkfifoResult = child_process.spawnSync('mkfifo', [filename]); + if (!mkfifoResult.error) { + child_process.exec(`echo "xyz foobar" > '${filename}'`); + const stream = new fs.createReadStream(filename, common.mustNotMutateObjectDeep({ end: 1 })); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual(stream.data, 'xy'); + fs.unlinkSync(filename); + })); + } else { + common.printSkipMessage('mkfifo not available'); + } +} + +{ + // Pause and then resume immediately. + const pauseRes = fs.createReadStream(rangeFile); + pauseRes.pause(); + pauseRes.resume(); +} + +{ + let file = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ autoClose: false })); + let data = ''; + file.on('data', function(chunk) { data += chunk; }); + file.on('end', common.mustCall(function() { + assert.strictEqual(data, 'xyz\n'); + process.nextTick(function() { + assert(!file.closed); + assert(!file.destroyed); + fileNext(); + }); + })); + + function fileNext() { + // This will tell us if the fd is usable again or not. + file = fs.createReadStream(null, common.mustNotMutateObjectDeep({ fd: file.fd, start: 0 })); + file.data = ''; + file.on('data', function(data) { + file.data += data; + }); + file.on('end', common.mustCall(function(err) { + assert.strictEqual(file.data, 'xyz\n'); + })); + process.on('exit', function() { + assert(file.closed); + assert(file.destroyed); + }); + } +} + +{ + // Just to make sure autoClose won't close the stream because of error. + const file = fs.createReadStream(null, common.mustNotMutateObjectDeep({ fd: 13337, autoClose: false })); + file.on('data', common.mustNotCall()); + file.on('error', common.mustCall()); + process.on('exit', function() { + assert(!file.closed); + assert(!file.destroyed); + assert(file.fd); + }); +} + +{ + // Make sure stream is destroyed when file does not exist. + const file = fs.createReadStream('/path/to/file/that/does/not/exist'); + file.on('data', common.mustNotCall()); + file.on('error', common.mustCall()); + + process.on('exit', function() { + assert(file.closed); + assert(file.destroyed); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-type.js b/cli/tests/node_compat/test/parallel/test-fs-read-type.js new file mode 100644 index 00000000000000..5eceb7627de812 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-type.js @@ -0,0 +1,250 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const filepath = fixtures.path('x.txt'); +const fd = fs.openSync(filepath, 'r'); +const expected = 'xyz\n'; + + +// Error must be thrown with string +assert.throws( + () => fs.read(fd, expected.length, 0, 'utf-8', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer" argument must be an instance of Buffer, ' + + 'TypedArray, or DataView. Received type number (4)' + } +); + +[true, null, undefined, () => {}, {}].forEach((value) => { + assert.throws(() => { + fs.read(value, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + 0, + common.mustNotCall()); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +}); + +assert.throws(() => { + fs.read(fd, + Buffer.allocUnsafe(expected.length), + -1, + expected.length, + 0, + common.mustNotCall()); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}); + +assert.throws(() => { + fs.read(fd, + Buffer.allocUnsafe(expected.length), + NaN, + expected.length, + 0, + common.mustNotCall()); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. It must be an integer. ' + + 'Received NaN' +}); + +assert.throws(() => { + fs.read(fd, + Buffer.allocUnsafe(expected.length), + 0, + -1, + 0, + common.mustNotCall()); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "length" is out of range. ' + + 'It must be >= 0. Received -1' +}); + +[true, () => {}, {}, ''].forEach((value) => { + assert.throws(() => { + fs.read(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + value, + common.mustNotCall()); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +}); + +[0.5, 2 ** 53, 2n ** 63n].forEach((value) => { + assert.throws(() => { + fs.read(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + value, + common.mustNotCall()); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError' + }); +}); + +fs.read(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + 0n, + common.mustSucceed()); + +fs.read(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + 2n ** 53n - 1n, + common.mustCall((err) => { + if (err) { + if (common.isIBMi) + assert.strictEqual(err.code, 'EOVERFLOW'); + else + assert.strictEqual(err.code, 'EFBIG'); + } + })); + +assert.throws( + () => fs.readSync(fd, expected.length, 0, 'utf-8'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer" argument must be an instance of Buffer, ' + + 'TypedArray, or DataView. Received type number (4)' + } +); + +[true, null, undefined, () => {}, {}].forEach((value) => { + assert.throws(() => { + fs.readSync(value, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + 0); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +}); + +assert.throws(() => { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + -1, + expected.length, + 0); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}); + +assert.throws(() => { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + NaN, + expected.length, + 0); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. It must be an integer. ' + + 'Received NaN' +}); + +assert.throws(() => { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + 0, + -1, + 0); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "length" is out of range. ' + + 'It must be >= 0. Received -1' +}); + +assert.throws(() => { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length + 1, + 0); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "length" is out of range. ' + + 'It must be <= 4. Received 5' +}); + +[true, () => {}, {}, ''].forEach((value) => { + assert.throws(() => { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + value); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +}); + +[0.5, 2 ** 53, 2n ** 63n].forEach((value) => { + assert.throws(() => { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + value); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError' + }); +}); + +fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + 0n); + +try { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), + 0, + expected.length, + 2n ** 53n - 1n); +} catch (err) { + // On systems where max file size is below 2^53-1, we'd expect a EFBIG error. + // This is not using `assert.throws` because the above call should not raise + // any error on systems that allows file of that size. + if (err.code !== 'EFBIG' && !(common.isIBMi && err.code === 'EOVERFLOW')) + throw err; +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-read-zero-length.js b/cli/tests/node_compat/test/parallel/test-fs-read-zero-length.js new file mode 100644 index 00000000000000..9c514a70bf5d9c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read-zero-length.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); +const filepath = fixtures.path('x.txt'); +const fd = fs.openSync(filepath, 'r'); +const bufferAsync = Buffer.alloc(0); +const bufferSync = Buffer.alloc(0); + +fs.read(fd, bufferAsync, 0, 0, 0, common.mustCall((err, bytesRead) => { + assert.strictEqual(bytesRead, 0); + assert.deepStrictEqual(bufferAsync, Buffer.alloc(0)); +})); + +const r = fs.readSync(fd, bufferSync, 0, 0, 0); +assert.deepStrictEqual(bufferSync, Buffer.alloc(0)); +assert.strictEqual(r, 0); diff --git a/cli/tests/node_compat/test/parallel/test-fs-read.js b/cli/tests/node_compat/test/parallel/test-fs-read.js new file mode 100644 index 00000000000000..cf49366b1d0b5f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-read.js @@ -0,0 +1,109 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); +const filepath = fixtures.path('x.txt'); +const fd = fs.openSync(filepath, 'r'); + +const expected = Buffer.from('xyz\n'); + +function test(bufferAsync, bufferSync, expected) { + fs.read(fd, + bufferAsync, + 0, + expected.length, + 0, + common.mustSucceed((bytesRead) => { + assert.strictEqual(bytesRead, expected.length); + assert.deepStrictEqual(bufferAsync, expected); + })); + + const r = fs.readSync(fd, bufferSync, 0, expected.length, 0); + assert.deepStrictEqual(bufferSync, expected); + assert.strictEqual(r, expected.length); +} + +test(Buffer.allocUnsafe(expected.length), + Buffer.allocUnsafe(expected.length), + expected); + +test(new Uint8Array(expected.length), + new Uint8Array(expected.length), + Uint8Array.from(expected)); + +{ + // Reading beyond file length (3 in this case) should return no data. + // This is a test for a bug where reads > uint32 would return data + // from the current position in the file. + const pos = 0xffffffff + 1; // max-uint32 + 1 + const nRead = fs.readSync(fd, Buffer.alloc(1), 0, 1, pos); + assert.strictEqual(nRead, 0); + + fs.read(fd, Buffer.alloc(1), 0, 1, pos, common.mustSucceed((nRead) => { + assert.strictEqual(nRead, 0); + })); +} + +assert.throws(() => new fs.Dir(), { + code: 'ERR_MISSING_ARGS', +}); + +assert.throws( + () => fs.read(fd, Buffer.alloc(1), 0, 1, 0), + { + code: 'ERR_INVALID_ARG_TYPE', + } +); + +assert.throws( + () => fs.read(fd, { buffer: null }, common.mustNotCall()), + /TypeError: Cannot read properties of null \(reading 'byteLength'\)/, + 'throws when options.buffer is null' +); + +assert.throws( + () => fs.readSync(fd, { buffer: null }), + { + name: 'TypeError', + message: 'The "buffer" argument must be an instance of Buffer, ' + + 'TypedArray, or DataView. Received an instance of Object', + }, + 'throws when options.buffer is null' +); + +assert.throws( + () => fs.read(null, Buffer.alloc(1), 0, 1, 0), + { + message: 'The "fd" argument must be of type number. Received null', + code: 'ERR_INVALID_ARG_TYPE', + } +); diff --git a/cli/tests/node_compat/test/parallel/test-fs-readdir-stack-overflow.js b/cli/tests/node_compat/test/parallel/test-fs-readdir-stack-overflow.js new file mode 100644 index 00000000000000..f25933d0d16eb1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-readdir-stack-overflow.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +const assert = require('assert'); +const fs = require('fs'); + +function recurse() { + fs.readdirSync('.'); + recurse(); +} + +assert.throws( + () => recurse(), + { + name: 'RangeError', + message: 'Maximum call stack size exceeded' + } +); diff --git a/cli/tests/node_compat/test/parallel/test-fs-readdir.js b/cli/tests/node_compat/test/parallel/test-fs-readdir.js new file mode 100644 index 00000000000000..98de468bd37a8c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-readdir.js @@ -0,0 +1,60 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +const readdirDir = tmpdir.path; +const files = ['empty', 'files', 'for', 'just', 'testing']; + +// Make sure tmp directory is clean +tmpdir.refresh(); + +// Create the necessary files +files.forEach(function(currentFile) { + fs.closeSync(fs.openSync(`${readdirDir}/${currentFile}`, 'w')); +}); + +// Check the readdir Sync version +assert.deepStrictEqual(files, fs.readdirSync(readdirDir).sort()); + +// Check the readdir async version +fs.readdir(readdirDir, common.mustSucceed((f) => { + assert.deepStrictEqual(files, f.sort()); +})); + +// readdir() on file should throw ENOTDIR +// https://github.com/joyent/node/issues/1869 +assert.throws(function() { + fs.readdirSync(__filename); +}, /Error: ENOTDIR: not a directory/); + +fs.readdir(__filename, common.mustCall(function(e) { + assert.strictEqual(e.code, 'ENOTDIR'); +})); + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.readdir(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.readdirSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-readfile-empty.js b/cli/tests/node_compat/test/parallel/test-fs-readfile-empty.js new file mode 100644 index 00000000000000..82dd70eeeff666 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-readfile-empty.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// Trivial test of fs.readFile on an empty file. +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('empty.txt'); + +fs.readFile(fn, common.mustCall((err, data) => { + assert.ok(data); +})); + +fs.readFile(fn, 'utf8', common.mustCall((err, data) => { + assert.strictEqual(data, ''); +})); + +fs.readFile(fn, { encoding: 'utf8' }, common.mustCall((err, data) => { + assert.strictEqual(data, ''); +})); + +assert.ok(fs.readFileSync(fn)); +assert.strictEqual(fs.readFileSync(fn, 'utf8'), ''); diff --git a/cli/tests/node_compat/test/parallel/test-fs-realpath-native.js b/cli/tests/node_compat/test/parallel/test-fs-realpath-native.js new file mode 100644 index 00000000000000..d99a2e559502d2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-realpath-native.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const filename = __filename.toLowerCase(); + +assert.strictEqual( + fs.realpathSync.native('./test/parallel/test-fs-realpath-native.js') + .toLowerCase(), + filename); + +fs.realpath.native( + './test/parallel/test-fs-realpath-native.js', + common.mustSucceed(function(res) { + assert.strictEqual(res.toLowerCase(), filename); + assert.strictEqual(this, undefined); + })); diff --git a/cli/tests/node_compat/test/parallel/test-fs-rm.js b/cli/tests/node_compat/test/parallel/test-fs-rm.js new file mode 100644 index 00000000000000..d1b29b623a1041 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rm.js @@ -0,0 +1,433 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const { pathToFileURL } = require('url'); +const { execSync } = require('child_process'); + +const { validateRmOptionsSync } = require('internal/fs/utils'); + +tmpdir.refresh(); + +let count = 0; +const nextDirPath = (name = 'rm') => + path.join(tmpdir.path, `${name}-${count++}`); + +const isGitPresent = (() => { + try { execSync('git --version'); return true; } catch { return false; } +})(); + +function gitInit(gitDirectory) { + fs.mkdirSync(gitDirectory); + execSync('git init', common.mustNotMutateObjectDeep({ cwd: gitDirectory })); +} + +function makeNonEmptyDirectory(depth, files, folders, dirname, createSymLinks) { + fs.mkdirSync(dirname, common.mustNotMutateObjectDeep({ recursive: true })); + fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8'); + + const options = common.mustNotMutateObjectDeep({ flag: 'wx' }); + + for (let f = files; f > 0; f--) { + fs.writeFileSync(path.join(dirname, `f-${depth}-${f}`), '', options); + } + + if (createSymLinks) { + // Valid symlink + fs.symlinkSync( + `f-${depth}-1`, + path.join(dirname, `link-${depth}-good`), + 'file' + ); + + // Invalid symlink + fs.symlinkSync( + 'does-not-exist', + path.join(dirname, `link-${depth}-bad`), + 'file' + ); + } + + // File with a name that looks like a glob + fs.writeFileSync(path.join(dirname, '[a-z0-9].txt'), '', options); + + depth--; + if (depth <= 0) { + return; + } + + for (let f = folders; f > 0; f--) { + fs.mkdirSync( + path.join(dirname, `folder-${depth}-${f}`), + { recursive: true } + ); + makeNonEmptyDirectory( + depth, + files, + folders, + path.join(dirname, `d-${depth}-${f}`), + createSymLinks + ); + } +} + +function removeAsync(dir) { + // Removal should fail without the recursive option. + fs.rm(dir, common.mustCall((err) => { + assert.strictEqual(err.syscall, 'rm'); + + // Removal should fail without the recursive option set to true. + fs.rm(dir, common.mustNotMutateObjectDeep({ recursive: false }), common.mustCall((err) => { + assert.strictEqual(err.syscall, 'rm'); + + // Recursive removal should succeed. + fs.rm(dir, common.mustNotMutateObjectDeep({ recursive: true }), common.mustSucceed(() => { + + // Attempted removal should fail now because the directory is gone. + fs.rm(dir, common.mustCall((err) => { + assert.strictEqual(err.syscall, 'stat'); + })); + })); + })); + })); +} + +// Test the asynchronous version +{ + // Create a 4-level folder hierarchy including symlinks + let dir = nextDirPath(); + makeNonEmptyDirectory(4, 10, 2, dir, true); + removeAsync(dir); + + // Create a 2-level folder hierarchy without symlinks + dir = nextDirPath(); + makeNonEmptyDirectory(2, 10, 2, dir, false); + removeAsync(dir); + + // Same test using URL instead of a path + dir = nextDirPath(); + makeNonEmptyDirectory(2, 10, 2, dir, false); + removeAsync(pathToFileURL(dir)); + + // Create a flat folder including symlinks + dir = nextDirPath(); + makeNonEmptyDirectory(1, 10, 2, dir, true); + removeAsync(dir); + + // Should fail if target does not exist + fs.rm( + path.join(tmpdir.path, 'noexist.txt'), + common.mustNotMutateObjectDeep({ recursive: true }), + common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOENT'); + }) + ); + + // Should delete a file + const filePath = path.join(tmpdir.path, 'rm-async-file.txt'); + fs.writeFileSync(filePath, ''); + fs.rm(filePath, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => { + try { + assert.strictEqual(err, null); + assert.strictEqual(fs.existsSync(filePath), false); + } finally { + fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true })); + } + })); +} + +// Removing a .git directory should not throw an EPERM. +// Refs: https://github.com/isaacs/rimraf/issues/21. +if (isGitPresent) { + const gitDirectory = nextDirPath(); + gitInit(gitDirectory); + fs.rm(gitDirectory, common.mustNotMutateObjectDeep({ recursive: true }), common.mustSucceed(() => { + assert.strictEqual(fs.existsSync(gitDirectory), false); + })); +} + +// Test the synchronous version. +{ + const dir = nextDirPath(); + makeNonEmptyDirectory(4, 10, 2, dir, true); + + // Removal should fail without the recursive option set to true. + assert.throws(() => { + fs.rmSync(dir); + }, { syscall: 'rm' }); + assert.throws(() => { + fs.rmSync(dir, common.mustNotMutateObjectDeep({ recursive: false })); + }, { syscall: 'rm' }); + + // Should fail if target does not exist + assert.throws(() => { + fs.rmSync(path.join(tmpdir.path, 'noexist.txt'), common.mustNotMutateObjectDeep({ recursive: true })); + }, { + code: 'ENOENT', + name: 'Error', + message: /^ENOENT: no such file or directory, stat/ + }); + + // Should delete a file + const filePath = path.join(tmpdir.path, 'rm-file.txt'); + fs.writeFileSync(filePath, ''); + + try { + fs.rmSync(filePath, common.mustNotMutateObjectDeep({ recursive: true })); + } finally { + fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true })); + } + + // Should accept URL + const fileURL = pathToFileURL(path.join(tmpdir.path, 'rm-file.txt')); + fs.writeFileSync(fileURL, ''); + + try { + fs.rmSync(fileURL, common.mustNotMutateObjectDeep({ recursive: true })); + } finally { + fs.rmSync(fileURL, common.mustNotMutateObjectDeep({ force: true })); + } + + // Recursive removal should succeed. + fs.rmSync(dir, { recursive: true }); + + // Attempted removal should fail now because the directory is gone. + assert.throws(() => fs.rmSync(dir), { syscall: 'stat' }); +} + +// Removing a .git directory should not throw an EPERM. +// Refs: https://github.com/isaacs/rimraf/issues/21. +if (isGitPresent) { + const gitDirectory = nextDirPath(); + gitInit(gitDirectory); + fs.rmSync(gitDirectory, common.mustNotMutateObjectDeep({ recursive: true })); + assert.strictEqual(fs.existsSync(gitDirectory), false); +} + +// Test the Promises based version. +(async () => { + const dir = nextDirPath(); + makeNonEmptyDirectory(4, 10, 2, dir, true); + + // Removal should fail without the recursive option set to true. + await assert.rejects(fs.promises.rm(dir), { syscall: 'rm' }); + await assert.rejects(fs.promises.rm(dir, common.mustNotMutateObjectDeep({ recursive: false })), { + syscall: 'rm' + }); + + // Recursive removal should succeed. + await fs.promises.rm(dir, common.mustNotMutateObjectDeep({ recursive: true })); + + // Attempted removal should fail now because the directory is gone. + await assert.rejects(fs.promises.rm(dir), { syscall: 'stat' }); + + // Should fail if target does not exist + await assert.rejects(fs.promises.rm( + path.join(tmpdir.path, 'noexist.txt'), + { recursive: true } + ), { + code: 'ENOENT', + name: 'Error', + message: /^ENOENT: no such file or directory, stat/ + }); + + // Should not fail if target does not exist and force option is true + await fs.promises.rm(path.join(tmpdir.path, 'noexist.txt'), common.mustNotMutateObjectDeep({ force: true })); + + // Should delete file + const filePath = path.join(tmpdir.path, 'rm-promises-file.txt'); + fs.writeFileSync(filePath, ''); + + try { + await fs.promises.rm(filePath, common.mustNotMutateObjectDeep({ recursive: true })); + } finally { + fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true })); + } + + // Should accept URL + const fileURL = pathToFileURL(path.join(tmpdir.path, 'rm-promises-file.txt')); + fs.writeFileSync(fileURL, ''); + + try { + await fs.promises.rm(fileURL, common.mustNotMutateObjectDeep({ recursive: true })); + } finally { + fs.rmSync(fileURL, common.mustNotMutateObjectDeep({ force: true })); + } +})().then(common.mustCall()); + +// Removing a .git directory should not throw an EPERM. +// Refs: https://github.com/isaacs/rimraf/issues/21. +if (isGitPresent) { + (async () => { + const gitDirectory = nextDirPath(); + gitInit(gitDirectory); + await fs.promises.rm(gitDirectory, common.mustNotMutateObjectDeep({ recursive: true })); + assert.strictEqual(fs.existsSync(gitDirectory), false); + })().then(common.mustCall()); +} + +// Test input validation. +{ + const dir = nextDirPath(); + makeNonEmptyDirectory(4, 10, 2, dir, true); + const filePath = (path.join(tmpdir.path, 'rm-args-file.txt')); + fs.writeFileSync(filePath, ''); + + const defaults = { + retryDelay: 100, + maxRetries: 0, + recursive: false, + force: false + }; + const modified = { + retryDelay: 953, + maxRetries: 5, + recursive: true, + force: false + }; + + assert.deepStrictEqual(validateRmOptionsSync(filePath), defaults); + assert.deepStrictEqual(validateRmOptionsSync(filePath, {}), defaults); + assert.deepStrictEqual(validateRmOptionsSync(filePath, modified), modified); + assert.deepStrictEqual(validateRmOptionsSync(filePath, { + maxRetries: 99 + }), { + retryDelay: 100, + maxRetries: 99, + recursive: false, + force: false + }); + + [null, 'foo', 5, NaN].forEach((bad) => { + assert.throws(() => { + validateRmOptionsSync(filePath, bad); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "options" argument must be of type object\./ + }); + }); + + [undefined, null, 'foo', Infinity, function() {}].forEach((bad) => { + assert.throws(() => { + validateRmOptionsSync(filePath, { recursive: bad }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "options\.recursive" property must be of type boolean\./ + }); + }); + + [undefined, null, 'foo', Infinity, function() {}].forEach((bad) => { + assert.throws(() => { + validateRmOptionsSync(filePath, { force: bad }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "options\.force" property must be of type boolean\./ + }); + }); + + assert.throws(() => { + validateRmOptionsSync(filePath, { retryDelay: -1 }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: /^The value of "options\.retryDelay" is out of range\./ + }); + + assert.throws(() => { + validateRmOptionsSync(filePath, { maxRetries: -1 }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: /^The value of "options\.maxRetries" is out of range\./ + }); +} + +{ + // IBMi has a different access permission mechanism + // This test should not be run as `root` + if (!common.isIBMi && (common.isWindows || process.getuid() !== 0)) { + function makeDirectoryReadOnly(dir, mode) { + let accessErrorCode = 'EACCES'; + if (common.isWindows) { + accessErrorCode = 'EPERM'; + execSync(`icacls ${dir} /deny "everyone:(OI)(CI)(DE,DC)"`); + } else { + fs.chmodSync(dir, mode); + } + return accessErrorCode; + } + + function makeDirectoryWritable(dir) { + if (fs.existsSync(dir)) { + if (common.isWindows) { + execSync(`icacls ${dir} /remove:d "everyone"`); + } else { + fs.chmodSync(dir, 0o777); + } + } + } + + { + // Check that deleting a file that cannot be accessed using rmsync throws + // https://github.com/nodejs/node/issues/38683 + const dirname = nextDirPath(); + const filePath = path.join(dirname, 'text.txt'); + try { + fs.mkdirSync(dirname, common.mustNotMutateObjectDeep({ recursive: true })); + fs.writeFileSync(filePath, 'hello'); + const code = makeDirectoryReadOnly(dirname, 0o444); + assert.throws(() => { + fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true })); + }, { + code, + name: 'Error', + }); + } finally { + makeDirectoryWritable(dirname); + } + } + + { + // Check endless recursion. + // https://github.com/nodejs/node/issues/34580 + const dirname = nextDirPath(); + fs.mkdirSync(dirname, common.mustNotMutateObjectDeep({ recursive: true })); + const root = fs.mkdtempSync(path.join(dirname, 'fs-')); + const middle = path.join(root, 'middle'); + fs.mkdirSync(middle); + fs.mkdirSync(path.join(middle, 'leaf')); // Make `middle` non-empty + try { + const code = makeDirectoryReadOnly(middle, 0o555); + try { + assert.throws(() => { + fs.rmSync(root, common.mustNotMutateObjectDeep({ recursive: true })); + }, { + code, + name: 'Error', + }); + } catch (err) { + // Only fail the test if the folder was not deleted. + // as in some cases rmSync succesfully deletes read-only folders. + if (fs.existsSync(root)) { + throw err; + } + } + } finally { + makeDirectoryWritable(middle); + } + } + } +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-not-found.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-not-found.js new file mode 100644 index 00000000000000..13fe487568abc7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-not-found.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +tmpdir.refresh(); + +{ + // Should warn when trying to delete a nonexistent path + common.expectWarning( + 'DeprecationWarning', + 'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' + + 'will be removed. Use fs.rm(path, { recursive: true }) instead', + 'DEP0147' + ); + assert.throws( + () => fs.rmdirSync(path.join(tmpdir.path, 'noexist.txt'), + { recursive: true }), + { code: 'ENOENT' } + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-on-file.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-on-file.js new file mode 100644 index 00000000000000..d0b053a2f58bd6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-sync-warns-on-file.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +tmpdir.refresh(); + +{ + common.expectWarning( + 'DeprecationWarning', + 'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' + + 'will be removed. Use fs.rm(path, { recursive: true }) instead', + 'DEP0147' + ); + const filePath = path.join(tmpdir.path, 'rmdir-recursive.txt'); + fs.writeFileSync(filePath, ''); + assert.throws( + () => fs.rmdirSync(filePath, { recursive: true }), + { code: common.isWindows ? 'ENOENT' : 'ENOTDIR' } + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-not-found.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-not-found.js new file mode 100644 index 00000000000000..e94ed3c8f94d06 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-not-found.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +tmpdir.refresh(); + +{ + assert.throws( + () => + fs.rmdirSync(path.join(tmpdir.path, 'noexist.txt'), { recursive: true }), + { + code: 'ENOENT', + } + ); +} +{ + fs.rmdir( + path.join(tmpdir.path, 'noexist.txt'), + { recursive: true }, + common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOENT'); + }) + ); +} +{ + assert.rejects( + () => fs.promises.rmdir(path.join(tmpdir.path, 'noexist.txt'), + { recursive: true }), + { + code: 'ENOENT', + } + ).then(common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-on-file.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-on-file.js new file mode 100644 index 00000000000000..da007796eac09f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-throws-on-file.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +tmpdir.refresh(); + +const code = common.isWindows ? 'ENOENT' : 'ENOTDIR'; + +{ + const filePath = path.join(tmpdir.path, 'rmdir-recursive.txt'); + fs.writeFileSync(filePath, ''); + assert.throws(() => fs.rmdirSync(filePath, { recursive: true }), { code }); +} +{ + const filePath = path.join(tmpdir.path, 'rmdir-recursive.txt'); + fs.writeFileSync(filePath, ''); + fs.rmdir(filePath, { recursive: true }, common.mustCall((err) => { + assert.strictEqual(err.code, code); + })); +} +{ + const filePath = path.join(tmpdir.path, 'rmdir-recursive.txt'); + fs.writeFileSync(filePath, ''); + assert.rejects(() => fs.promises.rmdir(filePath, { recursive: true }), + { code }).then(common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-not-found.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-not-found.js new file mode 100644 index 00000000000000..55865a98919265 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-not-found.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); +const path = require('path'); + +tmpdir.refresh(); + +{ + // Should warn when trying to delete a nonexistent path + common.expectWarning( + 'DeprecationWarning', + 'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' + + 'will be removed. Use fs.rm(path, { recursive: true }) instead', + 'DEP0147' + ); + fs.rmdir( + path.join(tmpdir.path, 'noexist.txt'), + { recursive: true }, + common.mustCall() + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-on-file.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-on-file.js new file mode 100644 index 00000000000000..db0c8442c9a410 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive-warns-on-file.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +tmpdir.refresh(); + +{ + common.expectWarning( + 'DeprecationWarning', + 'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' + + 'will be removed. Use fs.rm(path, { recursive: true }) instead', + 'DEP0147' + ); + const filePath = path.join(tmpdir.path, 'rmdir-recursive.txt'); + fs.writeFileSync(filePath, ''); + fs.rmdir(filePath, { recursive: true }, common.mustCall((err) => { + assert.strictEqual(err.code, common.isWindows ? 'ENOENT' : 'ENOTDIR'); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive.js new file mode 100644 index 00000000000000..31bde44874163b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-recursive.js @@ -0,0 +1,252 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const { validateRmdirOptions } = require('internal/fs/utils'); + +common.expectWarning( + 'DeprecationWarning', + 'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' + + 'will be removed. Use fs.rm(path, { recursive: true }) instead', + 'DEP0147' +); + +tmpdir.refresh(); + +let count = 0; +const nextDirPath = (name = 'rmdir-recursive') => + path.join(tmpdir.path, `${name}-${count++}`); + +function makeNonEmptyDirectory(depth, files, folders, dirname, createSymLinks) { + fs.mkdirSync(dirname, { recursive: true }); + fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8'); + + const options = { flag: 'wx' }; + + for (let f = files; f > 0; f--) { + fs.writeFileSync(path.join(dirname, `f-${depth}-${f}`), '', options); + } + + if (createSymLinks) { + // Valid symlink + fs.symlinkSync( + `f-${depth}-1`, + path.join(dirname, `link-${depth}-good`), + 'file' + ); + + // Invalid symlink + fs.symlinkSync( + 'does-not-exist', + path.join(dirname, `link-${depth}-bad`), + 'file' + ); + } + + // File with a name that looks like a glob + fs.writeFileSync(path.join(dirname, '[a-z0-9].txt'), '', options); + + depth--; + if (depth <= 0) { + return; + } + + for (let f = folders; f > 0; f--) { + fs.mkdirSync( + path.join(dirname, `folder-${depth}-${f}`), + { recursive: true } + ); + makeNonEmptyDirectory( + depth, + files, + folders, + path.join(dirname, `d-${depth}-${f}`), + createSymLinks + ); + } +} + +function removeAsync(dir) { + // Removal should fail without the recursive option. + fs.rmdir(dir, common.mustCall((err) => { + assert.strictEqual(err.syscall, 'rmdir'); + + // Removal should fail without the recursive option set to true. + fs.rmdir(dir, { recursive: false }, common.mustCall((err) => { + assert.strictEqual(err.syscall, 'rmdir'); + + // Recursive removal should succeed. + fs.rmdir(dir, { recursive: true }, common.mustSucceed(() => { + // An error should occur if recursive and the directory does not exist. + fs.rmdir(dir, { recursive: true }, common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOENT'); + // Attempted removal should fail now because the directory is gone. + fs.rmdir(dir, common.mustCall((err) => { + assert.strictEqual(err.syscall, 'rmdir'); + })); + })); + })); + })); + })); +} + +// Test the asynchronous version +{ + // Create a 4-level folder hierarchy including symlinks + let dir = nextDirPath(); + makeNonEmptyDirectory(4, 10, 2, dir, true); + removeAsync(dir); + + // Create a 2-level folder hierarchy without symlinks + dir = nextDirPath(); + makeNonEmptyDirectory(2, 10, 2, dir, false); + removeAsync(dir); + + // Create a flat folder including symlinks + dir = nextDirPath(); + makeNonEmptyDirectory(1, 10, 2, dir, true); + removeAsync(dir); +} + +// Test the synchronous version. +{ + const dir = nextDirPath(); + makeNonEmptyDirectory(4, 10, 2, dir, true); + + // Removal should fail without the recursive option set to true. + assert.throws(() => { + fs.rmdirSync(dir); + }, { syscall: 'rmdir' }); + assert.throws(() => { + fs.rmdirSync(dir, { recursive: false }); + }, { syscall: 'rmdir' }); + + // Recursive removal should succeed. + fs.rmdirSync(dir, { recursive: true }); + + // An error should occur if recursive and the directory does not exist. + assert.throws(() => fs.rmdirSync(dir, { recursive: true }), + { code: 'ENOENT' }); + + // Attempted removal should fail now because the directory is gone. + assert.throws(() => fs.rmdirSync(dir), { syscall: 'rmdir' }); +} + +// Test the Promises based version. +(async () => { + const dir = nextDirPath(); + makeNonEmptyDirectory(4, 10, 2, dir, true); + + // Removal should fail without the recursive option set to true. + assert.rejects(fs.promises.rmdir(dir), { syscall: 'rmdir' }); + assert.rejects(fs.promises.rmdir(dir, { recursive: false }), { + syscall: 'rmdir' + }); + + // Recursive removal should succeed. + await fs.promises.rmdir(dir, { recursive: true }); + + // An error should occur if recursive and the directory does not exist. + await assert.rejects(fs.promises.rmdir(dir, { recursive: true }), + { code: 'ENOENT' }); + + // Attempted removal should fail now because the directory is gone. + assert.rejects(fs.promises.rmdir(dir), { syscall: 'rmdir' }); +})().then(common.mustCall()); + +// Test input validation. +{ + const defaults = { + retryDelay: 100, + maxRetries: 0, + recursive: false + }; + const modified = { + retryDelay: 953, + maxRetries: 5, + recursive: true + }; + + assert.deepStrictEqual(validateRmdirOptions(), defaults); + assert.deepStrictEqual(validateRmdirOptions({}), defaults); + assert.deepStrictEqual(validateRmdirOptions(modified), modified); + assert.deepStrictEqual(validateRmdirOptions({ + maxRetries: 99 + }), { + retryDelay: 100, + maxRetries: 99, + recursive: false + }); + + [null, 'foo', 5, NaN].forEach((bad) => { + assert.throws(() => { + validateRmdirOptions(bad); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "options" argument must be of type object\./ + }); + }); + + [undefined, null, 'foo', Infinity, function() {}].forEach((bad) => { + assert.throws(() => { + validateRmdirOptions({ recursive: bad }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "options\.recursive" property must be of type boolean\./ + }); + }); + + assert.throws(() => { + validateRmdirOptions({ retryDelay: -1 }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: /^The value of "options\.retryDelay" is out of range\./ + }); + + assert.throws(() => { + validateRmdirOptions({ maxRetries: -1 }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: /^The value of "options\.maxRetries" is out of range\./ + }); +} + +// FIXME(f3n67u): make this test pass +// It should not pass recursive option to rmdirSync, when called from +// rimraf (see: #35566) +// { +// // Make a non-empty directory: +// const original = fs.rmdirSync; +// const dir = `${nextDirPath()}/foo/bar`; +// fs.mkdirSync(dir, { recursive: true }); +// fs.writeFileSync(`${dir}/foo.txt`, 'hello world', 'utf8'); + +// // When called the second time from rimraf, the recursive option should +// // not be set for rmdirSync: +// let callCount = 0; +// let rmdirSyncOptionsFromRimraf; +// fs.rmdirSync = (path, options) => { +// if (callCount > 0) { +// rmdirSyncOptionsFromRimraf = { ...options }; +// } +// callCount++; +// return original(path, options); +// }; +// fs.rmdirSync(dir, { recursive: true }); +// fs.rmdirSync = original; +// assert.strictEqual(rmdirSyncOptionsFromRimraf.recursive, undefined); +// } diff --git a/cli/tests/node_compat/test/parallel/test-fs-rmdir-type-check.js b/cli/tests/node_compat/test/parallel/test-fs-rmdir-type-check.js new file mode 100644 index 00000000000000..8cddd3777d554f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-rmdir-type-check.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.rmdir(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.rmdirSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-watch.js b/cli/tests/node_compat/test/parallel/test-fs-watch.js new file mode 100644 index 00000000000000..785d42911267eb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-watch.js @@ -0,0 +1,105 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// Tests if `filename` is provided to watcher on supported platforms + +const fs = require('fs'); +const assert = require('assert'); +const { join } = require('path'); + +class WatchTestCase { + constructor(shouldInclude, dirName, fileName, field) { + this.dirName = dirName; + this.fileName = fileName; + this.field = field; + this.shouldSkip = !shouldInclude; + } + get dirPath() { return join(tmpdir.path, this.dirName); } + get filePath() { return join(this.dirPath, this.fileName); } +} + +const cases = [ + // Watch on a file should callback with a filename on supported systems + new WatchTestCase( + common.isLinux || common.isOSX || common.isWindows || common.isAIX, + 'watch1', + 'foo', + 'filePath' + ), + // Watch on a directory should callback with a filename on supported systems + new WatchTestCase( + common.isLinux || common.isOSX || common.isWindows, + 'watch2', + 'bar', + 'dirPath' + ), +]; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +for (const testCase of cases) { + if (testCase.shouldSkip) continue; + fs.mkdirSync(testCase.dirPath); + // Long content so it's actually flushed. + const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4); + fs.writeFileSync(testCase.filePath, content1); + + let interval; + const pathToWatch = testCase[testCase.field]; + const watcher = fs.watch(pathToWatch); + watcher.on('error', (err) => { + if (interval) { + clearInterval(interval); + interval = null; + } + assert.fail(err); + }); + watcher.on('close', common.mustCall(() => { + watcher.close(); // Closing a closed watcher should be a noop + })); + watcher.on('change', common.mustCall(function(eventType, argFilename) { + if (interval) { + clearInterval(interval); + interval = null; + } + if (common.isOSX) + assert.strictEqual(['rename', 'change'].includes(eventType), true); + else + assert.strictEqual(eventType, 'change'); + assert.strictEqual(argFilename, testCase.fileName); + + watcher.close(); + + // We document that watchers cannot be used anymore when it's closed, + // here we turn the methods into noops instead of throwing + watcher.close(); // Closing a closed watcher should be a noop + })); + + // Long content so it's actually flushed. toUpperCase so there's real change. + const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4); + interval = setInterval(() => { + fs.writeFileSync(testCase.filePath, ''); + fs.writeFileSync(testCase.filePath, content2); + }, 100); +} + +[false, 1, {}, [], null, undefined].forEach((input) => { + assert.throws( + () => fs.watch(input, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-watchfile.js b/cli/tests/node_compat/test/parallel/test-fs-watchfile.js new file mode 100644 index 00000000000000..907a7697a82a62 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-watchfile.js @@ -0,0 +1,112 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); + +// Basic usage tests. +assert.throws( + () => { + fs.watchFile('./some-file'); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws( + () => { + fs.watchFile('./another-file', {}, 'bad listener'); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws(() => { + fs.watchFile(new Object(), common.mustNotCall()); +}, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); + +const enoentFile = path.join(tmpdir.path, 'non-existent-file'); +const expectedStatObject = new fs.Stats( + 0, // dev + 0, // mode + 0, // nlink + 0, // uid + 0, // gid + 0, // rdev + 0, // blksize + 0, // ino + 0, // size + 0, // blocks + Date.UTC(1970, 0, 1, 0, 0, 0), // atime + Date.UTC(1970, 0, 1, 0, 0, 0), // mtime + Date.UTC(1970, 0, 1, 0, 0, 0), // ctime + Date.UTC(1970, 0, 1, 0, 0, 0) // birthtime +); + +tmpdir.refresh(); + +// If the file initially didn't exist, and gets created at a later point of +// time, the callback should be invoked again with proper values in stat object +let fileExists = false; + +const watcher = + fs.watchFile(enoentFile, { interval: 0 }, common.mustCall((curr, prev) => { + if (!fileExists) { + // If the file does not exist, all the fields should be zero and the date + // fields should be UNIX EPOCH time + assert.deepStrictEqual(curr, expectedStatObject); + assert.deepStrictEqual(prev, expectedStatObject); + // Create the file now, so that the callback will be called back once the + // event loop notices it. + fs.closeSync(fs.openSync(enoentFile, 'w')); + fileExists = true; + } else { + // If the ino (inode) value is greater than zero, it means that the file + // is present in the filesystem and it has a valid inode number. + assert(curr.ino > 0); + // As the file just got created, previous ino value should be lesser than + // or equal to zero (non-existent file). + assert(prev.ino <= 0); + // Stop watching the file + fs.unwatchFile(enoentFile); + watcher.stop(); // Stopping a stopped watcher should be a noop + } + }, 2)); + +// 'stop' should only be emitted once - stopping a stopped watcher should +// not trigger a 'stop' event. +watcher.on('stop', common.mustCall(function onStop() {})); + +// Watch events should callback with a filename on supported systems. +// Omitting AIX. It works but not reliably. +if (common.isLinux || common.isOSX || common.isWindows) { + const dir = path.join(tmpdir.path, 'watch'); + + fs.mkdir(dir, common.mustCall(function(err) { + if (err) assert.fail(err); + + fs.watch(dir, common.mustCall(function(eventType, filename) { + clearInterval(interval); + this._handle.close(); + assert.strictEqual(filename, 'foo.txt'); + })); + + const interval = setInterval(() => { + fs.writeFile(path.join(dir, 'foo.txt'), 'foo', common.mustCall((err) => { + if (err) assert.fail(err); + })); + }, 1); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-buffer.js b/cli/tests/node_compat/test/parallel/test-fs-write-buffer.js new file mode 100644 index 00000000000000..6627ac2793172c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-buffer.js @@ -0,0 +1,172 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const expected = Buffer.from('hello'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// fs.write with all parameters provided: +{ + const filename = path.join(tmpdir.path, 'write1.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + fs.write(fd, expected, 0, expected.length, null, cb); + })); +} + +// fs.write with a buffer, without the length parameter: +{ + const filename = path.join(tmpdir.path, 'write2.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, 2); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, 'lo'); + }); + + fs.write(fd, Buffer.from('hello'), 3, cb); + })); +} + +// fs.write with a buffer, without the offset and length parameters: +{ + const filename = path.join(tmpdir.path, 'write3.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.deepStrictEqual(expected.toString(), found); + }); + + fs.write(fd, expected, cb); + })); +} + +// fs.write with the offset passed as undefined followed by the callback: +{ + const filename = path.join(tmpdir.path, 'write4.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.deepStrictEqual(expected.toString(), found); + }); + + fs.write(fd, expected, undefined, cb); + })); +} + +// fs.write with offset and length passed as undefined followed by the callback: +{ + const filename = path.join(tmpdir.path, 'write5.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + fs.write(fd, expected, undefined, undefined, cb); + })); +} + +// fs.write with a Uint8Array, without the offset and length parameters: +{ + const filename = path.join(tmpdir.path, 'write6.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + fs.write(fd, Uint8Array.from(expected), cb); + })); +} + +// fs.write with invalid offset type +{ + const filename = path.join(tmpdir.path, 'write7.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + assert.throws(() => { + fs.write(fd, + Buffer.from('abcd'), + NaN, + expected.length, + 0, + common.mustNotCall()); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + 'It must be an integer. Received NaN' + }); + + fs.closeSync(fd); + })); +} + +// fs.write with a DataView, without the offset and length parameters: +{ + const filename = path.join(tmpdir.path, 'write8.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + const uint8 = Uint8Array.from(expected); + fs.write(fd, new DataView(uint8.buffer), cb); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-file-buffer.js b/cli/tests/node_compat/test/parallel/test-fs-write-file-buffer.js new file mode 100644 index 00000000000000..66a8e3eefb5b01 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-file-buffer.js @@ -0,0 +1,62 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const join = require('path').join; +const util = require('util'); +const fs = require('fs'); + +let data = [ + '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcH', + 'Bw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/', + '2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e', + 'Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAQABADASIAAhEBAxEB/8QA', + 'HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF', + 'BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK', + 'FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1', + 'dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG', + 'x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB', + 'AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC', + 'AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom', + 'JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE', + 'hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU', + '1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDhfBUFl/wk', + 'OmPqKJJZw3aiZFBw4z93jnkkc9u9dj8XLfSI/EBt7DTo7ea2Ox5YXVo5FC7g', + 'Tjq24nJPXNVtO0KATRvNHCIg3zoWJWQHqp+o4pun+EtJ0zxBq8mnLJa2d1L5', + '0NvnKRjJBUE5PAx3NYxxUY0pRtvYHSc5Ka2X9d7H/9k=']; + +data = data.join('\n'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const buf = Buffer.from(data, 'base64'); +fs.writeFileSync(join(tmpdir.path, 'test.jpg'), buf); + +util.log('Done!'); diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-file-invalid-path.js b/cli/tests/node_compat/test/parallel/test-fs-write-file-invalid-path.js new file mode 100644 index 00000000000000..631dd5ef604b8b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-file-invalid-path.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +if (!common.isWindows) + common.skip('This test is for Windows only.'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const DATA_VALUE = 'hello'; + +// Refs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx +// Ignore '/', '\\' and ':' +const RESERVED_CHARACTERS = '<>"|?*'; + +[...RESERVED_CHARACTERS].forEach((ch) => { + const pathname = path.join(tmpdir.path, `somefile_${ch}`); + assert.throws( + () => { + fs.writeFileSync(pathname, DATA_VALUE); + }, + /^Error: ENOENT: no such file or directory, open '.*'$/, + `failed with '${ch}'`); +}); + +// Test for ':' (NTFS data streams). +// Refs: https://msdn.microsoft.com/en-us/library/windows/desktop/bb540537.aspx +const pathname = path.join(tmpdir.path, 'foo:bar'); +fs.writeFileSync(pathname, DATA_VALUE); + +let content = ''; +const fileDataStream = fs.createReadStream(pathname, { + encoding: 'utf8' +}); + +fileDataStream.on('data', (data) => { + content += data; +}); + +fileDataStream.on('end', common.mustCall(() => { + assert.strictEqual(content, DATA_VALUE); +})); diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-file-sync.js b/cli/tests/node_compat/test/parallel/test-fs-write-file-sync.js new file mode 100644 index 00000000000000..b84c4d2046a5a3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-file-sync.js @@ -0,0 +1,128 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.isMainThread) + common.skip('Setting process.umask is not supported in Workers'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +// On Windows chmod is only able to manipulate read-only bit. Test if creating +// the file in read-only mode works. +const mode = common.isWindows ? 0o444 : 0o755; + +// Reset the umask for testing +process.umask(0o000); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test writeFileSync +{ + const file = path.join(tmpdir.path, 'testWriteFileSync.txt'); + + fs.writeFileSync(file, '123', { mode }); + const content = fs.readFileSync(file, { encoding: 'utf8' }); + assert.strictEqual(content, '123'); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); +} + +// Test appendFileSync +{ + const file = path.join(tmpdir.path, 'testAppendFileSync.txt'); + + fs.appendFileSync(file, 'abc', { mode }); + const content = fs.readFileSync(file, { encoding: 'utf8' }); + assert.strictEqual(content, 'abc'); + assert.strictEqual(fs.statSync(file).mode & mode, mode); +} + +// Test writeFileSync with file descriptor +{ + // Need to hijack fs.open/close to make sure that things + // get closed once they're opened. + const _openSync = fs.openSync; + const _closeSync = fs.closeSync; + let openCount = 0; + + fs.openSync = (...args) => { + openCount++; + return _openSync(...args); + }; + + fs.closeSync = (...args) => { + openCount--; + return _closeSync(...args); + }; + + const file = path.join(tmpdir.path, 'testWriteFileSyncFd.txt'); + const fd = fs.openSync(file, 'w+', mode); + + fs.writeFileSync(fd, '123'); + fs.closeSync(fd); + const content = fs.readFileSync(file, { encoding: 'utf8' }); + assert.strictEqual(content, '123'); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + + // Verify that all opened files were closed. + assert.strictEqual(openCount, 0); + fs.openSync = _openSync; + fs.closeSync = _closeSync; +} + +// Test writeFileSync with flags +{ + const file = path.join(tmpdir.path, 'testWriteFileSyncFlags.txt'); + + fs.writeFileSync(file, 'hello ', { encoding: 'utf8', flag: 'a' }); + fs.writeFileSync(file, 'world!', { encoding: 'utf8', flag: 'a' }); + const content = fs.readFileSync(file, { encoding: 'utf8' }); + assert.strictEqual(content, 'hello world!'); +} + +// Test writeFileSync with an object with an own toString function +{ + // Runtime deprecated by DEP0162 + common.expectWarning('DeprecationWarning', + 'Implicit coercion of objects with own toString property is deprecated.', + 'DEP0162'); + const file = path.join(tmpdir.path, 'testWriteFileSyncStringify.txt'); + const data = { + toString() { + return 'hello world!'; + } + }; + + fs.writeFileSync(file, data, { encoding: 'utf8', flag: 'a' }); + const content = fs.readFileSync(file, { encoding: 'utf8' }); + assert.strictEqual(content, String(data)); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-file.js b/cli/tests/node_compat/test/parallel/test-fs-write-file.js new file mode 100644 index 00000000000000..a5c93cd232151b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-file.js @@ -0,0 +1,115 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const join = require('path').join; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filename = join(tmpdir.path, 'test.txt'); + +const s = '南越国是前203年至前111年存在于岭南地区的一个国家,国都位于番禺,疆域包括今天中国的广东、' + + '广西两省区的大部份地区,福建省、湖南、贵州、云南的一小部份地区和越南的北部。' + + '南越国是秦朝灭亡后,由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。' + + '前196年和前179年,南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' + + '南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。南越国共存在93年,' + + '历经五代君主。南越国是岭南地区的第一个有记载的政权国家,采用封建制和郡县制并存的制度,' + + '它的建立保证了秦末乱世岭南地区社会秩序的稳定,有效的改善了岭南地区落后的政治、##济现状。\n'; + +fs.writeFile(filename, s, common.mustSucceed(() => { + fs.readFile(filename, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s), buffer.length); + })); +})); + +// Test that writeFile accepts buffers. +const filename2 = join(tmpdir.path, 'test2.txt'); +const buf = Buffer.from(s, 'utf8'); + +fs.writeFile(filename2, buf, common.mustSucceed(() => { + fs.readFile(filename2, common.mustSucceed((buffer) => { + assert.strictEqual(buf.length, buffer.length); + })); +})); + +// Test that writeFile accepts file descriptors. +const filename4 = join(tmpdir.path, 'test4.txt'); + +fs.open(filename4, 'w+', common.mustSucceed((fd) => { + fs.writeFile(fd, s, common.mustSucceed(() => { + fs.close(fd, common.mustSucceed(() => { + fs.readFile(filename4, common.mustSucceed((buffer) => { + assert.strictEqual(Buffer.byteLength(s), buffer.length); + })); + })); + })); +})); + + +{ + // Test that writeFile is cancellable with an AbortSignal. + // Before the operation has started + const controller = new AbortController(); + const signal = controller.signal; + const filename3 = join(tmpdir.path, 'test3.txt'); + + fs.writeFile(filename3, s, { signal }, common.mustCall((err) => { + assert.strictEqual(err.name, 'AbortError'); + })); + + controller.abort(); +} + +// FIXME(bartlomieju): +// { +// // Test that writeFile is cancellable with an AbortSignal. +// // After the operation has started +// const controller = new AbortController(); +// const signal = controller.signal; +// const filename4 = join(tmpdir.path, 'test5.txt'); + +// fs.writeFile(filename4, s, { signal }, common.mustCall((err) => { +// assert.strictEqual(err.name, 'AbortError'); +// })); + +// process.nextTick(() => controller.abort()); +// } + +{ + // Test read-only mode + const filename = join(tmpdir.path, 'test6.txt'); + fs.writeFileSync(filename, ''); + + // TODO: Correct the error type + const expectedError = common.isWindows ? /EPERM/ : /EBADF/; + fs.writeFile(filename, s, { flag: 'r' }, common.expectsError(expectedError)); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-no-fd.js b/cli/tests/node_compat/test/parallel/test-fs-write-no-fd.js new file mode 100644 index 00000000000000..7a6300a3fb6297 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-no-fd.js @@ -0,0 +1,19 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +assert.throws(function() { + fs.write(null, Buffer.allocUnsafe(1), 0, 1, common.mustNotCall()); +}, /TypeError/); + +assert.throws(function() { + fs.write(null, '1', 0, 1, common.mustNotCall()); +}, /TypeError/); diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-stream-autoclose-option.js b/cli/tests/node_compat/test/parallel/test-fs-write-stream-autoclose-option.js new file mode 100644 index 00000000000000..ebb92f031369c0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-stream-autoclose-option.js @@ -0,0 +1,66 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +const file = path.join(tmpdir.path, 'write-autoclose-opt1.txt'); +tmpdir.refresh(); +let stream = fs.createWriteStream(file, { flags: 'w+', autoClose: false }); +stream.write('Test1'); +stream.end(); +stream.on('finish', common.mustCall(function() { + stream.on('close', common.mustNotCall()); + process.nextTick(common.mustCall(function() { + assert.strictEqual(stream.closed, false); + assert.notStrictEqual(stream.fd, null); + next(); + })); +})); + +function next() { + // This will tell us if the fd is usable again or not + stream = fs.createWriteStream(null, { fd: stream.fd, start: 0 }); + stream.write('Test2'); + stream.end(); + stream.on('finish', common.mustCall(function() { + assert.strictEqual(stream.closed, false); + stream.on('close', common.mustCall(function() { + assert.strictEqual(stream.fd, null); + assert.strictEqual(stream.closed, true); + process.nextTick(next2); + })); + })); +} + +function next2() { + // This will test if after reusing the fd data is written properly + fs.readFile(file, function(err, data) { + assert.ifError(err); + assert.strictEqual(data.toString(), 'Test2'); + process.nextTick(common.mustCall(next3)); + }); +} + +function next3() { + // This is to test success scenario where autoClose is true + const stream = fs.createWriteStream(file, { autoClose: true }); + stream.write('Test3'); + stream.end(); + stream.on('finish', common.mustCall(function() { + assert.strictEqual(stream.closed, false); + stream.on('close', common.mustCall(function() { + assert.strictEqual(stream.fd, null); + assert.strictEqual(stream.closed, true); + })); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-stream-close-without-callback.js b/cli/tests/node_compat/test/parallel/test-fs-write-stream-close-without-callback.js new file mode 100644 index 00000000000000..d7c3511a1da78c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-stream-close-without-callback.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const s = fs.createWriteStream(path.join(tmpdir.path, 'nocallback')); + +s.end('hello world'); +s.close(); diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-stream-double-close.js b/cli/tests/node_compat/test/parallel/test-fs-write-stream-double-close.js new file mode 100644 index 00000000000000..be3c1cbc7177b4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-stream-double-close.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +{ + const s = fs.createWriteStream(path.join(tmpdir.path, 'rw')); + + s.close(common.mustCall()); + s.close(common.mustCall()); +} + +{ + const s = fs.createWriteStream(path.join(tmpdir.path, 'rw2')); + + let emits = 0; + s.on('close', () => { + emits++; + }); + + s.close(common.mustCall(() => { + assert.strictEqual(emits, 1); + s.close(common.mustCall(() => { + assert.strictEqual(emits, 1); + })); + process.nextTick(() => { + s.close(common.mustCall(() => { + assert.strictEqual(emits, 1); + })); + }); + })); +} + +{ + const s = fs.createWriteStream(path.join(tmpdir.path, 'rw'), { + autoClose: false + }); + + s.close(common.mustCall()); + s.close(common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-stream-end.js b/cli/tests/node_compat/test/parallel/test-fs-write-stream-end.js new file mode 100644 index 00000000000000..4940c7c0146092 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-stream-end.js @@ -0,0 +1,67 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +{ + const file = path.join(tmpdir.path, 'write-end-test0.txt'); + const stream = fs.createWriteStream(file); + stream.end(); + stream.on('close', common.mustCall()); +} + +{ + const file = path.join(tmpdir.path, 'write-end-test1.txt'); + const stream = fs.createWriteStream(file); + stream.end('a\n', 'utf8'); + stream.on('close', common.mustCall(function() { + const content = fs.readFileSync(file, 'utf8'); + assert.strictEqual(content, 'a\n'); + })); +} + +{ + const file = path.join(tmpdir.path, 'write-end-test2.txt'); + const stream = fs.createWriteStream(file); + stream.end(); + + let calledOpen = false; + stream.on('open', () => { + calledOpen = true; + }); + stream.on('finish', common.mustCall(() => { + assert.strictEqual(calledOpen, true); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-stream-fs.js b/cli/tests/node_compat/test/parallel/test-fs-write-stream-fs.js new file mode 100644 index 00000000000000..be3fbf754fef4e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-stream-fs.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +{ + const file = path.join(tmpdir.path, 'write-end-test0.txt'); + const stream = fs.createWriteStream(file, { + fs: { + open: common.mustCall(fs.open), + write: common.mustCallAtLeast(fs.write, 1), + close: common.mustCall(fs.close), + } + }); + stream.end('asd'); + stream.on('close', common.mustCall()); +} + + +{ + const file = path.join(tmpdir.path, 'write-end-test1.txt'); + const stream = fs.createWriteStream(file, { + fs: { + open: common.mustCall(fs.open), + write: fs.write, + writev: common.mustCallAtLeast(fs.writev, 1), + close: common.mustCall(fs.close), + } + }); + stream.write('asd'); + stream.write('asd'); + stream.write('asd'); + stream.end(); + stream.on('close', common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-stream-throw-type-error.js b/cli/tests/node_compat/test/parallel/test-fs-write-stream-throw-type-error.js new file mode 100644 index 00000000000000..3b53fbbefd8b23 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-stream-throw-type-error.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); + +const example = path.join(tmpdir.path, 'dummy'); + +tmpdir.refresh(); +// Should not throw. +fs.createWriteStream(example, undefined).end(); +fs.createWriteStream(example, null).end(); +fs.createWriteStream(example, 'utf8').end(); +fs.createWriteStream(example, { encoding: 'utf8' }).end(); + +const createWriteStreamErr = (path, opt) => { + assert.throws( + () => { + fs.createWriteStream(path, opt); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +}; + +createWriteStreamErr(example, 123); +createWriteStreamErr(example, 0); +createWriteStreamErr(example, true); +createWriteStreamErr(example, false); diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-stream.js b/cli/tests/node_compat/test/parallel/test-fs-write-stream.js new file mode 100644 index 00000000000000..b30aaf8acb9711 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-stream.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +const file = path.join(tmpdir.path, 'write.txt'); + +tmpdir.refresh(); + +{ + const stream = fs.WriteStream(file); + const _fs_close = fs.close; + + fs.close = function(fd) { + assert.ok(fd, 'fs.close must not be called without an undefined fd.'); + fs.close = _fs_close; + fs.closeSync(fd); + }; + stream.destroy(); +} + +{ + const stream = fs.createWriteStream(file); + + stream.on('drain', function() { + assert.fail('\'drain\' event must not be emitted before ' + + 'stream.write() has been called at least once.'); + }); + stream.destroy(); +} + +// Throws if data is not of type Buffer. +{ + const stream = fs.createWriteStream(file); + stream.on('error', common.mustNotCall()); + assert.throws(() => { + stream.write(42); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + stream.destroy(); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write-sync.js b/cli/tests/node_compat/test/parallel/test-fs-write-sync.js new file mode 100644 index 00000000000000..24c20b80d401b4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write-sync.js @@ -0,0 +1,63 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const filename = path.join(tmpdir.path, 'write.txt'); + +tmpdir.refresh(); + +{ + const parameters = [Buffer.from('bár'), 0, Buffer.byteLength('bár')]; + + // The first time fs.writeSync is called with all parameters provided. + // After that, each pop in the cycle removes the final parameter. So: + // - The 2nd time fs.writeSync with a buffer, without the length parameter. + // - The 3rd time fs.writeSync with a buffer, without the offset and length + // parameters. + while (parameters.length > 0) { + const fd = fs.openSync(filename, 'w'); + + let written = fs.writeSync(fd, ''); + assert.strictEqual(written, 0); + + fs.writeSync(fd, 'foo'); + + written = fs.writeSync(fd, ...parameters); + assert.ok(written > 3); + fs.closeSync(fd); + + assert.strictEqual(fs.readFileSync(filename, 'utf-8'), 'foobár'); + + parameters.pop(); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-write.js b/cli/tests/node_compat/test/parallel/test-fs-write.js new file mode 100644 index 00000000000000..33fcb84cf985c7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-write.js @@ -0,0 +1,212 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --expose_externalize_string +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const fn = path.join(tmpdir.path, 'write.txt'); +const fn2 = path.join(tmpdir.path, 'write2.txt'); +const fn3 = path.join(tmpdir.path, 'write3.txt'); +const fn4 = path.join(tmpdir.path, 'write4.txt'); +const fn5 = path.join(tmpdir.path, 'write5.txt'); +const expected = 'ümlaut.'; +const constants = fs.constants; + +const { externalizeString, isOneByteString } = global; + +// Account for extra globals exposed by --expose_externalize_string. +common.allowGlobals(externalizeString, isOneByteString, global.x); + +{ + const expected = 'ümlaut sechzig'; // Must be a unique string. + externalizeString(expected); + assert.strictEqual(isOneByteString(expected), true); + const fd = fs.openSync(fn, 'w'); + fs.writeSync(fd, expected, 0, 'latin1'); + fs.closeSync(fd); + assert.strictEqual(fs.readFileSync(fn, 'latin1'), expected); +} + +{ + const expected = 'ümlaut neunzig'; // Must be a unique string. + externalizeString(expected); + assert.strictEqual(isOneByteString(expected), true); + const fd = fs.openSync(fn, 'w'); + fs.writeSync(fd, expected, 0, 'utf8'); + fs.closeSync(fd); + assert.strictEqual(fs.readFileSync(fn, 'utf8'), expected); +} + +{ + const expected = 'Zhōngwén 1'; // Must be a unique string. + externalizeString(expected); + assert.strictEqual(isOneByteString(expected), false); + const fd = fs.openSync(fn, 'w'); + fs.writeSync(fd, expected, 0, 'ucs2'); + fs.closeSync(fd); + assert.strictEqual(fs.readFileSync(fn, 'ucs2'), expected); +} + +{ + const expected = 'Zhōngwén 2'; // Must be a unique string. + externalizeString(expected); + assert.strictEqual(isOneByteString(expected), false); + const fd = fs.openSync(fn, 'w'); + fs.writeSync(fd, expected, 0, 'utf8'); + fs.closeSync(fd); + assert.strictEqual(fs.readFileSync(fn, 'utf8'), expected); +} + +fs.open(fn, 'w', 0o644, common.mustSucceed((fd) => { + const done = common.mustSucceed((written) => { + assert.strictEqual(written, Buffer.byteLength(expected)); + fs.closeSync(fd); + const found = fs.readFileSync(fn, 'utf8'); + fs.unlinkSync(fn); + assert.strictEqual(found, expected); + }); + + const written = common.mustSucceed((written) => { + assert.strictEqual(written, 0); + fs.write(fd, expected, 0, 'utf8', done); + }); + + fs.write(fd, '', 0, 'utf8', written); +})); + +// TODO(kt3k): Enable this test when fs.open supports number for `flags` +// paramter. +/* +const args = constants.O_CREAT | constants.O_WRONLY | constants.O_TRUNC; +fs.open(fn2, args, 0o644, common.mustSucceed((fd) => { + const done = common.mustSucceed((written) => { + assert.strictEqual(written, Buffer.byteLength(expected)); + fs.closeSync(fd); + const found = fs.readFileSync(fn2, 'utf8'); + fs.unlinkSync(fn2); + assert.strictEqual(found, expected); + }); + + const written = common.mustSucceed((written) => { + assert.strictEqual(written, 0); + fs.write(fd, expected, 0, 'utf8', done); + }); + + fs.write(fd, '', 0, 'utf8', written); +})); +*/ + +fs.open(fn3, 'w', 0o644, common.mustSucceed((fd) => { + const done = common.mustSucceed((written) => { + assert.strictEqual(written, Buffer.byteLength(expected)); + fs.closeSync(fd); + }); + + fs.write(fd, expected, done); +})); + +fs.open(fn4, 'w', 0o644, common.mustSucceed((fd) => { + const done = common.mustSucceed((written) => { + assert.strictEqual(written, Buffer.byteLength(expected)); + fs.closeSync(fd); + }); + + const data = { + toString() { return expected; } + }; + fs.write(fd, data, done); +})); + +[false, 'test', {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.write(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.writeSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); + +[false, 5, {}, [], null, undefined].forEach((data) => { + assert.throws( + () => fs.write(1, data, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + message: /"buffer"/ + } + ); + assert.throws( + () => fs.writeSync(1, data), + { + code: 'ERR_INVALID_ARG_TYPE', + message: /"buffer"/ + } + ); +}); + +{ + // Regression test for https://github.com/nodejs/node/issues/38168 + const fd = fs.openSync(fn5, 'w'); + + assert.throws( + () => fs.writeSync(fd, 'abc', 0, 'hex'), + { + code: 'ERR_INVALID_ARG_VALUE', + message: /'encoding' is invalid for data of length 3/ + } + ); + + assert.throws( + () => fs.writeSync(fd, 'abc', 0, 'hex', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_VALUE', + message: /'encoding' is invalid for data of length 3/ + } + ); + + assert.strictEqual(fs.writeSync(fd, 'abcd', 0, 'hex'), 2); + + fs.write(fd, 'abcd', 0, 'hex', common.mustSucceed((written) => { + assert.strictEqual(written, 2); + fs.closeSync(fd); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-fs-writev-sync.js b/cli/tests/node_compat/test/parallel/test-fs-writev-sync.js new file mode 100644 index 00000000000000..40e7c4bb8e0861 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-writev-sync.js @@ -0,0 +1,104 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; + +const getFileName = (i) => path.join(tmpdir.path, `writev_sync_${i}.txt`); + +/** + * Testing with a array of buffers input + */ + +// fs.writevSync with array of buffers with all parameters +{ + const filename = getFileName(1); + const fd = fs.openSync(filename, 'w'); + + const buffer = Buffer.from(expected); + const bufferArr = [buffer, buffer]; + const expectedLength = bufferArr.length * buffer.byteLength; + + let written = fs.writevSync(fd, [Buffer.from('')], null); + assert.strictEqual(written, 0); + + written = fs.writevSync(fd, bufferArr, null); + assert.strictEqual(written, expectedLength); + + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); +} + +// fs.writevSync with array of buffers without position +{ + const filename = getFileName(2); + const fd = fs.openSync(filename, 'w'); + + const buffer = Buffer.from(expected); + const bufferArr = [buffer, buffer, buffer]; + const expectedLength = bufferArr.length * buffer.byteLength; + + let written = fs.writevSync(fd, [Buffer.from('')]); + assert.strictEqual(written, 0); + + written = fs.writevSync(fd, bufferArr); + assert.strictEqual(written, expectedLength); + + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); +} + +// fs.writevSync with empty array of buffers +{ + const filename = getFileName(3); + const fd = fs.openSync(filename, 'w'); + const written = fs.writevSync(fd, []); + assert.strictEqual(written, 0); + fs.closeSync(fd); + +} + +/** + * Testing with wrong input types + */ +{ + const filename = getFileName(4); + const fd = fs.openSync(filename, 'w'); + + [false, 'test', {}, [{}], ['sdf'], null, undefined].forEach((i) => { + assert.throws( + () => fs.writevSync(fd, i, null), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + }); + + fs.closeSync(fd); +} + +// fs.writevSync with wrong fd types +[false, 'test', {}, [{}], null, undefined].forEach((i) => { + assert.throws( + () => fs.writevSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-fs-writev.js b/cli/tests/node_compat/test/parallel/test-fs-writev.js new file mode 100644 index 00000000000000..a0f3622cc277e9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-fs-writev.js @@ -0,0 +1,114 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; + +const getFileName = (i) => path.join(tmpdir.path, `writev_${i}.txt`); + +/** + * Testing with a array of buffers input + */ + +// fs.writev with array of buffers with all parameters +{ + const filename = getFileName(1); + const fd = fs.openSync(filename, 'w'); + + const buffer = Buffer.from(expected); + const bufferArr = [buffer, buffer]; + + const done = common.mustSucceed((written, buffers) => { + assert.deepStrictEqual(bufferArr, buffers); + const expectedLength = bufferArr.length * buffer.byteLength; + assert.deepStrictEqual(written, expectedLength); + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); + }); + + fs.writev(fd, bufferArr, null, done); +} + +// fs.writev with array of buffers without position +{ + const filename = getFileName(2); + const fd = fs.openSync(filename, 'w'); + + const buffer = Buffer.from(expected); + const bufferArr = [buffer, buffer]; + + const done = common.mustSucceed((written, buffers) => { + assert.deepStrictEqual(bufferArr, buffers); + + const expectedLength = bufferArr.length * buffer.byteLength; + assert.deepStrictEqual(written, expectedLength); + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); + }); + + fs.writev(fd, bufferArr, done); +} + + +// fs.writev with empty array of buffers +{ + const filename = getFileName(3); + const fd = fs.openSync(filename, 'w'); + const bufferArr = []; + let afterSyncCall = false; + + const done = common.mustSucceed((written, buffers) => { + assert.strictEqual(buffers.length, 0); + assert.strictEqual(written, 0); + assert(afterSyncCall); + fs.closeSync(fd); + }); + + fs.writev(fd, bufferArr, done); + afterSyncCall = true; +} + +/** + * Testing with wrong input types + */ +{ + const filename = getFileName(4); + const fd = fs.openSync(filename, 'w'); + + [false, 'test', {}, [{}], ['sdf'], null, undefined].forEach((i) => { + assert.throws( + () => fs.writev(fd, i, null, common.mustNotCall()), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + }); + + fs.closeSync(fd); +} + +// fs.writev with wrong fd types +[false, 'test', {}, [{}], null, undefined].forEach((i) => { + assert.throws( + () => fs.writev(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-handle-wrap-close-abort.js b/cli/tests/node_compat/test/parallel/test-handle-wrap-close-abort.js new file mode 100644 index 00000000000000..c782de76a5322e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-handle-wrap-close-abort.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +process.on('uncaughtException', common.mustCall(2)); + +setTimeout(function() { + process.nextTick(function() { + const c = setInterval(function() { + clearInterval(c); + throw new Error('setInterval'); + }, 1); + }); + setTimeout(function() { + throw new Error('setTimeout'); + }, 1); +}, 1); diff --git a/cli/tests/node_compat/test/parallel/test-http-agent-getname.js b/cli/tests/node_compat/test/parallel/test-http-agent-getname.js new file mode 100644 index 00000000000000..f3067791e9b4d5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-agent-getname.js @@ -0,0 +1,63 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const http = require('http'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); + +const agent = new http.Agent(); + +// Default to localhost +assert.strictEqual( + agent.getName({ + port: 80, + localAddress: '192.168.1.1' + }), + 'localhost:80:192.168.1.1' +); + +// empty argument +assert.strictEqual( + agent.getName(), + 'localhost::' +); + +// empty options +assert.strictEqual( + agent.getName({}), + 'localhost::' +); + +// pass all arguments +assert.strictEqual( + agent.getName({ + host: '0.0.0.0', + port: 80, + localAddress: '192.168.1.1' + }), + '0.0.0.0:80:192.168.1.1' +); + +// unix socket +const socketPath = path.join(tmpdir.path, 'foo', 'bar'); +assert.strictEqual( + agent.getName({ + socketPath + }), + `localhost:::${socketPath}` +); + +for (const family of [0, null, undefined, 'bogus']) + assert.strictEqual(agent.getName({ family }), 'localhost::'); + +for (const family of [4, 6]) + assert.strictEqual(agent.getName({ family }), `localhost:::${family}`); diff --git a/cli/tests/node_compat/test/parallel/test-http-client-get-url.js b/cli/tests/node_compat/test/parallel/test-http-client-get-url.js new file mode 100644 index 00000000000000..f6b5a1aa38fbce --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-client-get-url.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); +const testPath = '/foo?bar'; + +const server = http.createServer(common.mustCall((req, res) => { + assert.strictEqual(req.method, 'GET'); + assert.strictEqual(req.url, testPath); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); +}, 3)); + +server.listen(0, common.localhostIPv4, common.mustCall(() => { + const u = `http://${common.localhostIPv4}:${server.address().port}${testPath}`; + http.get(u, common.mustCall(() => { + http.get(url.parse(u), common.mustCall(() => { + http.get(new URL(u), common.mustCall(() => { + server.close(); + })); + })); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-http-client-read-in-error.js b/cli/tests/node_compat/test/parallel/test-http-client-read-in-error.js new file mode 100644 index 00000000000000..ec127ece62ec97 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-client-read-in-error.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); +const http = require('http'); + +class Agent extends http.Agent { + createConnection() { + const socket = new net.Socket(); + + socket.on('error', function() { + socket.push('HTTP/1.1 200\r\n\r\n'); + }); + + let onNewListener; + socket.on('newListener', onNewListener = (name) => { + if (name !== 'error') + return; + socket.removeListener('newListener', onNewListener); + + // Let other listeners to be set up too + process.nextTick(() => { + this.breakSocket(socket); + }); + }); + + return socket; + } + + breakSocket(socket) { + socket.emit('error', new Error('Intentional error')); + } +} + +const agent = new Agent(); + +http.request({ + agent +}).once('error', function() { + console.log('ignore'); + this.on('data', common.mustNotCall()); +}); diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-buffer.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-buffer.js new file mode 100644 index 00000000000000..87e46c017e2879 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-buffer.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const { getDefaultHighWaterMark } = require('internal/streams/state'); + +const http = require('http'); +const OutgoingMessage = http.OutgoingMessage; + +const msg = new OutgoingMessage(); +msg._implicitHeader = function() {}; + +// Writes should be buffered until highwatermark +// even when no socket is assigned. + +assert.strictEqual(msg.write('asd'), true); +while (msg.write('asd')); +const highwatermark = msg.writableHighWaterMark || getDefaultHighWaterMark(); +assert(msg.outputSize >= highwatermark); diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-destroy.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-destroy.js new file mode 100644 index 00000000000000..52b41d9c395a5a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-destroy.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const http = require('http'); +const OutgoingMessage = http.OutgoingMessage; + +{ + const msg = new OutgoingMessage(); + assert.strictEqual(msg.destroyed, false); + msg.destroy(); + assert.strictEqual(msg.destroyed, true); + msg.write('asd', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + })); + msg.on('error', common.mustNotCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-finish-writable.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-finish-writable.js new file mode 100644 index 00000000000000..f87a1130c7ecba --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-finish-writable.js @@ -0,0 +1,47 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +// Verify that after calling end() on an `OutgoingMessage` (or a type that +// inherits from `OutgoingMessage`), its `writable` property is not set to false + +const server = http.createServer(common.mustCall(function(req, res) { + assert.strictEqual(res.writable, true); + assert.strictEqual(res.finished, false); + assert.strictEqual(res.writableEnded, false); + res.end(); + + // res.writable is set to false after it has finished sending + // Ref: https://github.com/nodejs/node/issues/15029 + assert.strictEqual(res.writable, true); + assert.strictEqual(res.finished, true); + assert.strictEqual(res.writableEnded, true); + + server.close(); +})); + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const clientRequest = http.request({ + port: server.address().port, + method: 'GET', + path: '/' + }); + + assert.strictEqual(clientRequest.writable, true); + clientRequest.end(); + + // Writable is still true when close + // THIS IS LEGACY, we cannot change it + // unless we break error detection + assert.strictEqual(clientRequest.writable, true); +})); diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-getter.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-getter.js new file mode 100644 index 00000000000000..c95a07cfe2cf3b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-getter.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const { OutgoingMessage } = require('http'); +const assert = require('assert'); + +const warn = 'OutgoingMessage.prototype._headerNames is deprecated'; +common.expectWarning('DeprecationWarning', warn, 'DEP0066'); + +{ + // Tests for _headerNames get method + const outgoingMessage = new OutgoingMessage(); + outgoingMessage._headerNames; // eslint-disable-line no-unused-expressions +} + +{ + // Tests _headerNames getter result after setting a header. + const outgoingMessage = new OutgoingMessage(); + outgoingMessage.setHeader('key', 'value'); + const expect = Object.create(null); + expect.key = 'key'; + assert.deepStrictEqual(outgoingMessage._headerNames, expect); +} diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-setter.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-setter.js new file mode 100644 index 00000000000000..cbafbcc569bea5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headernames-setter.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const { OutgoingMessage } = require('http'); + +const warn = 'OutgoingMessage.prototype._headerNames is deprecated'; +common.expectWarning('DeprecationWarning', warn, 'DEP0066'); + +{ + // Tests for _headerNames set method + const outgoingMessage = new OutgoingMessage(); + outgoingMessage._headerNames = { + 'x-flow-id': '61bba6c5-28a3-4eab-9241-2ecaa6b6a1fd' + }; +} diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headers.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headers.js new file mode 100644 index 00000000000000..2c11f1ce5f7588 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-internal-headers.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { kOutHeaders } = require('internal/http'); +const { OutgoingMessage } = require('http'); + +const warn = 'OutgoingMessage.prototype._headers is deprecated'; +common.expectWarning('DeprecationWarning', warn, 'DEP0066'); + +{ + // Tests for _headers get method + const outgoingMessage = new OutgoingMessage(); + outgoingMessage.getHeaders = common.mustCall(); + outgoingMessage._headers; // eslint-disable-line no-unused-expressions +} + +{ + // Tests for _headers set method + const outgoingMessage = new OutgoingMessage(); + outgoingMessage._headers = { + host: 'risingstack.com', + Origin: 'localhost' + }; + + assert.deepStrictEqual( + Object.entries(outgoingMessage[kOutHeaders]), + Object.entries({ + host: ['host', 'risingstack.com'], + origin: ['Origin', 'localhost'] + })); +} + +{ + // Tests for _headers set method `null` + const outgoingMessage = new OutgoingMessage(); + outgoingMessage._headers = null; + + assert.strictEqual( + outgoingMessage[kOutHeaders], + null + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-message-inheritance.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-message-inheritance.js new file mode 100644 index 00000000000000..84ed9b1574c763 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-message-inheritance.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { OutgoingMessage } = require('http'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// Check that OutgoingMessage can be used without a proper Socket +// Refs: https://github.com/nodejs/node/issues/14386 +// Refs: https://github.com/nodejs/node/issues/14381 + +class Response extends OutgoingMessage { + _implicitHeader() {} +} + +const res = new Response(); + +let firstChunk = true; + +const ws = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + if (firstChunk) { + assert(chunk.toString().endsWith('hello world')); + firstChunk = false; + } else { + assert.strictEqual(chunk.length, 0); + } + setImmediate(callback); + }, 2) +}); + +res.socket = ws; +ws._httpMessage = res; +res.connection = ws; + +res.end('hello world'); diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-renderHeaders.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-renderHeaders.js new file mode 100644 index 00000000000000..6fb8bfc9006da8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-renderHeaders.js @@ -0,0 +1,57 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Flags: --expose-internals + +require('../common'); +const assert = require('assert'); + +const kOutHeaders = require('internal/http').kOutHeaders; +const http = require('http'); +const OutgoingMessage = http.OutgoingMessage; + +{ + const outgoingMessage = new OutgoingMessage(); + outgoingMessage._header = {}; + assert.throws( + () => outgoingMessage._renderHeaders(), + { + code: 'ERR_HTTP_HEADERS_SENT', + name: 'Error', + message: 'Cannot render headers after they are sent to the client' + } + ); +} + +{ + const outgoingMessage = new OutgoingMessage(); + outgoingMessage[kOutHeaders] = null; + const result = outgoingMessage._renderHeaders(); + assert.deepStrictEqual(result, {}); +} + + +{ + const outgoingMessage = new OutgoingMessage(); + outgoingMessage[kOutHeaders] = {}; + const result = outgoingMessage._renderHeaders(); + assert.deepStrictEqual(result, {}); +} + +{ + const outgoingMessage = new OutgoingMessage(); + outgoingMessage[kOutHeaders] = { + host: ['host', 'nodejs.org'], + origin: ['Origin', 'localhost'] + }; + const result = outgoingMessage._renderHeaders(); + assert.deepStrictEqual(result, { + host: 'nodejs.org', + Origin: 'localhost' + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-http-outgoing-settimeout.js b/cli/tests/node_compat/test/parallel/test-http-outgoing-settimeout.js new file mode 100644 index 00000000000000..8e3db0bf9c658a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-outgoing-settimeout.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { OutgoingMessage } = require('http'); + +{ + // Tests for settimeout method with socket + const expectedMsecs = 42; + const outgoingMessage = new OutgoingMessage(); + outgoingMessage.socket = { + setTimeout: common.mustCall((msecs) => { + assert.strictEqual(msecs, expectedMsecs); + }) + }; + outgoingMessage.setTimeout(expectedMsecs); +} + +{ + // Tests for settimeout method without socket + const expectedMsecs = 23; + const outgoingMessage = new OutgoingMessage(); + outgoingMessage.setTimeout(expectedMsecs); + + outgoingMessage.emit('socket', { + setTimeout: common.mustCall((msecs) => { + assert.strictEqual(msecs, expectedMsecs); + }) + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-http-url.parse-auth.js b/cli/tests/node_compat/test/parallel/test-http-url.parse-auth.js new file mode 100644 index 00000000000000..baaea7c76d549a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-url.parse-auth.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // The correct authorization header is be passed + assert.strictEqual(request.headers.authorization, 'Basic dXNlcjpwYXNzOg=='); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const port = this.address().port; + // username = "user", password = "pass:" + const testURL = url.parse(`http://user:pass%3A@localhost:${port}`); + + // make the request + http.request(testURL).end(); +}); diff --git a/cli/tests/node_compat/test/parallel/test-http-url.parse-path.js b/cli/tests/node_compat/test/parallel/test-http-url.parse-path.js new file mode 100644 index 00000000000000..7587caa2a783cb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-url.parse-path.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // A path should come over + assert.strictEqual(request.url, '/asdf'); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const testURL = url.parse(`http://localhost:${this.address().port}/asdf`); + + // make the request + http.request(testURL).end(); +}); diff --git a/cli/tests/node_compat/test/parallel/test-http-url.parse-post.js b/cli/tests/node_compat/test/parallel/test-http-url.parse-post.js new file mode 100644 index 00000000000000..2e461cc331a5ec --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-url.parse-post.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +let testURL; + +function check(request) { + // url.parse should not mess with the method + assert.strictEqual(request.method, 'POST'); + // Everything else should be right + assert.strictEqual(request.url, '/asdf?qwer=zxcv'); + // The host header should use the url.parse.hostname + assert.strictEqual(request.headers.host, + `${testURL.hostname}:${testURL.port}`); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + testURL = url.parse(`http://localhost:${this.address().port}/asdf?qwer=zxcv`); + testURL.method = 'POST'; + + // make the request + http.request(testURL).end(); +}); diff --git a/cli/tests/node_compat/test/parallel/test-http-url.parse-search.js b/cli/tests/node_compat/test/parallel/test-http-url.parse-search.js new file mode 100644 index 00000000000000..60304c657bcfa8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-http-url.parse-search.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // A path should come over with params + assert.strictEqual(request.url, '/asdf?qwer=zxcv'); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const port = this.address().port; + const testURL = url.parse(`http://localhost:${port}/asdf?qwer=zxcv`); + + // make the request + http.request(testURL).end(); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-access-byteswritten.js b/cli/tests/node_compat/test/parallel/test-net-access-byteswritten.js new file mode 100644 index 00000000000000..1be6b29b7f49ed --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-access-byteswritten.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const net = require('net'); +const tls = require('tls'); +const tty = require('tty'); + +// Check that the bytesWritten getter doesn't crash if object isn't +// constructed. +assert.strictEqual(net.Socket.prototype.bytesWritten, undefined); +assert.strictEqual(Object.getPrototypeOf(tls.TLSSocket).prototype.bytesWritten, + undefined); +assert.strictEqual(tls.TLSSocket.prototype.bytesWritten, undefined); +assert.strictEqual(Object.getPrototypeOf(tty.ReadStream).prototype.bytesWritten, + undefined); +assert.strictEqual(tty.ReadStream.prototype.bytesWritten, undefined); +assert.strictEqual(tty.WriteStream.prototype.bytesWritten, undefined); diff --git a/cli/tests/node_compat/test/parallel/test-net-after-close.js b/cli/tests/node_compat/test/parallel/test-net-after-close.js new file mode 100644 index 00000000000000..9ffa68407ef302 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-after-close.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall((s) => { + console.error('SERVER: got connection'); + s.end(); +})); + +server.listen(0, common.mustCall(() => { + const c = net.createConnection(server.address().port); + c.on('close', common.mustCall(() => { + /* eslint-disable no-unused-expressions */ + console.error('connection closed'); + assert.strictEqual(c._handle, null); + // Calling functions / accessing properties of a closed socket should not + // throw. + c.setNoDelay(); + c.setKeepAlive(); + c.bufferSize; + c.pause(); + c.resume(); + c.address(); + c.remoteAddress; + c.remotePort; + server.close(); + /* eslint-enable no-unused-expressions */ + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-allow-half-open.js b/cli/tests/node_compat/test/parallel/test-net-allow-half-open.js new file mode 100644 index 00000000000000..cfc2ec5520e205 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-allow-half-open.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + const server = net.createServer(common.mustCall((socket) => { + socket.end(Buffer.alloc(1024)); + })).listen(0, common.mustCall(() => { + const socket = net.connect(server.address().port); + assert.strictEqual(socket.allowHalfOpen, false); + socket.resume(); + socket.on('end', common.mustCall(() => { + process.nextTick(() => { + // Ensure socket is not destroyed straight away + // without proper shutdown. + assert(!socket.destroyed); + server.close(); + }); + })); + socket.on('finish', common.mustCall(() => { + assert(!socket.destroyed); + })); + socket.on('close', common.mustCall()); + })); +} + +{ + const server = net.createServer(common.mustCall((socket) => { + socket.end(Buffer.alloc(1024)); + })).listen(0, common.mustCall(() => { + const socket = net.connect(server.address().port); + assert.strictEqual(socket.allowHalfOpen, false); + socket.resume(); + socket.on('end', common.mustCall(() => { + assert(!socket.destroyed); + })); + socket.end('asd'); + socket.on('finish', common.mustCall(() => { + assert(!socket.destroyed); + })); + socket.on('close', common.mustCall(() => { + server.close(); + })); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen-path.js b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen-path.js new file mode 100644 index 00000000000000..80bc6f7bcaec4e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen-path.js @@ -0,0 +1,17 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const fp = '/blah/fadfa'; +const server = net.createServer(common.mustNotCall()); +server.listen(fp, common.mustNotCall()); +server.on('error', common.mustCall(function(e) { + assert.strictEqual(e.address, fp); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen.js b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen.js new file mode 100644 index 00000000000000..14febbcdf4bcd0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-listen.js @@ -0,0 +1,19 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustNotCall()); +server.listen(1, '1.1.1.1', common.mustNotCall()); +server.on('error', common.mustCall(function(e) { + assert.strictEqual(e.address, '1.1.1.1'); + assert.strictEqual(e.port, 1); + assert.strictEqual(e.syscall, 'listen'); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-better-error-messages-path.js b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-path.js new file mode 100644 index 00000000000000..d1bada362dd7b2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-path.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + const fp = '/tmp/fadagagsdfgsdf'; + const c = net.connect(fp); + + c.on('connect', common.mustNotCall()); + c.on('error', common.expectsError({ + code: 'ENOENT', + message: `connect ENOENT ${fp}` + })); +} + +{ + assert.throws( + () => net.createConnection({ path: {} }), + { code: 'ERR_INVALID_ARG_TYPE' } + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-better-error-messages-port-hostname.js b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-port-hostname.js new file mode 100644 index 00000000000000..e9a87118861b0f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-better-error-messages-port-hostname.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This tests that the error thrown from net.createConnection +// comes with host and port properties. +// See https://github.com/nodejs/node-v0.x-archive/issues/7005 + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const { addresses } = require('../common/internet'); +const { + errorLookupMock, + mockedErrorCode +} = require('../common/dns'); + +// Using port 0 as hostname used is already invalid. +const c = net.createConnection({ + port: 0, + host: addresses.INVALID_HOST, + lookup: common.mustCall(errorLookupMock()) +}); + +c.on('connect', common.mustNotCall()); + +c.on('error', common.mustCall((error) => { + assert.ok(!('port' in error)); + assert.ok(!('host' in error)); + assert.throws(() => { throw error; }, { + errno: mockedErrorCode, + code: mockedErrorCode, + name: 'Error', + message: 'getaddrinfo ENOTFOUND something.invalid', + hostname: addresses.INVALID_HOST, + syscall: 'getaddrinfo' + }); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-bind-twice.js b/cli/tests/node_compat/test/parallel/test-net-bind-twice.js new file mode 100644 index 00000000000000..6500f2aef7ca99 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-bind-twice.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server1 = net.createServer(common.mustNotCall()); +server1.listen(0, '127.0.0.1', common.mustCall(function() { + const server2 = net.createServer(common.mustNotCall()); + server2.listen(this.address().port, '127.0.0.1', common.mustNotCall()); + + server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + server1.close(); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-buffersize.js b/cli/tests/node_compat/test/parallel/test-net-buffersize.js new file mode 100644 index 00000000000000..0831fa52602c5c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-buffersize.js @@ -0,0 +1,59 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const iter = 10; + +const server = net.createServer(function(socket) { + socket.on('readable', function() { + socket.read(); + }); + + socket.on('end', function() { + server.close(); + }); +}); + +server.listen(0, common.mustCall(function() { + const client = net.connect(this.address().port); + + client.on('finish', common.mustCall(() => { + assert.strictEqual(client.bufferSize, 0); + })); + + for (let i = 1; i < iter; i++) { + client.write('a'); + assert.strictEqual(client.bufferSize, i); + } + + client.end(); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-bytes-written-large.js b/cli/tests/node_compat/test/parallel/test-net-bytes-written-large.js new file mode 100644 index 00000000000000..98df17dca57901 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-bytes-written-large.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Regression test for https://github.com/nodejs/node/issues/19562: +// Writing to a socket first tries to push through as much data as possible +// without blocking synchronously, and, if that is not enough, queues more +// data up for asynchronous writing. +// Check that `bytesWritten` accounts for both parts of a write. + +const N = 10000000; +{ + // Variant 1: Write a Buffer. + const server = net.createServer(common.mustCall((socket) => { + socket.end(Buffer.alloc(N), common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, N); + })); + assert.strictEqual(socket.bytesWritten, N); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, N); + server.close(); + })); + })); +} + +{ + // Variant 2: Write a string. + const server = net.createServer(common.mustCall((socket) => { + socket.end('a'.repeat(N), common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, N); + })); + assert.strictEqual(socket.bytesWritten, N); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, N); + server.close(); + })); + })); +} + +{ + // Variant 2: writev() with mixed data. + const server = net.createServer(common.mustCall((socket) => { + socket.cork(); + socket.write('a'.repeat(N)); + assert.strictEqual(socket.bytesWritten, N); + socket.write(Buffer.alloc(N)); + assert.strictEqual(socket.bytesWritten, 2 * N); + socket.end('', common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, 2 * N); + })); + socket.uncork(); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, 2 * N); + server.close(); + })); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-can-reset-timeout.js b/cli/tests/node_compat/test/parallel/test-net-can-reset-timeout.js new file mode 100644 index 00000000000000..535a86a10d1f6b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-can-reset-timeout.js @@ -0,0 +1,64 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Ref: https://github.com/nodejs/node-v0.x-archive/issues/481 + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(stream) { + stream.setTimeout(100); + + stream.resume(); + + stream.once('timeout', common.mustCall(function() { + console.log('timeout'); + // Try to reset the timeout. + stream.write('WHAT.'); + })); + + stream.on('end', common.mustCall(function() { + console.log('server side end'); + stream.end(); + })); +})); + +server.listen(0, common.mustCall(function() { + const c = net.createConnection(this.address().port); + + c.on('data', function() { + c.end(); + }); + + c.on('end', function() { + console.log('client side end'); + server.close(); + }); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-after-destroy.js b/cli/tests/node_compat/test/parallel/test-net-connect-after-destroy.js new file mode 100644 index 00000000000000..b04799f569f138 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-after-destroy.js @@ -0,0 +1,16 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/819. + +require('../common'); +const net = require('net'); + +// Connect to something that we need to DNS resolve +const c = net.createConnection(80, 'google.com'); +c.destroy(); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-buffer.js b/cli/tests/node_compat/test/parallel/test-net-connect-buffer.js new file mode 100644 index 00000000000000..04e71247e425da --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-buffer.js @@ -0,0 +1,86 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// TODO: support not using "new" +const tcp = new net.Server(common.mustCall((s) => { + tcp.close(); + + let buf = ''; + s.setEncoding('utf8'); + s.on('data', function(d) { + buf += d; + }); + + s.on('end', common.mustCall(function() { + console.error('SERVER: end', buf); + assert.strictEqual(buf, "L'État, c'est moi"); + s.end(); + })); +})); + +tcp.listen(0, common.mustCall(function() { + // TODO: support not using "new" + const socket = new net.Stream({ highWaterMark: 0 }); + + let connected = false; + assert.strictEqual(socket.pending, true); + socket.connect(this.address().port, common.mustCall(() => connected = true)); + + assert.strictEqual(socket.pending, true); + assert.strictEqual(socket.connecting, true); + assert.strictEqual(socket.readyState, 'opening'); + + // Write a string that contains a multi-byte character sequence to test that + // `bytesWritten` is incremented with the # of bytes, not # of characters. + const a = "L'État, c'est "; + const b = 'moi'; + + // We're still connecting at this point so the datagram is first pushed onto + // the connect queue. Make sure that it's not added to `bytesWritten` again + // when the actual write happens. + const r = socket.write(a, common.mustCall((er) => { + console.error('write cb'); + assert.ok(connected); + assert.strictEqual(socket.bytesWritten, Buffer.from(a + b).length); + assert.strictEqual(socket.pending, false); + })); + socket.on('close', common.mustCall(() => { + assert.strictEqual(socket.pending, true); + })); + + assert.strictEqual(socket.bytesWritten, Buffer.from(a).length); + assert.strictEqual(r, false); + socket.end(b); + + assert.strictEqual(socket.readyState, 'opening'); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-buffer2.js b/cli/tests/node_compat/test/parallel/test-net-connect-buffer2.js new file mode 100644 index 00000000000000..499f3849f287ef --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-buffer2.js @@ -0,0 +1,63 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const tcp = new net.Server(common.mustCall((s) => { + tcp.close(); + + let buf = ''; + s.setEncoding('utf8'); + s.on('data', function(d) { + buf += d; + }); + + s.on('end', common.mustCall(function() { + console.error('SERVER: end', buf); + assert.strictEqual(buf, "L'État, c'est moi"); + s.end(); + })); +})); + +tcp.listen(0, common.mustCall(function() { + const socket = new net.Stream({ highWaterMark: 0 }); + + let connected = false; + assert.strictEqual(socket.pending, true); + socket.connect(this.address().port, common.mustCall(() => connected = true)); + + assert.strictEqual(socket.pending, true); + assert.strictEqual(socket.connecting, true); + assert.strictEqual(socket.readyState, 'opening'); + + // Write a string that contains a multi-byte character sequence to test that + // `bytesWritten` is incremented with the # of bytes, not # of characters. + const a = "L'État, c'est "; + const b = 'moi'; + + // We're still connecting at this point so the datagram is first pushed onto + // the connect queue. Make sure that it's not added to `bytesWritten` again + // when the actual write happens. + const r = socket.write(a, common.mustCall((er) => { + console.error('write cb'); + assert.ok(connected); + assert.strictEqual(socket.bytesWritten, Buffer.from(a + b).length); + assert.strictEqual(socket.pending, false); + })); + socket.on('close', common.mustCall(() => { + assert.strictEqual(socket.pending, true); + })); + + assert.strictEqual(socket.bytesWritten, Buffer.from(a).length); + assert.strictEqual(r, false); + socket.end(b); + + assert.strictEqual(socket.readyState, 'opening'); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-call-socket-connect.js b/cli/tests/node_compat/test/parallel/test-net-connect-call-socket-connect.js new file mode 100644 index 00000000000000..87412d4adde89a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-call-socket-connect.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// This test checks that calling `net.connect` internally calls +// `Socket.prototype.connect`. +// +// This is important for people who monkey-patch `Socket.prototype.connect` +// since it's not possible to monkey-patch `net.connect` directly (as the core +// `connect` function is called internally in Node instead of calling the +// `exports.connect` function). +// +// Monkey-patching of `Socket.prototype.connect` is done by - among others - +// most APM vendors, the async-listener module and the +// continuation-local-storage module. +// +// Related: +// - https://github.com/nodejs/node/pull/12342 +// - https://github.com/nodejs/node/pull/12852 + +const net = require('net'); +const Socket = net.Socket; + +// Monkey patch Socket.prototype.connect to check that it's called. +const orig = Socket.prototype.connect; +Socket.prototype.connect = common.mustCall(function() { + return orig.apply(this, arguments); +}); + +const server = net.createServer(); + +server.listen(common.mustCall(function() { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(function() { + client.end(); + })); + client.on('end', common.mustCall(function() { + server.close(); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-destroy.js b/cli/tests/node_compat/test/parallel/test-net-connect-destroy.js new file mode 100644 index 00000000000000..bdeb9eb0cdd715 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-destroy.js @@ -0,0 +1,14 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const socket = new net.Socket(); +socket.on('close', common.mustCall()); +socket.destroy(); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-immediate-destroy.js b/cli/tests/node_compat/test/parallel/test-net-connect-immediate-destroy.js new file mode 100644 index 00000000000000..0412d3f5635ef0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-immediate-destroy.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0); +const port = server.address().port; +const socket = net.connect(port, common.localhostIPv4, common.mustNotCall()); +socket.on('error', common.mustNotCall()); +server.close(); +socket.destroy(); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-immediate-finish.js b/cli/tests/node_compat/test/parallel/test-net-connect-immediate-finish.js new file mode 100644 index 00000000000000..51c2176df6148b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-immediate-finish.js @@ -0,0 +1,66 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// This tests that if the socket is still in the 'connecting' state +// when the user calls socket.end() ('finish'), the socket would emit +// 'connect' and defer the handling until the 'connect' event is handled. + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const { addresses } = require('../common/internet'); +const { + errorLookupMock, + mockedErrorCode, + mockedSysCall +} = require('../common/dns'); + +const client = net.connect({ + host: addresses.INVALID_HOST, + port: 80, // Port number doesn't matter because host name is invalid + lookup: common.mustCall(errorLookupMock()) +}, common.mustNotCall()); + +client.once('error', common.mustCall((error) => { + // TODO(BridgeAR): Add a better way to handle not defined properties using + // `assert.throws(fn, object)`. + assert.ok(!('port' in error)); + assert.ok(!('host' in error)); + assert.throws(() => { throw error; }, { + code: mockedErrorCode, + errno: mockedErrorCode, + syscall: mockedSysCall, + hostname: addresses.INVALID_HOST, + message: 'getaddrinfo ENOTFOUND something.invalid' + }); +})); + +client.end(); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-no-arg.js b/cli/tests/node_compat/test/parallel/test-net-connect-no-arg.js new file mode 100644 index 00000000000000..6fd3e88abaab81 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-no-arg.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Tests that net.connect() called without arguments throws ERR_MISSING_ARGS. + +assert.throws(() => { + net.connect(); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); + +assert.throws(() => { + new net.Socket().connect(); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); + +assert.throws(() => { + net.connect({}); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); + +assert.throws(() => { + new net.Socket().connect({}); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-options-ipv6.js b/cli/tests/node_compat/test/parallel/test-net-connect-options-ipv6.js new file mode 100644 index 00000000000000..af87d57133a62c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-options-ipv6.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Test that the family option of net.connect is honored. + +'use strict'; +const common = require('../common'); +if (!common.hasIPv6) + common.skip('no IPv6 support'); + +const assert = require('assert'); +const net = require('net'); + +const hostAddrIPv6 = '::1'; +const HOSTNAME = 'dummy'; + +const server = net.createServer({ allowHalfOpen: true }, (socket) => { + socket.resume(); + socket.on('end', common.mustCall()); + socket.end(); +}); + +function tryConnect() { + const connectOpt = { + host: HOSTNAME, + port: server.address().port, + family: 6, + allowHalfOpen: true, + lookup: common.mustCall((addr, opt, cb) => { + assert.strictEqual(addr, HOSTNAME); + assert.strictEqual(opt.family, 6); + cb(null, hostAddrIPv6, opt.family); + }) + }; + // No `mustCall`, since test could skip, and it's the only path to `close`. + const client = net.connect(connectOpt, () => { + client.resume(); + client.on('end', () => { + // Wait for next uv tick and make sure the socket stream is writable. + setTimeout(function() { + assert(client.writable); + client.end(); + }, 10); + }); + client.on('close', () => server.close()); + }); +} + +server.listen(0, hostAddrIPv6, tryConnect); diff --git a/cli/tests/node_compat/test/parallel/test-net-connect-options-port.js b/cli/tests/node_compat/test/parallel/test-net-connect-options-port.js new file mode 100644 index 00000000000000..6dd10461a6f20c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-connect-options-port.js @@ -0,0 +1,237 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dns = require('dns'); +const net = require('net'); + +// Test wrong type of ports +{ + const portTypeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }; + + syncFailToConnect(true, portTypeError); + syncFailToConnect(false, portTypeError); + syncFailToConnect([], portTypeError, true); + syncFailToConnect({}, portTypeError, true); + syncFailToConnect(null, portTypeError); +} + +// Test out of range ports +{ + const portRangeError = { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' + }; + + syncFailToConnect('', portRangeError); + syncFailToConnect(' ', portRangeError); + syncFailToConnect('0x', portRangeError, true); + syncFailToConnect('-0x1', portRangeError, true); + syncFailToConnect(NaN, portRangeError); + syncFailToConnect(Infinity, portRangeError); + syncFailToConnect(-1, portRangeError); + syncFailToConnect(65536, portRangeError); +} + +// Test invalid hints +{ + // connect({hint}, cb) and connect({hint}) + const hints = (dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL) + 42; + const hintOptBlocks = doConnect([{ port: 42, hints }], + () => common.mustNotCall()); + for (const fn of hintOptBlocks) { + assert.throws(fn, { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: /The argument 'hints' is invalid\. Received \d+/ + }); + } +} + +// Test valid combinations of connect(port) and connect(port, host) +{ + const expectedConnections = 72; + let serverConnected = 0; + + const server = net.createServer(common.mustCall((socket) => { + socket.end('ok'); + if (++serverConnected === expectedConnections) { + server.close(); + } + }, expectedConnections)); + + server.listen(0, common.localhostIPv4, common.mustCall(() => { + const port = server.address().port; + + // Total connections = 3 * 4(canConnect) * 6(doConnect) = 72 + canConnect(port); + canConnect(String(port)); + canConnect(`0x${port.toString(16)}`); + })); + + // Try connecting to random ports, but do so once the server is closed + server.on('close', () => { + asyncFailToConnect(0); + }); +} + +function doConnect(args, getCb) { + return [ + function createConnectionWithCb() { + return net.createConnection.apply(net, args.concat(getCb())) + .resume(); + }, + function createConnectionWithoutCb() { + return net.createConnection.apply(net, args) + .on('connect', getCb()) + .resume(); + }, + function connectWithCb() { + return net.connect.apply(net, args.concat(getCb())) + .resume(); + }, + function connectWithoutCb() { + return net.connect.apply(net, args) + .on('connect', getCb()) + .resume(); + }, + function socketConnectWithCb() { + const socket = new net.Socket(); + return socket.connect.apply(socket, args.concat(getCb())) + .resume(); + }, + function socketConnectWithoutCb() { + const socket = new net.Socket(); + return socket.connect.apply(socket, args) + .on('connect', getCb()) + .resume(); + }, + ]; +} + +function syncFailToConnect(port, assertErr, optOnly) { + const family = 4; + if (!optOnly) { + // connect(port, cb) and connect(port) + const portArgFunctions = doConnect([{ port, family }], + () => common.mustNotCall()); + for (const fn of portArgFunctions) { + assert.throws(fn, assertErr, `${fn.name}(${port})`); + } + + // connect(port, host, cb) and connect(port, host) + const portHostArgFunctions = doConnect([{ port, + host: 'localhost', + family }], + () => common.mustNotCall()); + for (const fn of portHostArgFunctions) { + assert.throws(fn, assertErr, `${fn.name}(${port}, 'localhost')`); + } + } + // connect({port}, cb) and connect({port}) + const portOptFunctions = doConnect([{ port, family }], + () => common.mustNotCall()); + for (const fn of portOptFunctions) { + assert.throws(fn, assertErr, `${fn.name}({port: ${port}})`); + } + + // connect({port, host}, cb) and connect({port, host}) + const portHostOptFunctions = doConnect([{ port: port, + host: 'localhost', + family: family }], + () => common.mustNotCall()); + for (const fn of portHostOptFunctions) { + assert.throws(fn, + assertErr, + `${fn.name}({port: ${port}, host: 'localhost'})`); + } +} + +function canConnect(port) { + const noop = () => common.mustCall(); + const family = 4; + + // connect(port, cb) and connect(port) + const portArgFunctions = doConnect([{ port, family }], noop); + for (const fn of portArgFunctions) { + fn(); + } + + // connect(port, host, cb) and connect(port, host) + const portHostArgFunctions = doConnect([{ port, host: 'localhost', family }], + noop); + for (const fn of portHostArgFunctions) { + fn(); + } + + // connect({port}, cb) and connect({port}) + const portOptFunctions = doConnect([{ port, family }], noop); + for (const fn of portOptFunctions) { + fn(); + } + + // connect({port, host}, cb) and connect({port, host}) + const portHostOptFns = doConnect([{ port, host: 'localhost', family }], + noop); + for (const fn of portHostOptFns) { + fn(); + } +} + +function asyncFailToConnect(port) { + const onError = () => common.mustCall((err) => { + const regexp = /^Error: connect E\w+.+$/; + assert.match(String(err), regexp); + }); + + const dont = () => common.mustNotCall(); + const family = 4; + // connect(port, cb) and connect(port) + const portArgFunctions = doConnect([{ port, family }], dont); + for (const fn of portArgFunctions) { + fn().on('error', onError()); + } + + // connect({port}, cb) and connect({port}) + const portOptFunctions = doConnect([{ port, family }], dont); + for (const fn of portOptFunctions) { + fn().on('error', onError()); + } + + // connect({port, host}, cb) and connect({port, host}) + const portHostOptFns = doConnect([{ port, host: 'localhost', family }], + dont); + for (const fn of portHostOptFns) { + fn().on('error', onError()); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-net-dns-custom-lookup.js b/cli/tests/node_compat/test/parallel/test-net-dns-custom-lookup.js new file mode 100644 index 00000000000000..6847dc57e441ae --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-dns-custom-lookup.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +function check(addressType, cb) { + const server = net.createServer(function(client) { + client.end(); + server.close(); + cb && cb(); + }); + + const address = addressType === 4 ? common.localhostIPv4 : '::1'; + server.listen(0, address, common.mustCall(function() { + net.connect({ + port: this.address().port, + host: 'localhost', + family: addressType, + lookup: lookup + }).on('lookup', common.mustCall(function(err, ip, type) { + assert.strictEqual(err, null); + assert.strictEqual(address, ip); + assert.strictEqual(type, addressType); + })); + })); + + function lookup(host, dnsopts, cb) { + dnsopts.family = addressType; + if (addressType === 4) { + process.nextTick(function() { + cb(null, common.localhostIPv4, 4); + }); + } else { + process.nextTick(function() { + cb(null, '::1', 6); + }); + } + } +} + +check(4, function() { + common.hasIPv6 && check(6); +}); + +// Verify that bad lookup() IPs are handled. +{ + net.connect({ + host: 'localhost', + port: 80, + lookup(host, dnsopts, cb) { + cb(null, undefined, 4); + } + }).on('error', common.expectsError({ code: 'ERR_INVALID_IP_ADDRESS' })); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-dns-error.js b/cli/tests/node_compat/test/parallel/test-net-dns-error.js new file mode 100644 index 00000000000000..8362d646519f30 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-dns-error.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const host = '*'.repeat(64); +// Resolving hostname > 63 characters may return EAI_FAIL (permanent failure). +const errCodes = ['ENOTFOUND', 'EAI_FAIL']; + +const socket = net.connect(42, host, common.mustNotCall()); +socket.on('error', common.mustCall(function(err) { + assert(errCodes.includes(err.code), err); +})); +socket.on('lookup', common.mustCall(function(err, ip, type) { + assert(err instanceof Error); + assert(errCodes.includes(err.code), err); + assert.strictEqual(ip, undefined); + assert.strictEqual(type, undefined); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-dns-lookup-skip.js b/cli/tests/node_compat/test/parallel/test-net-dns-lookup-skip.js new file mode 100644 index 00000000000000..f0fb49781b8fd8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-dns-lookup-skip.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +function check(addressType) { + const server = net.createServer(function(client) { + client.end(); + server.close(); + }); + + const address = addressType === 4 ? '127.0.0.1' : '::1'; + server.listen(0, address, function() { + net.connect(this.address().port, address) + .on('lookup', common.mustNotCall()); + }); +} + +check(4); +common.hasIPv6 && check(6); diff --git a/cli/tests/node_compat/test/parallel/test-net-dns-lookup.js b/cli/tests/node_compat/test/parallel/test-net-dns-lookup.js new file mode 100644 index 00000000000000..57cb90d39bc28d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-dns-lookup.js @@ -0,0 +1,47 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(function(client) { + client.end(); + server.close(); +}); + +server.listen(0, common.mustCall(function() { + net.connect(this.address().port, 'localhost') + .on('lookup', common.mustCall(function(err, ip, type, host) { + assert.strictEqual(err, null); + assert.match(ip, /^(127\.0\.0\.1|::1)$/); + assert.match(type.toString(), /^(4|6)$/); + assert.strictEqual(host, 'localhost'); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-during-close.js b/cli/tests/node_compat/test/parallel/test-net-during-close.js new file mode 100644 index 00000000000000..3262e281b117ce --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-during-close.js @@ -0,0 +1,49 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(0, common.mustCall(function() { + /* eslint-disable no-unused-expressions */ + const client = net.createConnection(this.address().port); + server.close(); + // Server connection event has not yet fired client is still attempting to + // connect. Accessing properties should not throw in this case. + client.remoteAddress; + client.remoteFamily; + client.remotePort; + // Exit now, do not wait for the client error event. + process.exit(0); + /* eslint-enable no-unused-expressions */ +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-eaddrinuse.js b/cli/tests/node_compat/test/parallel/test-net-eaddrinuse.js new file mode 100644 index 00000000000000..d4b9b234fbb2ec --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-eaddrinuse.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server1 = net.createServer(function(socket) { +}); +const server2 = net.createServer(function(socket) { +}); +server1.listen(0, common.mustCall(function() { + server2.on('error', function(error) { + assert.strictEqual(error.message.includes('EADDRINUSE'), true); + server1.close(); + }); + server2.listen(this.address().port); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-end-close.js b/cli/tests/node_compat/test/parallel/test-net-end-close.js new file mode 100644 index 00000000000000..a818dd0974cf28 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-end-close.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// const { internalBinding } = require('internal/test/binding'); +// const { UV_EOF } = internalBinding('uv'); +// const { streamBaseState, kReadBytesOrError } = internalBinding('stream_wrap'); + +const s = new net.Socket({ + handle: { + readStart: function() { + setImmediate(() => { + // streamBaseState[kReadBytesOrError] = UV_EOF; + // internal onread has different shape to Node. + this.onread(new Uint8Array(), -4095); + }); + }, + close: (cb) => setImmediate(cb) + }, + writable: false +}); +assert.strictEqual(s, s.resume()); + +const events = []; + +s.on('end', () => { + events.push('end'); +}); +s.on('close', () => { + events.push('close'); +}); + +process.on('exit', () => { + assert.deepStrictEqual(events, [ 'end', 'close' ]); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-end-destroyed.js b/cli/tests/node_compat/test/parallel/test-net-end-destroyed.js new file mode 100644 index 00000000000000..fc440844ed352d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-end-destroyed.js @@ -0,0 +1,33 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer(); + +server.on('connection', common.mustCall()); + +// Ensure that the socket is not destroyed when the 'end' event is emitted. + +server.listen(common.mustCall(function() { + const socket = net.createConnection({ + port: server.address().port + }); + + socket.on('connect', common.mustCall(function() { + socket.on('end', common.mustCall(function() { + assert.strictEqual(socket.destroyed, false); + server.close(); + })); + + socket.end(); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-end-without-connect.js b/cli/tests/node_compat/test/parallel/test-net-end-without-connect.js new file mode 100644 index 00000000000000..10b7a9e14f30f3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-end-without-connect.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const net = require('net'); + +const sock = new net.Socket(); +sock.end(); // Should not throw. diff --git a/cli/tests/node_compat/test/parallel/test-net-isip.js b/cli/tests/node_compat/test/parallel/test-net-isip.js new file mode 100644 index 00000000000000..c85dbcbfd4a5ef --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-isip.js @@ -0,0 +1,103 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +assert.strictEqual(net.isIP('127.0.0.1'), 4); +assert.strictEqual(net.isIP('x127.0.0.1'), 0); +assert.strictEqual(net.isIP('example.com'), 0); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000::0000'), + 0); +assert.strictEqual(net.isIP('1050:0:0:0:5:600:300c:326b'), 6); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001::'), 6); +assert.strictEqual(net.isIP('2001:dead::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::'), 6); +assert.strictEqual(net.isIP('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 6); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6'), 0); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP('2001:252::1::2008:6'), 0); +assert.strictEqual(net.isIP('::2001:252:1:2008:6'), 6); +assert.strictEqual(net.isIP('::2001:252:1:1.1.1.1'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255.76'), 0); +assert.strictEqual(net.isIP('fe80::2008%eth0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0.0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0@1'), 0); +assert.strictEqual(net.isIP('::anything'), 0); +assert.strictEqual(net.isIP('::1'), 6); +assert.strictEqual(net.isIP('::'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:12345:0000'), 0); +assert.strictEqual(net.isIP('0'), 0); +assert.strictEqual(net.isIP(), 0); +assert.strictEqual(net.isIP(''), 0); +assert.strictEqual(net.isIP(null), 0); +assert.strictEqual(net.isIP(123), 0); +assert.strictEqual(net.isIP(true), 0); +assert.strictEqual(net.isIP({}), 0); +assert.strictEqual(net.isIP({ toString: () => '::2001:252:1:255.255.255.255' }), + 6); +assert.strictEqual(net.isIP({ toString: () => '127.0.0.1' }), 4); +assert.strictEqual(net.isIP({ toString: () => 'bla' }), 0); + +assert.strictEqual(net.isIPv4('127.0.0.1'), true); +assert.strictEqual(net.isIPv4('example.com'), false); +assert.strictEqual(net.isIPv4('2001:252:0:1::2008:6'), false); +assert.strictEqual(net.isIPv4(), false); +assert.strictEqual(net.isIPv4(''), false); +assert.strictEqual(net.isIPv4(null), false); +assert.strictEqual(net.isIPv4(123), false); +assert.strictEqual(net.isIPv4(true), false); +assert.strictEqual(net.isIPv4({}), false); +assert.strictEqual(net.isIPv4({ + toString: () => '::2001:252:1:255.255.255.255' +}), false); +assert.strictEqual(net.isIPv4({ toString: () => '127.0.0.1' }), true); +assert.strictEqual(net.isIPv4({ toString: () => 'bla' }), false); + +assert.strictEqual(net.isIPv6('127.0.0.1'), false); +assert.strictEqual(net.isIPv6('example.com'), false); +assert.strictEqual(net.isIPv6('2001:252:0:1::2008:6'), true); +assert.strictEqual(net.isIPv6(), false); +assert.strictEqual(net.isIPv6(''), false); +assert.strictEqual(net.isIPv6(null), false); +assert.strictEqual(net.isIPv6(123), false); +assert.strictEqual(net.isIPv6(true), false); +assert.strictEqual(net.isIPv6({}), false); +assert.strictEqual(net.isIPv6({ + toString: () => '::2001:252:1:255.255.255.255' +}), true); +assert.strictEqual(net.isIPv6({ toString: () => '127.0.0.1' }), false); +assert.strictEqual(net.isIPv6({ toString: () => 'bla' }), false); diff --git a/cli/tests/node_compat/test/parallel/test-net-isipv4.js b/cli/tests/node_compat/test/parallel/test-net-isipv4.js new file mode 100644 index 00000000000000..abdbd7b0bfd477 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-isipv4.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v4 = [ + '0.0.0.0', + '8.8.8.8', + '127.0.0.1', + '100.100.100.100', + '192.168.0.1', + '18.101.25.153', + '123.23.34.2', + '172.26.168.134', + '212.58.241.131', + '128.0.0.0', + '23.71.254.72', + '223.255.255.255', + '192.0.2.235', + '99.198.122.146', + '46.51.197.88', + '173.194.34.134', +]; + +const v4not = [ + '.100.100.100.100', + '100..100.100.100.', + '100.100.100.100.', + '999.999.999.999', + '256.256.256.256', + '256.100.100.100.100', + '123.123.123', + 'http://123.123.123', + '1000.2.3.4', + '999.2.3.4', + '0000000192.168.0.200', + '192.168.0.2000000000', +]; + +v4.forEach((ip) => { + assert.strictEqual(net.isIPv4(ip), true); +}); + +v4not.forEach((ip) => { + assert.strictEqual(net.isIPv4(ip), false); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-isipv6.js b/cli/tests/node_compat/test/parallel/test-net-isipv6.js new file mode 100644 index 00000000000000..9aa09295707467 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-isipv6.js @@ -0,0 +1,251 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v6 = [ + '::', + '1::', + '::1', + '1::8', + '1::7:8', + '1:2:3:4:5:6:7:8', + '1:2:3:4:5:6::8', + '1:2:3:4:5:6:7::', + '1:2:3:4:5::7:8', + '1:2:3:4:5::8', + '1:2:3::8', + '1::4:5:6:7:8', + '1::6:7:8', + '1::3:4:5:6:7:8', + '1:2:3:4::6:7:8', + '1:2::4:5:6:7:8', + '::2:3:4:5:6:7:8', + '1:2::8', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876', + '3ffe:0b00:0000:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0000', + '::ffff:192.168.1.26', + '2::10', + 'ff02::1', + 'fe80::', + '2002::', + '2001:db8::', + '2001:0db8:1234::', + '::ffff:0:0', + '::ffff:192.168.1.1', + '1:2:3:4::8', + '1::2:3:4:5:6:7', + '1::2:3:4:5:6', + '1::2:3:4:5', + '1::2:3:4', + '1::2:3', + '::2:3:4:5:6:7', + '::2:3:4:5:6', + '::2:3:4:5', + '::2:3:4', + '::2:3', + '::8', + '1:2:3:4:5:6::', + '1:2:3:4:5::', + '1:2:3:4::', + '1:2:3::', + '1:2::', + '1:2:3:4::7:8', + '1:2:3::7:8', + '1:2::7:8', + '1:2:3:4:5:6:1.2.3.4', + '1:2:3:4:5::1.2.3.4', + '1:2:3:4::1.2.3.4', + '1:2:3::1.2.3.4', + '1:2::1.2.3.4', + '1::1.2.3.4', + '1:2:3:4::5:1.2.3.4', + '1:2:3::5:1.2.3.4', + '1:2::5:1.2.3.4', + '1::5:1.2.3.4', + '1::5:11.22.33.44', + 'fe80::217:f2ff:254.7.237.98', + 'fe80::217:f2ff:fe07:ed62', + '2001:DB8:0:0:8:800:200C:417A', + 'FF01:0:0:0:0:0:0:101', + '0:0:0:0:0:0:0:1', + '0:0:0:0:0:0:0:0', + '2001:DB8::8:800:200C:417A', + 'FF01::101', + '0:0:0:0:0:0:13.1.68.3', + '0:0:0:0:0:FFFF:129.144.52.38', + '::13.1.68.3', + '::FFFF:129.144.52.38', + 'fe80:0000:0000:0000:0204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:fe9d:f156', + 'fe80::204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:254.157.241.86', + 'fe80::204:61ff:254.157.241.86', + 'fe80::1', + '2001:0db8:85a3:0000:0000:8a2e:0370:7334', + '2001:db8:85a3:0:0:8a2e:370:7334', + '2001:db8:85a3::8a2e:370:7334', + '2001:0db8:0000:0000:0000:0000:1428:57ab', + '2001:0db8:0000:0000:0000::1428:57ab', + '2001:0db8:0:0:0:0:1428:57ab', + '2001:0db8:0:0::1428:57ab', + '2001:0db8::1428:57ab', + '2001:db8::1428:57ab', + '::ffff:12.34.56.78', + '::ffff:0c22:384e', + '2001:0db8:1234:0000:0000:0000:0000:0000', + '2001:0db8:1234:ffff:ffff:ffff:ffff:ffff', + '2001:db8:a::123', + '::ffff:192.0.2.128', + '::ffff:c000:280', + 'a:b:c:d:e:f:f1:f2', + 'a:b:c::d:e:f:f1', + 'a:b:c::d:e:f', + 'a:b:c::d:e', + 'a:b:c::d', + '::a', + '::a:b:c', + '::a:b:c:d:e:f:f1', + 'a::', + 'a:b:c::', + 'a:b:c:d:e:f:f1::', + 'a:bb:ccc:dddd:000e:00f:0f::', + '0:a:0:a:0:0:0:a', + '0:a:0:0:a:0:0:a', + '2001:db8:1:1:1:1:0:0', + '2001:db8:1:1:1:0:0:0', + '2001:db8:1:1:0:0:0:0', + '2001:db8:1:0:0:0:0:0', + '2001:db8:0:0:0:0:0:0', + '2001:0:0:0:0:0:0:0', + 'A:BB:CCC:DDDD:000E:00F:0F::', + '0:0:0:0:0:0:0:a', + '0:0:0:0:a:0:0:0', + '0:0:0:a:0:0:0:0', + 'a:0:0:a:0:0:a:a', + 'a:0:0:a:0:0:0:a', + 'a:0:0:0:a:0:0:a', + 'a:0:0:0:a:0:0:0', + 'a:0:0:0:0:0:0:0', + 'fe80::7:8%eth0', + 'fe80::7:8%1', +]; + +const v6not = [ + '', + '1:', + ':1', + '11:36:12', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', + '2001:0000:1234:0000:00001:C1C0:ABCD:0876', + '2001:0000:1234: 0000:0000:C1C0:ABCD:0876', + '2001:1:1:1:1:1:255Z255X255Y255', + '3ffe:0b00:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0000:0001', + '3ffe:b00::1::a', + '::1111:2222:3333:4444:5555:6666::', + '1:2:3::4:5::7:8', + '12345::6:7:8', + '1::5:400.2.3.4', + '1::5:260.2.3.4', + '1::5:256.2.3.4', + '1::5:1.256.3.4', + '1::5:1.2.256.4', + '1::5:1.2.3.256', + '1::5:300.2.3.4', + '1::5:1.300.3.4', + '1::5:1.2.300.4', + '1::5:1.2.3.300', + '1::5:900.2.3.4', + '1::5:1.900.3.4', + '1::5:1.2.900.4', + '1::5:1.2.3.900', + '1::5:300.300.300.300', + '1::5:3000.30.30.30', + '1::400.2.3.4', + '1::260.2.3.4', + '1::256.2.3.4', + '1::1.256.3.4', + '1::1.2.256.4', + '1::1.2.3.256', + '1::300.2.3.4', + '1::1.300.3.4', + '1::1.2.300.4', + '1::1.2.3.300', + '1::900.2.3.4', + '1::1.900.3.4', + '1::1.2.900.4', + '1::1.2.3.900', + '1::300.300.300.300', + '1::3000.30.30.30', + '::400.2.3.4', + '::260.2.3.4', + '::256.2.3.4', + '::1.256.3.4', + '::1.2.256.4', + '::1.2.3.256', + '::300.2.3.4', + '::1.300.3.4', + '::1.2.300.4', + '::1.2.3.300', + '::900.2.3.4', + '::1.900.3.4', + '::1.2.900.4', + '::1.2.3.900', + '::300.300.300.300', + '::3000.30.30.30', + '2001:DB8:0:0:8:800:200C:417A:221', + 'FF01::101::2', + '1111:2222:3333:4444::5555:', + '1111:2222:3333::5555:', + '1111:2222::5555:', + '1111::5555:', + '::5555:', + ':::', + '1111:', + ':', + ':1111:2222:3333:4444::5555', + ':1111:2222:3333::5555', + ':1111:2222::5555', + ':1111::5555', + ':::5555', + '1.2.3.4:1111:2222:3333:4444::5555', + '1.2.3.4:1111:2222:3333::5555', + '1.2.3.4:1111:2222::5555', + '1.2.3.4:1111::5555', + '1.2.3.4::5555', + '1.2.3.4::', + 'fe80:0000:0000:0000:0204:61ff:254.157.241.086', + '123', + 'ldkfj', + '2001::FFD3::57ab', + '2001:db8:85a3::8a2e:37023:7334', + '2001:db8:85a3::8a2e:370k:7334', + '1:2:3:4:5:6:7:8:9', + '1::2::3', + '1:::3:4:5', + '1:2:3::4:5:6:7:8:9', + '::ffff:2.3.4', + '::ffff:257.1.2.3', + '::ffff:12345678901234567890.1.26', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876 0', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', +]; + +v6.forEach((ip) => { + assert.strictEqual(net.isIPv6(ip), true); +}); + +v6not.forEach((ip) => { + assert.strictEqual(net.isIPv6(ip), false); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-listen-after-destroying-stdin.js b/cli/tests/node_compat/test/parallel/test-net-listen-after-destroying-stdin.js new file mode 100644 index 00000000000000..aa1c2ec28a32e2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-listen-after-destroying-stdin.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Just test that destroying stdin doesn't mess up listening on a server. +// This is a regression test for +// https://github.com/nodejs/node-v0.x-archive/issues/746. + +const common = require('../common'); +const net = require('net'); + +process.stdin.destroy(); + +const server = net.createServer(common.mustCall((socket) => { + console.log('accepted...'); + socket.end(common.mustCall(() => { console.log('finished...'); })); + server.close(common.mustCall(() => { console.log('closed'); })); +})); + + +server.listen(0, common.mustCall(() => { + console.log('listening...'); + + net.createConnection(server.address().port); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-listen-close-server-callback-is-not-function.js b/cli/tests/node_compat/test/parallel/test-net-listen-close-server-callback-is-not-function.js new file mode 100644 index 00000000000000..74e7dbd8cd2175 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-listen-close-server-callback-is-not-function.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(common.mustNotCall()); + +server.on('close', common.mustCall()); + +server.listen(0, common.mustNotCall()); + +server.close('bad argument'); diff --git a/cli/tests/node_compat/test/parallel/test-net-listen-close-server.js b/cli/tests/node_compat/test/parallel/test-net-listen-close-server.js new file mode 100644 index 00000000000000..16e861c8886eb0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-listen-close-server.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { +}); +server.listen(0, common.mustNotCall()); +server.on('error', common.mustNotCall()); +server.close(); diff --git a/cli/tests/node_compat/test/parallel/test-net-listen-error.js b/cli/tests/node_compat/test/parallel/test-net-listen-error.js new file mode 100644 index 00000000000000..25d2cd90806309 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-listen-error.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { +}); +server.listen(1, '1.1.1.1', common.mustNotCall()); // EACCES or EADDRNOTAVAIL +server.on('error', common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-net-listen-invalid-port.js b/cli/tests/node_compat/test/parallel/test-net-listen-invalid-port.js new file mode 100644 index 00000000000000..10685f1d0ffedb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-listen-invalid-port.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// This test ensures that port numbers are validated in *all* kinds of `listen` +// calls. If an invalid port is supplied, ensures a `RangeError` is thrown. +// https://github.com/nodejs/node/issues/5727 + +const assert = require('assert'); +const net = require('net'); + +const invalidPort = -1 >>> 0; + +// TODO: support net.Server() without new + +new net.Server().listen(0, function() { + const address = this.address(); + const key = `${address.family}:${address.address}:0`; + + assert.strictEqual(this._connectionKey, key); + this.close(); +}); + +// The first argument is a configuration object +assert.throws(() => { + new net.Server().listen({ port: invalidPort }, common.mustNotCall()); +}, { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' +}); + +// The first argument is the port, no IP given. +assert.throws(() => { + new net.Server().listen(invalidPort, common.mustNotCall()); +}, { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' +}); + +// The first argument is the port, the second an IP. +assert.throws(() => { + new net.Server().listen(invalidPort, '0.0.0.0', common.mustNotCall()); +}, { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-listening.js b/cli/tests/node_compat/test/parallel/test-net-listening.js new file mode 100644 index 00000000000000..4ddf72d33d9ad9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-listening.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +assert.strictEqual(server.listening, false); + +server.listen(0, common.mustCall(() => { + assert.strictEqual(server.listening, true); + + server.close(common.mustCall(() => { + assert.strictEqual(server.listening, false); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-local-address-port.js b/cli/tests/node_compat/test/parallel/test-net-local-address-port.js new file mode 100644 index 00000000000000..98a63a952169c1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-local-address-port.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall(function(socket) { + assert.strictEqual(socket.localAddress, common.localhostIPv4); + assert.strictEqual(socket.localPort, this.address().port); + assert.strictEqual(socket.localFamily, this.address().family); + socket.on('end', function() { + server.close(); + }); + socket.resume(); +})); + +server.listen(0, common.localhostIPv4, function() { + const client = net.createConnection(this.address() + .port, common.localhostIPv4); + client.on('connect', function() { + client.end(); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-localerror.js b/cli/tests/node_compat/test/parallel/test-net-localerror.js new file mode 100644 index 00000000000000..2c60d00d588057 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-localerror.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const connect = (opts, code, type) => { + assert.throws( + () => net.connect(opts), + { code, name: type.name } + ); +}; + +connect({ + host: 'localhost', + port: 0, + localAddress: 'foobar', +}, 'ERR_INVALID_IP_ADDRESS', TypeError); + +connect({ + host: 'localhost', + port: 0, + localPort: 'foobar', +}, 'ERR_INVALID_ARG_TYPE', TypeError); diff --git a/cli/tests/node_compat/test/parallel/test-net-options-lookup.js b/cli/tests/node_compat/test/parallel/test-net-options-lookup.js new file mode 100644 index 00000000000000..f03c1e4ff37479 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-options-lookup.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +['foobar', 1, {}, []].forEach((input) => connectThrows(input)); + +// Using port 0 as lookup is emitted before connecting. +function connectThrows(input) { + const opts = { + host: 'localhost', + port: 0, + lookup: input + }; + + assert.throws(() => { + net.connect(opts); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +connectDoesNotThrow(() => {}); + +function connectDoesNotThrow(input) { + const opts = { + host: 'localhost', + port: 0, + lookup: input + }; + + return net.connect(opts); +} + +{ + // Verify that an error is emitted when an invalid address family is returned. + const s = connectDoesNotThrow((host, options, cb) => { + cb(null, '127.0.0.1', 100); + }); + + s.on('error', common.expectsError({ + code: 'ERR_INVALID_ADDRESS_FAMILY', + host: 'localhost', + port: 0, + message: 'Invalid address family: 100 localhost:0' + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-pause-resume-connecting.js b/cli/tests/node_compat/test/parallel/test-net-pause-resume-connecting.js new file mode 100644 index 00000000000000..07f3254625ec34 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-pause-resume-connecting.js @@ -0,0 +1,102 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +let connections = 0; +let dataEvents = 0; +let conn; + + +// Server +const server = net.createServer(function(conn) { + connections++; + conn.end('This was the year he fell to pieces.'); + + if (connections === 5) + server.close(); +}); + +server.listen(0, function() { + // Client 1 + conn = net.createConnection(this.address().port, 'localhost'); + conn.resume(); + conn.on('data', onDataOk); + + + // Client 2 + conn = net.createConnection(this.address().port, 'localhost'); + conn.pause(); + conn.resume(); + conn.on('data', onDataOk); + + + // Client 3 + conn = net.createConnection(this.address().port, 'localhost'); + conn.pause(); + conn.on('data', common.mustNotCall()); + scheduleTearDown(conn); + + + // Client 4 + conn = net.createConnection(this.address().port, 'localhost'); + conn.resume(); + conn.pause(); + conn.resume(); + conn.on('data', onDataOk); + + + // Client 5 + conn = net.createConnection(this.address().port, 'localhost'); + conn.resume(); + conn.resume(); + conn.pause(); + conn.on('data', common.mustNotCall()); + scheduleTearDown(conn); + + function onDataOk() { + dataEvents++; + } + + function scheduleTearDown(conn) { + setTimeout(function() { + conn.removeAllListeners('data'); + conn.resume(); + }, 100); + } +}); + + +// Exit sanity checks +process.on('exit', function() { + assert.strictEqual(connections, 5); + assert.strictEqual(dataEvents, 3); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-persistent-ref-unref.js b/cli/tests/node_compat/test/parallel/test-net-persistent-ref-unref.js new file mode 100644 index 00000000000000..06e2c6d0f1c8f8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-persistent-ref-unref.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); +const { internalBinding } = require('internal/test/binding'); +const TCPWrap = internalBinding('tcp_wrap').TCP; + +const echoServer = net.createServer((conn) => { + conn.end(); +}); + +const ref = TCPWrap.prototype.ref; +const unref = TCPWrap.prototype.unref; + +let refCount = 0; + +TCPWrap.prototype.ref = function() { + ref.call(this); + refCount++; + assert.strictEqual(refCount, 0); +}; + +TCPWrap.prototype.unref = function() { + unref.call(this); + refCount--; + assert.strictEqual(refCount, -1); +}; + +echoServer.listen(0); + +echoServer.on('listening', function() { + const sock = new net.Socket(); + sock.unref(); + sock.ref(); + sock.connect(this.address().port); + sock.on('end', () => { + assert.strictEqual(refCount, 0); + echoServer.close(); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-pipe-connect-errors.js b/cli/tests/node_compat/test/parallel/test-net-pipe-connect-errors.js new file mode 100644 index 00000000000000..6266e4f072f54d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-pipe-connect-errors.js @@ -0,0 +1,104 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const net = require('net'); +const assert = require('assert'); + +// Test if ENOTSOCK is fired when trying to connect to a file which is not +// a socket. + +let emptyTxt; + +if (common.isWindows) { + // On Win, common.PIPE will be a named pipe, so we use an existing empty + // file instead + emptyTxt = fixtures.path('empty.txt'); +} else { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + // Keep the file name very short so that we don't exceed the 108 char limit + // on CI for a POSIX socket. Even though this isn't actually a socket file, + // the error will be different from the one we are expecting if we exceed the + // limit. + emptyTxt = `${tmpdir.path}0.txt`; + + function cleanup() { + try { + fs.unlinkSync(emptyTxt); + } catch (e) { + assert.strictEqual(e.code, 'ENOENT'); + } + } + process.on('exit', cleanup); + cleanup(); + fs.writeFileSync(emptyTxt, ''); +} + +const notSocketClient = net.createConnection(emptyTxt, function() { + assert.fail('connection callback should not run'); +}); + +notSocketClient.on('error', common.mustCall(function(err) { + assert(err.code === 'ENOTSOCK' || err.code === 'ECONNREFUSED', + `received ${err.code} instead of ENOTSOCK or ECONNREFUSED`); +})); + + +// Trying to connect to not-existing socket should result in ENOENT error +const noEntSocketClient = net.createConnection('no-ent-file', function() { + assert.fail('connection to non-existent socket, callback should not run'); +}); + +noEntSocketClient.on('error', common.mustCall(function(err) { + assert.strictEqual(err.code, 'ENOENT'); +})); + + +// On Windows or IBMi or when running as root, +// a chmod has no effect on named pipes +if (!common.isWindows && !common.isIBMi && process.getuid() !== 0) { + // Trying to connect to a socket one has no access to should result in EACCES + const accessServer = net.createServer( + common.mustNotCall('server callback should not run')); + accessServer.listen(common.PIPE, common.mustCall(function() { + fs.chmodSync(common.PIPE, 0); + + const accessClient = net.createConnection(common.PIPE, function() { + assert.fail('connection should get EACCES, callback should not run'); + }); + + accessClient.on('error', common.mustCall(function(err) { + assert.strictEqual(err.code, 'EACCES'); + accessServer.close(); + })); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-remote-address-port.js b/cli/tests/node_compat/test/parallel/test-net-remote-address-port.js new file mode 100644 index 00000000000000..c1dc0282c6803e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-remote-address-port.js @@ -0,0 +1,91 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const net = require('net'); + +let conns_closed = 0; + +const remoteAddrCandidates = [ common.localhostIPv4, + '::1', + '::ffff:127.0.0.1' ]; + +const remoteFamilyCandidates = ['IPv4', 'IPv6']; + +const server = net.createServer(common.mustCall(function(socket) { + assert.ok(remoteAddrCandidates.includes(socket.remoteAddress), + `Invalid remoteAddress: ${socket.remoteAddress}`); + assert.ok(remoteFamilyCandidates.includes(socket.remoteFamily), + `Invalid remoteFamily: ${socket.remoteFamily}`); + assert.ok(socket.remotePort); + assert.notStrictEqual(socket.remotePort, this.address().port); + socket.on('end', function() { + if (++conns_closed === 2) server.close(); + }); + socket.on('close', function() { + assert.ok(remoteAddrCandidates.includes(socket.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(socket.remoteFamily)); + }); + socket.resume(); +}, 2)); + +server.listen(0, function() { + const client = net.createConnection(this.address().port, '127.0.0.1'); + const client2 = net.createConnection(this.address().port); + + assert.strictEqual(client.remoteAddress, undefined); + assert.strictEqual(client.remoteFamily, undefined); + assert.strictEqual(client.remotePort, undefined); + assert.strictEqual(client2.remoteAddress, undefined); + assert.strictEqual(client2.remoteFamily, undefined); + assert.strictEqual(client2.remotePort, undefined); + + client.on('connect', function() { + assert.ok(remoteAddrCandidates.includes(client.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client.remoteFamily)); + assert.strictEqual(client.remotePort, server.address().port); + client.end(); + }); + client.on('close', function() { + assert.ok(remoteAddrCandidates.includes(client.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client.remoteFamily)); + }); + client2.on('connect', function() { + assert.ok(remoteAddrCandidates.includes(client2.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client2.remoteFamily)); + assert.strictEqual(client2.remotePort, server.address().port); + client2.end(); + }); + client2.on('close', function() { + assert.ok(remoteAddrCandidates.includes(client2.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client2.remoteFamily)); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-call-listen-multiple-times.js b/cli/tests/node_compat/test/parallel/test-net-server-call-listen-multiple-times.js new file mode 100644 index 00000000000000..30b443b1806faf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-call-listen-multiple-times.js @@ -0,0 +1,56 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// TODO: support net.Server() without new + +// First test. Check that after error event you can listen right away. +{ + const dummyServer = new net.Server(); + const server = new net.Server(); + + // Run some server in order to simulate EADDRINUSE error. + dummyServer.listen(common.mustCall(() => { + // Try to listen used port. + server.listen(dummyServer.address().port); + })); + + server.on('error', common.mustCall((e) => { + server.listen(common.mustCall(() => { + dummyServer.close(); + server.close(); + })); + })); +} + +// Second test. Check that second listen call throws an error. +{ + const server = new net.Server(); + + server.listen(common.mustCall(() => server.close())); + + assert.throws(() => server.listen(), { + code: 'ERR_SERVER_ALREADY_LISTEN', + name: 'Error' + }); +} + +// Third test. +// Check that after the close call you can run listen method just fine. +{ + const server = new net.Server(); + + server.listen(common.mustCall(() => { + server.close(); + server.listen(common.mustCall(() => server.close())); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-server-capture-rejection.js b/cli/tests/node_compat/test/parallel/test-net-server-capture-rejection.js new file mode 100644 index 00000000000000..d8096251e20e71 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-capture-rejection.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const { createServer, connect } = require('net'); + +events.captureRejections = true; + +const server = createServer(common.mustCall(async (sock) => { + server.close(); + + const _err = new Error('kaboom'); + sock.on('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + throw _err; +})); + +server.listen(0, common.mustCall(() => { + const sock = connect( + server.address().port, + server.address().host + ); + + sock.on('close', common.mustCall()); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-close.js b/cli/tests/node_compat/test/parallel/test-net-server-close.js new file mode 100644 index 00000000000000..1be4a9c44c99d7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-close.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const sockets = []; + +const server = net.createServer(function(c) { + c.on('close', common.mustCall()); + + sockets.push(c); + + if (sockets.length === 2) { + assert.strictEqual(server.close(), server); + sockets.forEach((c) => c.destroy()); + } +}); + +server.on('close', common.mustCall()); + +assert.strictEqual(server, server.listen(0, () => { + net.createConnection(server.address().port); + net.createConnection(server.address().port); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-listen-options-signal.js b/cli/tests/node_compat/test/parallel/test-net-server-listen-options-signal.js new file mode 100644 index 00000000000000..ecb14deca121e2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-listen-options-signal.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + // Test bad signal. + const server = net.createServer(); + assert.throws( + () => server.listen({ port: 0, signal: 'INVALID_SIGNAL' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +{ + // Test close. + const server = net.createServer(); + const controller = new AbortController(); + server.on('close', common.mustCall()); + server.listen({ port: 0, signal: controller.signal }); + controller.abort(); +} + +{ + // Test close with pre-aborted signal. + const server = net.createServer(); + const signal = AbortSignal.abort(); + server.on('close', common.mustCall()); + server.listen({ port: 0, signal }); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-server-listen-options.js b/cli/tests/node_compat/test/parallel/test-net-server-listen-options.js new file mode 100644 index 00000000000000..9a515fd91073d4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-listen-options.js @@ -0,0 +1,101 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +function close() { this.close(); } + +{ + // Test listen() + net.createServer().listen().on('listening', common.mustCall(close)); + // Test listen(cb) + net.createServer().listen(common.mustCall(close)); + // Test listen(port) + net.createServer().listen(0).on('listening', common.mustCall(close)); + // Test listen({port}) + net.createServer().listen({ port: 0 }) + .on('listening', common.mustCall(close)); +} + +// Test listen(port, cb) and listen({ port }, cb) combinations +const listenOnPort = [ + (port, cb) => net.createServer().listen({ port }, cb), + (port, cb) => net.createServer().listen(port, cb), +]; + +{ + const assertPort = () => { + return common.expectsError({ + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' + }); + }; + + for (const listen of listenOnPort) { + // Arbitrary unused ports + listen('0', common.mustCall(close)); + listen(0, common.mustCall(close)); + listen(undefined, common.mustCall(close)); + listen(null, common.mustCall(close)); + // Test invalid ports + assert.throws(() => listen(-1, common.mustNotCall()), assertPort()); + assert.throws(() => listen(NaN, common.mustNotCall()), assertPort()); + assert.throws(() => listen(123.456, common.mustNotCall()), assertPort()); + assert.throws(() => listen(65536, common.mustNotCall()), assertPort()); + assert.throws(() => listen(1 / 0, common.mustNotCall()), assertPort()); + assert.throws(() => listen(-1 / 0, common.mustNotCall()), assertPort()); + } + // In listen(options, cb), port takes precedence over path + assert.throws(() => { + net.createServer().listen({ port: -1, path: common.PIPE }, + common.mustNotCall()); + }, assertPort()); +} + +{ + function shouldFailToListen(options) { + const fn = () => { + net.createServer().listen(options, common.mustNotCall()); + }; + + if (typeof options === 'object' && + !(('port' in options) || ('path' in options))) { + assert.throws(fn, + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: /^The argument 'options' must have the property "port" or "path"\. Received .+$/, + }); + } else { + assert.throws(fn, + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: /^The argument 'options' is invalid\. Received .+$/, + }); + } + } + + shouldFailToListen(false, { port: false }); + shouldFailToListen({ port: false }); + shouldFailToListen(true); + shouldFailToListen({ port: true }); + // Invalid fd as listen(handle) + shouldFailToListen({ fd: -1 }); + // Invalid path in listen(options) + shouldFailToListen({ path: -1 }); + + // Neither port or path are specified in options + shouldFailToListen({}); + shouldFailToListen({ host: 'localhost' }); + shouldFailToListen({ host: 'localhost:3000' }); + shouldFailToListen({ host: { port: 3000 } }); + shouldFailToListen({ exclusive: true }); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-server-listen-path.js b/cli/tests/node_compat/test/parallel/test-net-server-listen-path.js new file mode 100644 index 00000000000000..559e9c7eb4a8b0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-listen-path.js @@ -0,0 +1,100 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function closeServer() { + return common.mustCall(function() { + this.close(); + }); +} + +let counter = 0; + +// Avoid conflict with listen-handle +function randomPipePath() { + return `${common.PIPE}-listen-path-${counter++}`; +} + +// Test listen(path) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen(handlePath) + .on('listening', closeServer()); +} + +// Test listen({path}) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen({ path: handlePath }) + .on('listening', closeServer()); +} + +// Test listen(path, cb) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen(handlePath, closeServer()); +} + +// Test listen(path, cb) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen({ path: handlePath }, closeServer()); +} + +// Test pipe chmod +{ + const handlePath = randomPipePath(); + + const server = net.createServer() + .listen({ + path: handlePath, + readableAll: true, + writableAll: true + }, common.mustCall(() => { + if (process.platform !== 'win32') { + const mode = fs.statSync(handlePath).mode; + assert.notStrictEqual(mode & fs.constants.S_IROTH, 0); + assert.notStrictEqual(mode & fs.constants.S_IWOTH, 0); + } + server.close(); + })); +} + +// TODO(cmorten): seems Deno.listen() for Unix domains isn't throwing +// Deno.errors.AddrInUse errors as would expect...? +// Test should emit "error" events when listening fails. +// { +// const handlePath = randomPipePath(); +// const server1 = net.createServer().listen({ path: handlePath }, () => { +// // As the handlePath is in use, binding to the same address again should +// // make the server emit an 'EADDRINUSE' error. +// const server2 = net.createServer() +// .listen({ +// path: handlePath, +// writableAll: true, +// }, common.mustNotCall()); + +// server2.on('error', common.mustCall((err) => { +// server1.close(); +// assert.strictEqual(err.code, 'EADDRINUSE'); +// assert.match(err.message, /^listen EADDRINUSE: address already in use/); +// })); +// }); +// } diff --git a/cli/tests/node_compat/test/parallel/test-net-server-listen-remove-callback.js b/cli/tests/node_compat/test/parallel/test-net-server-listen-remove-callback.js new file mode 100644 index 00000000000000..00a32e1186cee4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-listen-remove-callback.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Server should only fire listen callback once +const server = net.createServer(); + +server.on('close', function() { + const listeners = server.listeners('listening'); + console.log('Closed, listeners:', listeners.length); + assert.strictEqual(listeners.length, 0); +}); + +server.listen(0, function() { + server.close(); +}); + +server.once('close', function() { + server.listen(0, function() { + server.close(); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-max-connections.js b/cli/tests/node_compat/test/parallel/test-net-server-max-connections.js new file mode 100644 index 00000000000000..76a6098087ff94 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-max-connections.js @@ -0,0 +1,114 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const net = require('net'); + +// This test creates 20 connections to a server and sets the server's +// maxConnections property to 10. The first 10 connections make it through +// and the last 10 connections are rejected. + +const N = 20; +let closes = 0; +const waits = []; + +const server = net.createServer(common.mustCall(function(connection) { + connection.write('hello'); + waits.push(function() { connection.end(); }); +}, N / 2)); + +server.listen(0, function() { + makeConnection(0); +}); + +server.maxConnections = N / 2; + + +function makeConnection(index) { + const c = net.createConnection(server.address().port); + let gotData = false; + + c.on('connect', function() { + if (index + 1 < N) { + makeConnection(index + 1); + } + + c.on('close', function() { + console.error(`closed ${index}`); + closes++; + + if (closes < N / 2) { + assert.ok( + server.maxConnections <= index, + `${index} should not have been one of the first closed connections` + ); + } + + if (closes === N / 2) { + let cb; + console.error('calling wait callback.'); + while ((cb = waits.shift()) !== undefined) { + cb(); + } + server.close(); + } + + if (index < server.maxConnections) { + assert.strictEqual(gotData, true, + `${index} didn't get data, but should have`); + } else { + assert.strictEqual(gotData, false, + `${index} got data, but shouldn't have`); + } + }); + }); + + c.on('end', function() { c.end(); }); + + c.on('data', function(b) { + gotData = true; + assert.ok(b.length > 0); + }); + + c.on('error', function(e) { + // Retry if SmartOS and ECONNREFUSED. See + // https://github.com/nodejs/node/issues/2663. + if (common.isSunOS && (e.code === 'ECONNREFUSED')) { + c.connect(server.address().port); + } + console.error(`error ${index}: ${e}`); + }); +} + + +process.on('exit', function() { + assert.strictEqual(closes, N); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-options.js b/cli/tests/node_compat/test/parallel/test-net-server-options.js new file mode 100644 index 00000000000000..6b532e427723d8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-options.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +assert.throws(() => net.createServer('path'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws(() => net.createServer(0), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-pause-on-connect.js b/cli/tests/node_compat/test/parallel/test-net-server-pause-on-connect.js new file mode 100644 index 00000000000000..9329a306a675a4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-pause-on-connect.js @@ -0,0 +1,79 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const msg = 'test'; +let stopped = true; +let server1Sock; + + +const server1ConnHandler = (socket) => { + socket.on('data', function(data) { + if (stopped) { + assert.fail('data event should not have happened yet'); + } + + assert.strictEqual(data.toString(), msg); + socket.end(); + server1.close(); + }); + + server1Sock = socket; +}; + +const server1 = net.createServer({ pauseOnConnect: true }, server1ConnHandler); + +const server2ConnHandler = (socket) => { + socket.on('data', function(data) { + assert.strictEqual(data.toString(), msg); + socket.end(); + server2.close(); + + assert.strictEqual(server1Sock.bytesRead, 0); + server1Sock.resume(); + stopped = false; + }); +}; + +const server2 = net.createServer({ pauseOnConnect: false }, server2ConnHandler); + +server1.listen(0, function() { + const clientHandler = common.mustCall(function() { + server2.listen(0, function() { + net.createConnection({ port: this.address().port }).write(msg); + }); + }); + net.createConnection({ port: this.address().port }).write(msg, clientHandler); +}); + +process.on('exit', function() { + assert.strictEqual(stopped, false); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-try-ports.js b/cli/tests/node_compat/test/parallel/test-net-server-try-ports.js new file mode 100644 index 00000000000000..69dbc78b8d2acd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-try-ports.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This test binds to one port, then attempts to start a server on that +// port. It should be EADDRINUSE but be able to then bind to another port. +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// TODO: support net.Server() without new + +const server1 = new net.Server(); + +const server2 = new net.Server(); + +server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + + server2.listen(0, common.mustCall(function() { + server1.close(); + server2.close(); + })); +})); + +server1.listen(0, common.mustCall(function() { + // This should make server2 emit EADDRINUSE + server2.listen(this.address().port); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-unref-persistent.js b/cli/tests/node_compat/test/parallel/test-net-server-unref-persistent.js new file mode 100644 index 00000000000000..34293df9ea39e8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-unref-persistent.js @@ -0,0 +1,19 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); +const server = net.createServer(); + +// Unref before listening +server.unref(); +server.listen(); + +// If the timeout fires, that means the server held the event loop open +// and the unref() was not persistent. Close the server and fail the test. +setTimeout(common.mustNotCall(), 1000).unref(); diff --git a/cli/tests/node_compat/test/parallel/test-net-server-unref.js b/cli/tests/node_compat/test/parallel/test-net-server-unref.js new file mode 100644 index 00000000000000..4820c3be44ba24 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-server-unref.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const s = net.createServer(); +s.listen(0); +s.unref(); + +setTimeout(common.mustNotCall(), 1000).unref(); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-close-after-end.js b/cli/tests/node_compat/test/parallel/test-net-socket-close-after-end.js new file mode 100644 index 00000000000000..8e19e09ab806ae --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-close-after-end.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +server.on('connection', (socket) => { + let endEmitted = false; + + socket.once('readable', () => { + setTimeout(() => { + socket.read(); + }, common.platformTimeout(100)); + }); + socket.on('end', () => { + endEmitted = true; + }); + socket.on('close', () => { + assert(endEmitted); + server.close(); + }); + socket.end('foo'); +}); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port, () => { + socket.end('foo'); + }); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-connect-without-cb.js b/cli/tests/node_compat/test/parallel/test-net-socket-connect-without-cb.js new file mode 100644 index 00000000000000..8f810938051c5b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-connect-without-cb.js @@ -0,0 +1,33 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// This test ensures that socket.connect can be called without callback +// which is optional. + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.end(); + server.close(); +})).listen(0, common.mustCall(function() { + const client = new net.Socket(); + + client.on('connect', common.mustCall(function() { + client.end(); + })); + + const address = server.address(); + if (!common.hasIPv6 && address.family === 'IPv6') { + // Necessary to pass CI running inside containers. + client.connect(address.port); + } else { + client.connect(address); + } +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-connecting.js b/cli/tests/node_compat/test/parallel/test-net-socket-connecting.js new file mode 100644 index 00000000000000..96883f5e12cc9d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-connecting.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer((conn) => { + conn.end(); + server.close(); +}).listen(0, () => { + const client = net.connect(server.address().port, () => { + assert.strictEqual(client.connecting, false); + + // Legacy getter + assert.strictEqual(client._connecting, false); + client.end(); + }); + assert.strictEqual(client.connecting, true); + + // Legacy getter + assert.strictEqual(client._connecting, true); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-destroy-send.js b/cli/tests/node_compat/test/parallel/test-net-socket-destroy-send.js new file mode 100644 index 00000000000000..47ac7c1add4413 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-destroy-send.js @@ -0,0 +1,31 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + const conn = net.createConnection(port); + + conn.on('connect', common.mustCall(function() { + // Test destroy returns this, even on multiple calls when it short-circuits. + assert.strictEqual(conn, conn.destroy().destroy()); + conn.on('error', common.mustNotCall()); + + conn.write(Buffer.from('kaboom'), common.expectsError({ + code: 'ERR_STREAM_DESTROYED', + message: 'Cannot call write after a stream was destroyed', + name: 'Error' + })); + server.close(); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-destroy-twice.js b/cli/tests/node_compat/test/parallel/test-net-socket-destroy-twice.js new file mode 100644 index 00000000000000..05027494277e01 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-destroy-twice.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0); +const port = server.address().port; +const conn = net.createConnection(port); + +conn.on('error', common.mustCall(() => { + conn.destroy(); +})); + +conn.on('close', common.mustCall()); +server.close(); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-end-before-connect.js b/cli/tests/node_compat/test/parallel/test-net-socket-end-before-connect.js new file mode 100644 index 00000000000000..be83964be37c5e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-end-before-connect.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const net = require('net'); + +const server = net.createServer(); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port); + socket.on('close', common.mustCall(() => server.close())); + socket.end(); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-end-callback.js b/cli/tests/node_compat/test/parallel/test-net-socket-end-callback.js new file mode 100644 index 00000000000000..7b0e48df5b85bd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-end-callback.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); + +const server = net.createServer((socket) => { + socket.resume(); +}).unref(); + +server.listen(common.mustCall(() => { + const connect = (...args) => { + const socket = net.createConnection(server.address().port, () => { + socket.end(...args); + }); + }; + + const cb = common.mustCall(() => {}, 3); + + connect(cb); + connect('foo', cb); + connect('foo', 'utf8', cb); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-no-halfopen-enforcer.js b/cli/tests/node_compat/test/parallel/test-net-socket-no-halfopen-enforcer.js new file mode 100644 index 00000000000000..84c14ba71b5cd1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-no-halfopen-enforcer.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test ensures that `net.Socket` does not inherit the no-half-open +// enforcer from `stream.Duplex`. + +const { Socket } = require('net'); +const { strictEqual } = require('assert'); + +const socket = new Socket({ allowHalfOpen: false }); +strictEqual(socket.listenerCount('end'), 1); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-ready-without-cb.js b/cli/tests/node_compat/test/parallel/test-net-socket-ready-without-cb.js new file mode 100644 index 00000000000000..29cf729dfa4812 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-ready-without-cb.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// This test ensures that socket.connect can be called without callback +// which is optional. + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.end(); + server.close(); +})).listen(0, 'localhost', common.mustCall(function() { + const client = new net.Socket(); + + client.on('ready', common.mustCall(function() { + client.end(); + })); + + client.connect(server.address()); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-timeout.js b/cli/tests/node_compat/test/parallel/test-net-socket-timeout.js new file mode 100644 index 00000000000000..b69e4053088c2b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-timeout.js @@ -0,0 +1,88 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); +const { inspect } = require('util'); + +// Verify that invalid delays throw +const s = new net.Socket(); +const nonNumericDelays = [ + '100', true, false, undefined, null, '', {}, () => {}, [], +]; +const badRangeDelays = [-0.001, -1, -Infinity, Infinity, NaN]; +const validDelays = [0, 0.001, 1, 1e6]; +const invalidCallbacks = [ + 1, '100', true, false, null, {}, [], Symbol('test'), +]; + + +for (let i = 0; i < nonNumericDelays.length; i++) { + assert.throws(() => { + s.setTimeout(nonNumericDelays[i], () => {}); + }, { code: 'ERR_INVALID_ARG_TYPE' }, nonNumericDelays[i]); +} + +for (let i = 0; i < badRangeDelays.length; i++) { + assert.throws(() => { + s.setTimeout(badRangeDelays[i], () => {}); + }, { code: 'ERR_OUT_OF_RANGE' }, badRangeDelays[i]); +} + +for (let i = 0; i < validDelays.length; i++) { + s.setTimeout(validDelays[i], () => {}); +} + +for (let i = 0; i < invalidCallbacks.length; i++) { + [0, 1].forEach((mesc) => + assert.throws( + () => s.setTimeout(mesc, invalidCallbacks[i]), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ) + ); +} + +// TODO: support net.Server() without new + +const server = new net.Server(); +server.listen(0, common.mustCall(() => { + const socket = net.createConnection(server.address().port); + assert.strictEqual( + socket.setTimeout(1, common.mustCall(() => { + socket.destroy(); + assert.strictEqual(socket.setTimeout(1, common.mustNotCall()), socket); + server.close(); + })), + socket + ); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-write-after-close.js b/cli/tests/node_compat/test/parallel/test-net-socket-write-after-close.js new file mode 100644 index 00000000000000..abd30aee11687a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-write-after-close.js @@ -0,0 +1,49 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + const server = net.createServer(); + + server.listen(common.mustCall(() => { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(() => { + client.on('error', common.mustCall((err) => { + server.close(); + assert.strictEqual(err.constructor, Error); + assert.strictEqual(err.message, 'write EBADF'); + })); + client._handle.close(); + client.write('foo'); + })); + })); +} + +{ + const server = net.createServer(); + + server.listen(common.mustCall(() => { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(() => { + client.on('error', common.expectsError({ + code: 'ERR_SOCKET_CLOSED', + message: 'Socket is closed', + name: 'Error' + })); + + server.close(); + + client._handle.close(); + client._handle = null; + client.write('foo'); + })); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-socket-write-error.js b/cli/tests/node_compat/test/parallel/test-net-socket-write-error.js new file mode 100644 index 00000000000000..d38ab30b4046fe --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-socket-write-error.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer().listen(0, connectToServer); + +function connectToServer() { + const client = net.createConnection(this.address().port, () => { + client.on('error', common.mustNotCall()); + assert.throws(() => { + client.write(1337); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + + client.destroy(); + }) + .on('close', () => server.close()); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-sync-cork.js b/cli/tests/node_compat/test/parallel/test-net-sync-cork.js new file mode 100644 index 00000000000000..eb4274b7406d3f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-sync-cork.js @@ -0,0 +1,40 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(handle); + +const N = 100; +const buf = Buffer.alloc(2, 'a'); + +server.listen(0, function() { + const conn = net.connect(this.address().port); + + conn.on('connect', () => { + let res = true; + let i = 0; + for (; i < N && res; i++) { + conn.cork(); + conn.write(buf); + res = conn.write(buf); + conn.uncork(); + } + assert.strictEqual(i, N); + conn.end(); + }); +}); + +function handle(socket) { + socket.resume(); + socket.on('error', common.mustNotCall()) + .on('close', common.mustCall(() => server.close())); +} diff --git a/cli/tests/node_compat/test/parallel/test-net-timeout-no-handle.js b/cli/tests/node_compat/test/parallel/test-net-timeout-no-handle.js new file mode 100644 index 00000000000000..aa12da70fa2fb9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-timeout-no-handle.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const socket = new net.Socket(); +socket.setTimeout(common.platformTimeout(50)); + +socket.on('timeout', common.mustCall(() => { + assert.strictEqual(socket._handle, null); +})); + +socket.on('connect', common.mustNotCall()); + +// Since the timeout is unrefed, the code will exit without this +setTimeout(() => {}, common.platformTimeout(200)); diff --git a/cli/tests/node_compat/test/parallel/test-net-writable.js b/cli/tests/node_compat/test/parallel/test-net-writable.js new file mode 100644 index 00000000000000..a7cc5e5beedfd0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-writable.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall(function(s) { + server.close(); + s.end(); +})).listen(0, '127.0.0.1', common.mustCall(function() { + const socket = net.connect(this.address().port, '127.0.0.1'); + socket.on('end', common.mustCall(() => { + assert.strictEqual(socket.writable, true); + socket.write('hello world'); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-write-after-end-nt.js b/cli/tests/node_compat/test/parallel/test-net-write-after-end-nt.js new file mode 100644 index 00000000000000..8f38d3bfdadc74 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-write-after-end-nt.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const { expectsError, mustCall } = common; + +// This test ensures those errors caused by calling `net.Socket.write()` +// after sockets ending will be emitted in the next tick. +const server = net.createServer(mustCall((socket) => { + socket.end(); +})).listen(() => { + const client = net.connect(server.address().port, () => { + let hasError = false; + client.on('error', mustCall((err) => { + hasError = true; + server.close(); + })); + client.on('end', mustCall(() => { + const ret = client.write('hello', expectsError({ + code: 'EPIPE', + message: 'This socket has been ended by the other party', + name: 'Error' + })); + + assert.strictEqual(ret, false); + assert(!hasError, 'The error should be emitted in the next tick.'); + })); + client.end(); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-write-arguments.js b/cli/tests/node_compat/test/parallel/test-net-write-arguments.js new file mode 100644 index 00000000000000..d6beb72ee162bc --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-write-arguments.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); +const socket = new net.Stream({ highWaterMark: 0 }); + +// Make sure that anything besides a buffer or a string throws. +socket.on('error', common.mustNotCall()); +assert.throws(() => { + socket.write(null); +}, { + code: 'ERR_STREAM_NULL_VALUES', + name: 'TypeError', + message: 'May not write null values to stream' +}); + +[ + true, + false, + undefined, + 1, + 1.0, + +Infinity, + -Infinity, + [], + {}, +].forEach((value) => { + const socket = new net.Stream({ highWaterMark: 0 }); + // We need to check the callback since 'error' will only + // be emitted once per instance. + assert.throws(() => { + socket.write(value); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "chunk" argument must be of type string or an instance of ' + + `Buffer or Uint8Array.${common.invalidArgTypeHelper(value)}` + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-net-write-fully-async-buffer.js b/cli/tests/node_compat/test/parallel/test-net-write-fully-async-buffer.js new file mode 100644 index 00000000000000..5e93ad4e225079 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-write-fully-async-buffer.js @@ -0,0 +1,41 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Flags: --expose-gc + +// Note: This is a variant of test-net-write-fully-async-hex-string.js. +// This always worked, but it seemed appropriate to add a test that checks the +// behavior for Buffers, too. +const common = require('../common'); +const net = require('net'); + +const data = Buffer.alloc(1000000); + +const server = net.createServer(common.mustCall(function(conn) { + conn.resume(); +})).listen(0, common.mustCall(function() { + const conn = net.createConnection(this.address().port, common.mustCall(() => { + let count = 0; + + function writeLoop() { + if (count++ === 200) { + conn.destroy(); + server.close(); + return; + } + + while (conn.write(Buffer.from(data))); + global.gc({ type: 'minor' }); + // The buffer allocated above should still be alive. + } + + conn.on('drain', writeLoop); + + writeLoop(); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-write-fully-async-hex-string.js b/cli/tests/node_compat/test/parallel/test-net-write-fully-async-hex-string.js new file mode 100644 index 00000000000000..ff355aa2a7ba8b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-write-fully-async-hex-string.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Flags: --expose-gc + +// Regression test for https://github.com/nodejs/node/issues/8251. +const common = require('../common'); +const net = require('net'); + +const data = Buffer.alloc(1000000).toString('hex'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.resume(); +})).listen(0, common.mustCall(function() { + const conn = net.createConnection(this.address().port, common.mustCall(() => { + let count = 0; + + function writeLoop() { + if (count++ === 20) { + conn.destroy(); + server.close(); + return; + } + + while (conn.write(data, 'hex')); + global.gc({ type: 'minor' }); + // The buffer allocated inside the .write() call should still be alive. + } + + conn.on('drain', writeLoop); + + writeLoop(); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-net-write-slow.js b/cli/tests/node_compat/test/parallel/test-net-write-slow.js new file mode 100644 index 00000000000000..6c00efb1f4cbfb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-net-write-slow.js @@ -0,0 +1,70 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const SIZE = 2E5; +const N = 10; +let flushed = 0; +let received = 0; +const buf = Buffer.alloc(SIZE, 'a'); + +const server = net.createServer(function(socket) { + socket.setNoDelay(); + socket.setTimeout(9999); + socket.on('timeout', function() { + assert.fail(`flushed: ${flushed}, received: ${received}/${SIZE * N}`); + }); + + for (let i = 0; i < N; ++i) { + socket.write(buf, function() { + ++flushed; + if (flushed === N) { + socket.setTimeout(0); + } + }); + } + socket.end(); + +}).listen(0, common.mustCall(function() { + const conn = net.connect(this.address().port); + conn.on('data', function(buf) { + received += buf.length; + conn.pause(); + setTimeout(function() { + conn.resume(); + }, 20); + }); + conn.on('end', common.mustCall(function() { + server.close(); + assert.strictEqual(received, SIZE * N); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-next-tick-doesnt-hang.js b/cli/tests/node_compat/test/parallel/test-next-tick-doesnt-hang.js new file mode 100644 index 00000000000000..a614fe850272bf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-next-tick-doesnt-hang.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// This test verifies that having a single nextTick statement and nothing else +// does not hang the event loop. If this test times out it has failed. + +require('../common'); +process.nextTick(function() { + // Nothing +}); diff --git a/cli/tests/node_compat/test/parallel/test-next-tick-fixed-queue-regression.js b/cli/tests/node_compat/test/parallel/test-next-tick-fixed-queue-regression.js new file mode 100644 index 00000000000000..254a756eae35f6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-next-tick-fixed-queue-regression.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +// This tests a highly specific regression tied to the FixedQueue size, which +// was introduced in Node.js 9.7.0: https://github.com/nodejs/node/pull/18617 +// More specifically, a nextTick list could potentially end up not fully +// clearing in one run through if exactly 2048 ticks were added after +// microtasks were executed within the nextTick loop. + +process.nextTick(() => { + Promise.resolve(1).then(() => { + for (let i = 0; i < 2047; i++) + process.nextTick(common.mustCall()); + const immediate = setImmediate(common.mustNotCall()); + process.nextTick(common.mustCall(() => clearImmediate(immediate))); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-next-tick-intentional-starvation.js b/cli/tests/node_compat/test/parallel/test-next-tick-intentional-starvation.js new file mode 100644 index 00000000000000..e7ab4d44565217 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-next-tick-intentional-starvation.js @@ -0,0 +1,68 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This is the inverse of test-next-tick-starvation. it verifies +// that process.nextTick will *always* come before other events + +let ran = false; +let starved = false; +const start = +new Date(); +let timerRan = false; + +function spin() { + ran = true; + const now = +new Date(); + if (now - start > 100) { + console.log('The timer is starving, just as we planned.'); + starved = true; + + // now let it out. + return; + } + + process.nextTick(spin); +} + +function onTimeout() { + if (!starved) throw new Error('The timer escaped!'); + console.log('The timer ran once the ban was lifted'); + timerRan = true; +} + +spin(); +setTimeout(onTimeout, 50); + +process.on('exit', function() { + assert.ok(ran); + assert.ok(starved); + assert.ok(timerRan); +}); diff --git a/cli/tests/node_compat/test/parallel/test-next-tick-ordering.js b/cli/tests/node_compat/test/parallel/test-next-tick-ordering.js new file mode 100644 index 00000000000000..6108808d72b1e8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-next-tick-ordering.js @@ -0,0 +1,62 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +let i; + +const N = 30; +const done = []; + +function get_printer(timeout) { + return function() { + console.log(`Running from setTimeout ${timeout}`); + done.push(timeout); + }; +} + +process.nextTick(function() { + console.log('Running from nextTick'); + done.push('nextTick'); +}); + +for (i = 0; i < N; i += 1) { + setTimeout(get_printer(i), i); +} + +console.log('Running from main.'); + + +process.on('exit', function() { + assert.strictEqual(done[0], 'nextTick'); + // Disabling this test. I don't think we can ensure the order + // for (i = 0; i < N; i += 1) { + // assert.strictEqual(i, done[i + 1]); + // } +}); diff --git a/cli/tests/node_compat/test/parallel/test-next-tick-ordering2.js b/cli/tests/node_compat/test/parallel/test-next-tick-ordering2.js new file mode 100644 index 00000000000000..a1929efa49644d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-next-tick-ordering2.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const order = []; +process.nextTick(function() { + setTimeout(function() { + order.push('setTimeout'); + }, 0); + + process.nextTick(function() { + order.push('nextTick'); + }); +}); + +process.on('exit', function() { + assert.deepStrictEqual(order, ['nextTick', 'setTimeout']); +}); diff --git a/cli/tests/node_compat/test/parallel/test-next-tick-when-exiting.js b/cli/tests/node_compat/test/parallel/test-next-tick-when-exiting.js new file mode 100644 index 00000000000000..996c764626e15f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-next-tick-when-exiting.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +process.on('exit', () => { + assert.strictEqual(process._exiting, true); + + process.nextTick( + common.mustNotCall('process is exiting, should not be called') + ); +}); + +process.exit(); diff --git a/cli/tests/node_compat/test/parallel/test-next-tick.js b/cli/tests/node_compat/test/parallel/test-next-tick.js new file mode 100644 index 00000000000000..dc989fd7627a4d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-next-tick.js @@ -0,0 +1,70 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); + +process.nextTick(common.mustCall(function() { + process.nextTick(common.mustCall(function() { + process.nextTick(common.mustCall()); + })); +})); + +setTimeout(common.mustCall(function() { + process.nextTick(common.mustCall()); +}), 50); + +process.nextTick(common.mustCall()); + +const obj = {}; + +process.nextTick(function(a, b) { + assert.strictEqual(a, 42); + assert.strictEqual(b, obj); + assert.strictEqual(this, undefined); +}, 42, obj); + +process.nextTick((a, b) => { + assert.strictEqual(a, 42); + assert.strictEqual(b, obj); + assert.deepStrictEqual(this, {}); +}, 42, obj); + +process.nextTick(function() { + assert.strictEqual(this, undefined); +}, 1, 2, 3, 4); + +process.nextTick(() => { + assert.deepStrictEqual(this, {}); +}, 1, 2, 3, 4); + +process.on('exit', function() { + process.nextTick(common.mustNotCall()); +}); diff --git a/cli/tests/node_compat/test/parallel/test-nodeeventtarget.js b/cli/tests/node_compat/test/parallel/test-nodeeventtarget.js new file mode 100644 index 00000000000000..3bf102c5ce8be5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-nodeeventtarget.js @@ -0,0 +1,190 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +const { NodeEventTarget } = require('internal/event_target'); + +const { + deepStrictEqual, + ok, + strictEqual, + throws, +} = require('assert'); + +const { on } = require('events'); + +{ + const eventTarget = new NodeEventTarget(); + strictEqual(eventTarget.listenerCount('foo'), 0); + deepStrictEqual(eventTarget.eventNames(), []); + + const ev1 = common.mustCall(function(event) { + strictEqual(event.type, 'foo'); + strictEqual(this, eventTarget); + }, 2); + + const ev2 = { + handleEvent: common.mustCall(function(event) { + strictEqual(event.type, 'foo'); + strictEqual(this, ev2); + }) + }; + + eventTarget.addEventListener('foo', ev1); + eventTarget.addEventListener('foo', ev2, { once: true }); + strictEqual(eventTarget.listenerCount('foo'), 2); + ok(eventTarget.dispatchEvent(new Event('foo'))); + strictEqual(eventTarget.listenerCount('foo'), 1); + eventTarget.dispatchEvent(new Event('foo')); + + eventTarget.removeEventListener('foo', ev1); + strictEqual(eventTarget.listenerCount('foo'), 0); + eventTarget.dispatchEvent(new Event('foo')); +} + +{ + const eventTarget = new NodeEventTarget(); + strictEqual(eventTarget.listenerCount('foo'), 0); + deepStrictEqual(eventTarget.eventNames(), []); + + const ev1 = common.mustCall((event) => { + strictEqual(event.type, 'foo'); + }, 2); + + const ev2 = { + handleEvent: common.mustCall((event) => { + strictEqual(event.type, 'foo'); + }) + }; + + strictEqual(eventTarget.on('foo', ev1), eventTarget); + strictEqual(eventTarget.once('foo', ev2, { once: true }), eventTarget); + strictEqual(eventTarget.listenerCount('foo'), 2); + eventTarget.dispatchEvent(new Event('foo')); + strictEqual(eventTarget.listenerCount('foo'), 1); + eventTarget.dispatchEvent(new Event('foo')); + + strictEqual(eventTarget.off('foo', ev1), eventTarget); + strictEqual(eventTarget.listenerCount('foo'), 0); + eventTarget.dispatchEvent(new Event('foo')); +} + +{ + const eventTarget = new NodeEventTarget(); + strictEqual(eventTarget.listenerCount('foo'), 0); + deepStrictEqual(eventTarget.eventNames(), []); + + const ev1 = common.mustCall((event) => { + strictEqual(event.type, 'foo'); + }, 2); + + const ev2 = { + handleEvent: common.mustCall((event) => { + strictEqual(event.type, 'foo'); + }) + }; + + eventTarget.addListener('foo', ev1); + eventTarget.once('foo', ev2, { once: true }); + strictEqual(eventTarget.listenerCount('foo'), 2); + eventTarget.dispatchEvent(new Event('foo')); + strictEqual(eventTarget.listenerCount('foo'), 1); + eventTarget.dispatchEvent(new Event('foo')); + + eventTarget.removeListener('foo', ev1); + strictEqual(eventTarget.listenerCount('foo'), 0); + eventTarget.dispatchEvent(new Event('foo')); +} + +{ + const eventTarget = new NodeEventTarget(); + strictEqual(eventTarget.listenerCount('foo'), 0); + deepStrictEqual(eventTarget.eventNames(), []); + + // Won't actually be called. + const ev1 = () => {}; + + // Won't actually be called. + const ev2 = { handleEvent() {} }; + + eventTarget.addListener('foo', ev1); + eventTarget.addEventListener('foo', ev1); + eventTarget.once('foo', ev2, { once: true }); + eventTarget.once('foo', ev2, { once: false }); + eventTarget.on('bar', ev1); + strictEqual(eventTarget.listenerCount('foo'), 2); + strictEqual(eventTarget.listenerCount('bar'), 1); + deepStrictEqual(eventTarget.eventNames(), ['foo', 'bar']); + strictEqual(eventTarget.removeAllListeners('foo'), eventTarget); + strictEqual(eventTarget.listenerCount('foo'), 0); + strictEqual(eventTarget.listenerCount('bar'), 1); + deepStrictEqual(eventTarget.eventNames(), ['bar']); + strictEqual(eventTarget.removeAllListeners(), eventTarget); + strictEqual(eventTarget.listenerCount('foo'), 0); + strictEqual(eventTarget.listenerCount('bar'), 0); + deepStrictEqual(eventTarget.eventNames(), []); +} + +{ + const target = new NodeEventTarget(); + + process.on('warning', common.mustCall((warning) => { + ok(warning instanceof Error); + strictEqual(warning.name, 'MaxListenersExceededWarning'); + strictEqual(warning.target, target); + strictEqual(warning.count, 2); + strictEqual(warning.type, 'foo'); + ok(warning.message.includes( + '2 foo listeners added to NodeEventTarget')); + })); + + strictEqual(target.getMaxListeners(), NodeEventTarget.defaultMaxListeners); + target.setMaxListeners(1); + target.on('foo', () => {}); + target.on('foo', () => {}); +} +{ + // Test NodeEventTarget emit + const emitter = new NodeEventTarget(); + emitter.addEventListener('foo', common.mustCall((e) => { + strictEqual(e.type, 'foo'); + strictEqual(e.detail, 'bar'); + ok(e instanceof Event); + }), { once: true }); + emitter.once('foo', common.mustCall((e, droppedAdditionalArgument) => { + strictEqual(e, 'bar'); + strictEqual(droppedAdditionalArgument, undefined); + })); + emitter.emit('foo', 'bar', 'baz'); +} +{ + // Test NodeEventTarget emit unsupported usage + const emitter = new NodeEventTarget(); + throws(() => { + emitter.emit(); + }, /ERR_INVALID_ARG_TYPE/); +} + +(async () => { + // test NodeEventTarget async-iterability + const emitter = new NodeEventTarget(); + const interval = setInterval(() => { + emitter.dispatchEvent(new Event('foo')); + }, 0); + let count = 0; + for await (const [ item ] of on(emitter, 'foo')) { + count++; + strictEqual(item.type, 'foo'); + if (count > 5) { + break; + } + } + clearInterval(interval); +})().then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-os.js b/cli/tests/node_compat/test/parallel/test-os.js new file mode 100644 index 00000000000000..09d97222c58552 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-os.js @@ -0,0 +1,278 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const os = require('os'); +const path = require('path'); +const { inspect } = require('util'); + +const is = { + number: (value, key) => { + assert(!Number.isNaN(value), `${key} should not be NaN`); + assert.strictEqual(typeof value, 'number'); + }, + string: (value) => { assert.strictEqual(typeof value, 'string'); }, + array: (value) => { assert.ok(Array.isArray(value)); }, + object: (value) => { + assert.strictEqual(typeof value, 'object'); + assert.notStrictEqual(value, null); + } +}; + +/* TODO(kt3k): Enable this test +process.env.TMPDIR = '/tmpdir'; +process.env.TMP = '/tmp'; +process.env.TEMP = '/temp'; +if (common.isWindows) { + assert.strictEqual(os.tmpdir(), '/temp'); + process.env.TEMP = ''; + assert.strictEqual(os.tmpdir(), '/tmp'); + process.env.TMP = ''; + const expected = `${process.env.SystemRoot || process.env.windir}\\temp`; + assert.strictEqual(os.tmpdir(), expected); + process.env.TEMP = '\\temp\\'; + assert.strictEqual(os.tmpdir(), '\\temp'); + process.env.TEMP = '\\tmpdir/'; + assert.strictEqual(os.tmpdir(), '\\tmpdir/'); + process.env.TEMP = '\\'; + assert.strictEqual(os.tmpdir(), '\\'); + process.env.TEMP = 'C:\\'; + assert.strictEqual(os.tmpdir(), 'C:\\'); +} else { + assert.strictEqual(os.tmpdir(), '/tmpdir'); + process.env.TMPDIR = ''; + assert.strictEqual(os.tmpdir(), '/tmp'); + process.env.TMP = ''; + assert.strictEqual(os.tmpdir(), '/temp'); + process.env.TEMP = ''; + assert.strictEqual(os.tmpdir(), '/tmp'); + process.env.TMPDIR = '/tmpdir/'; + assert.strictEqual(os.tmpdir(), '/tmpdir'); + process.env.TMPDIR = '/tmpdir\\'; + assert.strictEqual(os.tmpdir(), '/tmpdir\\'); + process.env.TMPDIR = '/'; + assert.strictEqual(os.tmpdir(), '/'); +} +*/ + +const endianness = os.endianness(); +is.string(endianness); +assert.match(endianness, /[BL]E/); + +const hostname = os.hostname(); +is.string(hostname); +assert.ok(hostname.length > 0); + +// On IBMi, os.uptime() returns 'undefined' +if (!common.isIBMi) { + const uptime = os.uptime(); + is.number(uptime); + assert.ok(uptime > 0); +} + +const cpus = os.cpus(); +is.array(cpus); +assert.ok(cpus.length > 0); +for (const cpu of cpus) { + assert.strictEqual(typeof cpu.model, 'string'); + assert.strictEqual(typeof cpu.speed, 'number'); + assert.strictEqual(typeof cpu.times.user, 'number'); + assert.strictEqual(typeof cpu.times.nice, 'number'); + assert.strictEqual(typeof cpu.times.sys, 'number'); + assert.strictEqual(typeof cpu.times.idle, 'number'); + assert.strictEqual(typeof cpu.times.irq, 'number'); +} + +const type = os.type(); +is.string(type); +assert.ok(type.length > 0); + +const release = os.release(); +is.string(release); +assert.ok(release.length > 0); +// TODO: Check format on more than just AIX +if (common.isAIX) + assert.match(release, /^\d+\.\d+$/); + +const platform = os.platform(); +is.string(platform); +assert.ok(platform.length > 0); + +const arch = os.arch(); +is.string(arch); +assert.ok(arch.length > 0); + +if (!common.isSunOS) { + // not implemented yet + assert.ok(os.loadavg().length > 0); + assert.ok(os.freemem() > 0); + assert.ok(os.totalmem() > 0); +} + +const interfaces = os.networkInterfaces(); +switch (platform) { + case 'linux': { + const filter = (e) => + e.address === '127.0.0.1' && + e.netmask === '255.0.0.0'; + + const actual = interfaces.lo.filter(filter); + const expected = [{ + address: '127.0.0.1', + netmask: '255.0.0.0', + family: 'IPv4', + mac: '00:00:00:00:00:00', + internal: true, + cidr: '127.0.0.1/8' + }]; + assert.deepStrictEqual(actual, expected); + break; + } + case 'win32': { + const filter = (e) => + e.address === '127.0.0.1'; + + const actual = interfaces['Loopback Pseudo-Interface 1'].filter(filter); + const expected = [{ + address: '127.0.0.1', + netmask: '255.0.0.0', + family: 'IPv4', + mac: '00:00:00:00:00:00', + internal: true, + cidr: '127.0.0.1/8' + }]; + assert.deepStrictEqual(actual, expected); + break; + } +} +const netmaskToCIDRSuffixMap = new Map(Object.entries({ + '255.0.0.0': 8, + '255.255.255.0': 24, + 'ffff:ffff:ffff:ffff::': 64, + 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff': 128 +})); + +Object.values(interfaces) + .flat(Infinity) + .map((v) => ({ v, mask: netmaskToCIDRSuffixMap.get(v.netmask) })) + .forEach(({ v, mask }) => { + assert.ok('cidr' in v, `"cidr" prop not found in ${inspect(v)}`); + if (mask) { + assert.strictEqual(v.cidr, `${v.address}/${mask}`); + } + }); + +const EOL = os.EOL; +if (common.isWindows) { + assert.strictEqual(EOL, '\r\n'); +} else { + assert.strictEqual(EOL, '\n'); +} + +const home = os.homedir(); +is.string(home); +assert.ok(home.includes(path.sep)); + +const version = os.version(); +assert.strictEqual(typeof version, 'string'); +assert(version); + +if (common.isWindows && process.env.USERPROFILE) { + assert.strictEqual(home, process.env.USERPROFILE); + delete process.env.USERPROFILE; + assert.ok(os.homedir().includes(path.sep)); + process.env.USERPROFILE = home; +} else if (!common.isWindows && process.env.HOME) { + assert.strictEqual(home, process.env.HOME); + delete process.env.HOME; + assert.ok(os.homedir().includes(path.sep)); + process.env.HOME = home; +} + +/* TODO(kt3k): Enable this test +const pwd = os.userInfo(); +is.object(pwd); +const pwdBuf = os.userInfo({ encoding: 'buffer' }); + +if (common.isWindows) { + assert.strictEqual(pwd.uid, -1); + assert.strictEqual(pwd.gid, -1); + assert.strictEqual(pwd.shell, null); + assert.strictEqual(pwdBuf.uid, -1); + assert.strictEqual(pwdBuf.gid, -1); + assert.strictEqual(pwdBuf.shell, null); +} else { + is.number(pwd.uid); + is.number(pwd.gid); + assert.strictEqual(typeof pwd.shell, 'string'); + // It's possible for /etc/passwd to leave the user's shell blank. + if (pwd.shell.length > 0) { + assert(pwd.shell.includes(path.sep)); + } + assert.strictEqual(pwd.uid, pwdBuf.uid); + assert.strictEqual(pwd.gid, pwdBuf.gid); + assert.strictEqual(pwd.shell, pwdBuf.shell.toString('utf8')); +} + +is.string(pwd.username); +assert.ok(pwd.homedir.includes(path.sep)); +assert.strictEqual(pwd.username, pwdBuf.username.toString('utf8')); +assert.strictEqual(pwd.homedir, pwdBuf.homedir.toString('utf8')); +*/ + +assert.strictEqual(`${os.hostname}`, os.hostname()); +assert.strictEqual(`${os.homedir}`, os.homedir()); +assert.strictEqual(`${os.release}`, os.release()); +assert.strictEqual(`${os.type}`, os.type()); +assert.strictEqual(`${os.endianness}`, os.endianness()); +// TODO(kt3k): Enable this test +// assert.strictEqual(`${os.tmpdir}`, os.tmpdir()); +assert.strictEqual(`${os.arch}`, os.arch()); +assert.strictEqual(`${os.platform}`, os.platform()); +assert.strictEqual(`${os.version}`, os.version()); + +assert.strictEqual(+os.totalmem, os.totalmem()); + +// Assert that the following values are coercible to numbers. +// On IBMi, os.uptime() returns 'undefined' +if (!common.isIBMi) { + is.number(+os.uptime, 'uptime'); + is.number(os.uptime(), 'uptime'); +} + +is.number(+os.freemem, 'freemem'); +is.number(os.freemem(), 'freemem'); + +const devNull = os.devNull; +if (common.isWindows) { + assert.strictEqual(devNull, '\\\\.\\nul'); +} else { + assert.strictEqual(devNull, '/dev/null'); +} diff --git a/cli/tests/node_compat/test/parallel/test-outgoing-message-destroy.js b/cli/tests/node_compat/test/parallel/test-outgoing-message-destroy.js new file mode 100644 index 00000000000000..1302fb03d9a22c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-outgoing-message-destroy.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Test that http.OutgoingMessage,prototype.destroy() returns `this`. +require('../common'); + +const assert = require('assert'); +const http = require('http'); +const outgoingMessage = new http.OutgoingMessage(); + +assert.strictEqual(outgoingMessage.destroyed, false); +assert.strictEqual(outgoingMessage.destroy(), outgoingMessage); +assert.strictEqual(outgoingMessage.destroyed, true); +assert.strictEqual(outgoingMessage.destroy(), outgoingMessage); diff --git a/cli/tests/node_compat/test/parallel/test-outgoing-message-pipe.js b/cli/tests/node_compat/test/parallel/test-outgoing-message-pipe.js new file mode 100644 index 00000000000000..1b40080a859bd4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-outgoing-message-pipe.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const OutgoingMessage = require('_http_outgoing').OutgoingMessage; + +// Verify that an error is thrown upon a call to `OutgoingMessage.pipe`. + +const outgoingMessage = new OutgoingMessage(); +assert.throws( + () => { outgoingMessage.pipe(outgoingMessage); }, + { + code: 'ERR_STREAM_CANNOT_PIPE', + name: 'Error' + } +); diff --git a/cli/tests/node_compat/test/parallel/test-path-basename.js b/cli/tests/node_compat/test/parallel/test-path-basename.js new file mode 100644 index 00000000000000..dec890c8556d2e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-basename.js @@ -0,0 +1,83 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.basename(__filename), 'test-path-basename.js'); +assert.strictEqual(path.basename(__filename, '.js'), 'test-path-basename'); +assert.strictEqual(path.basename('.js', '.js'), ''); +assert.strictEqual(path.basename('js', '.js'), 'js'); +assert.strictEqual(path.basename('file.js', '.ts'), 'file.js'); +assert.strictEqual(path.basename('file', '.js'), 'file'); +assert.strictEqual(path.basename('file.js.old', '.js.old'), 'file'); +assert.strictEqual(path.basename(''), ''); +assert.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('/basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext/'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext//'), 'basename.ext'); +assert.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'bb'), 'b'); +assert.strictEqual(path.basename('aaa/bbb', 'b'), 'bb'); +assert.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b'); +assert.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb'); +assert.strictEqual(path.basename('/aaa/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/'), 'aaa'); +assert.strictEqual(path.basename('/aaa/b'), 'b'); +assert.strictEqual(path.basename('/a/b'), 'b'); +assert.strictEqual(path.basename('//a'), 'a'); +assert.strictEqual(path.basename('a', 'a'), ''); + +// On Windows a backslash acts as a path separator. +assert.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('foo'), 'foo'); +assert.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb'); +assert.strictEqual(path.win32.basename('C:'), ''); +assert.strictEqual(path.win32.basename('C:.'), '.'); +assert.strictEqual(path.win32.basename('C:\\'), ''); +assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext'); +assert.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:foo'), 'foo'); +assert.strictEqual(path.win32.basename('file:stream'), 'file:stream'); +assert.strictEqual(path.win32.basename('a', 'a'), ''); + +// On unix a backslash is just treated as any other character. +assert.strictEqual(path.posix.basename('\\dir\\basename.ext'), + '\\dir\\basename.ext'); +assert.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext'); +assert.strictEqual(path.posix.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\'); +assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\'); +assert.strictEqual(path.posix.basename('foo'), 'foo'); + +// POSIX filenames may include control characters +// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html +const controlCharFilename = `Icon${String.fromCharCode(13)}`; +assert.strictEqual(path.posix.basename(`/a/b/${controlCharFilename}`), + controlCharFilename); diff --git a/cli/tests/node_compat/test/parallel/test-path-dirname.js b/cli/tests/node_compat/test/parallel/test-path-dirname.js new file mode 100644 index 00000000000000..7043bfa0555627 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-dirname.js @@ -0,0 +1,66 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.dirname(__filename).substr(-13), + common.isWindows ? 'test\\parallel' : 'test/parallel'); + +assert.strictEqual(path.posix.dirname('/a/b/'), '/a'); +assert.strictEqual(path.posix.dirname('/a/b'), '/a'); +assert.strictEqual(path.posix.dirname('/a'), '/'); +assert.strictEqual(path.posix.dirname(''), '.'); +assert.strictEqual(path.posix.dirname('/'), '/'); +assert.strictEqual(path.posix.dirname('////'), '/'); +assert.strictEqual(path.posix.dirname('//a'), '//'); +assert.strictEqual(path.posix.dirname('foo'), '.'); + +assert.strictEqual(path.win32.dirname('c:\\'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar'); +assert.strictEqual(path.win32.dirname('c:\\foo bar\\baz'), 'c:\\foo bar'); +assert.strictEqual(path.win32.dirname('\\'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo\\'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo'); +assert.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo'); +assert.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar'); +assert.strictEqual(path.win32.dirname('\\foo bar\\baz'), '\\foo bar'); +assert.strictEqual(path.win32.dirname('c:'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo'); +assert.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo'); +assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar'); +assert.strictEqual(path.win32.dirname('c:foo bar\\baz'), 'c:foo bar'); +assert.strictEqual(path.win32.dirname('file:stream'), '.'); +assert.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share'), + '\\\\unc\\share'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'), + '\\\\unc\\share\\'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'), + '\\\\unc\\share\\'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'), + '\\\\unc\\share\\foo'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'), + '\\\\unc\\share\\foo'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'), + '\\\\unc\\share\\foo\\bar'); +assert.strictEqual(path.win32.dirname('/a/b/'), '/a'); +assert.strictEqual(path.win32.dirname('/a/b'), '/a'); +assert.strictEqual(path.win32.dirname('/a'), '/'); +assert.strictEqual(path.win32.dirname(''), '.'); +assert.strictEqual(path.win32.dirname('/'), '/'); +assert.strictEqual(path.win32.dirname('////'), '/'); +assert.strictEqual(path.win32.dirname('foo'), '.'); diff --git a/cli/tests/node_compat/test/parallel/test-path-extname.js b/cli/tests/node_compat/test/parallel/test-path-extname.js new file mode 100644 index 00000000000000..d0e00adccfc8ff --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-extname.js @@ -0,0 +1,106 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; +const slashRE = /\//g; + +[ + [__filename, '.js'], + ['', ''], + ['/path/to/file', ''], + ['/path/to/file.ext', '.ext'], + ['/path.to/file.ext', '.ext'], + ['/path.to/file', ''], + ['/path.to/.file', ''], + ['/path.to/.file.ext', '.ext'], + ['/path/to/f.ext', '.ext'], + ['/path/to/..ext', '.ext'], + ['/path/to/..', ''], + ['file', ''], + ['file.ext', '.ext'], + ['.file', ''], + ['.file.ext', '.ext'], + ['/file', ''], + ['/file.ext', '.ext'], + ['/.file', ''], + ['/.file.ext', '.ext'], + ['.path/file.ext', '.ext'], + ['file.ext.ext', '.ext'], + ['file.', '.'], + ['.', ''], + ['./', ''], + ['.file.ext', '.ext'], + ['.file', ''], + ['.file.', '.'], + ['.file..', '.'], + ['..', ''], + ['../', ''], + ['..file.ext', '.ext'], + ['..file', '.file'], + ['..file.', '.'], + ['..file..', '.'], + ['...', '.'], + ['...ext', '.ext'], + ['....', '.'], + ['file.ext/', '.ext'], + ['file.ext//', '.ext'], + ['file/', ''], + ['file//', ''], + ['file./', '.'], + ['file.//', '.'], +].forEach((test) => { + const expected = test[1]; + [path.posix.extname, path.win32.extname].forEach((extname) => { + let input = test[0]; + let os; + if (extname === path.win32.extname) { + input = input.replace(slashRE, '\\'); + os = 'win32'; + } else { + os = 'posix'; + } + const actual = extname(input); + const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) + failures.push(`\n${message}`); + }); + { + const input = `C:${test[0].replace(slashRE, '\\')}`; + const actual = path.win32.extname(input); + const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) + failures.push(`\n${message}`); + } +}); +assert.strictEqual(failures.length, 0, failures.join('')); + +// On Windows, backslash is a path separator. +assert.strictEqual(path.win32.extname('.\\'), ''); +assert.strictEqual(path.win32.extname('..\\'), ''); +assert.strictEqual(path.win32.extname('file.ext\\'), '.ext'); +assert.strictEqual(path.win32.extname('file.ext\\\\'), '.ext'); +assert.strictEqual(path.win32.extname('file\\'), ''); +assert.strictEqual(path.win32.extname('file\\\\'), ''); +assert.strictEqual(path.win32.extname('file.\\'), '.'); +assert.strictEqual(path.win32.extname('file.\\\\'), '.'); + +// On *nix, backslash is a valid name component like any other character. +assert.strictEqual(path.posix.extname('.\\'), ''); +assert.strictEqual(path.posix.extname('..\\'), '.\\'); +assert.strictEqual(path.posix.extname('file.ext\\'), '.ext\\'); +assert.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\'); +assert.strictEqual(path.posix.extname('file\\'), ''); +assert.strictEqual(path.posix.extname('file\\\\'), ''); +assert.strictEqual(path.posix.extname('file.\\'), '.\\'); +assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\'); diff --git a/cli/tests/node_compat/test/parallel/test-path-isabsolute.js b/cli/tests/node_compat/test/parallel/test-path-isabsolute.js new file mode 100644 index 00000000000000..a55dca7a832075 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-isabsolute.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.win32.isAbsolute('/'), true); +assert.strictEqual(path.win32.isAbsolute('//'), true); +assert.strictEqual(path.win32.isAbsolute('//server'), true); +assert.strictEqual(path.win32.isAbsolute('//server/file'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\server'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\'), true); +assert.strictEqual(path.win32.isAbsolute('c'), false); +assert.strictEqual(path.win32.isAbsolute('c:'), false); +assert.strictEqual(path.win32.isAbsolute('c:\\'), true); +assert.strictEqual(path.win32.isAbsolute('c:/'), true); +assert.strictEqual(path.win32.isAbsolute('c://'), true); +assert.strictEqual(path.win32.isAbsolute('C:/Users/'), true); +assert.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true); +assert.strictEqual(path.win32.isAbsolute('C:cwd/another'), false); +assert.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false); +assert.strictEqual(path.win32.isAbsolute('directory/directory'), false); +assert.strictEqual(path.win32.isAbsolute('directory\\directory'), false); + +assert.strictEqual(path.posix.isAbsolute('/home/foo'), true); +assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true); +assert.strictEqual(path.posix.isAbsolute('bar/'), false); +assert.strictEqual(path.posix.isAbsolute('./baz'), false); diff --git a/cli/tests/node_compat/test/parallel/test-path-join.js b/cli/tests/node_compat/test/parallel/test-path-join.js new file mode 100644 index 00000000000000..482686538eec82 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-join.js @@ -0,0 +1,150 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; +const backslashRE = /\\/g; + +const joinTests = [ + [ [path.posix.join, path.win32.join], + // Arguments result + [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], + [[], '.'], + [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], + [['/foo', '../../../bar'], '/bar'], + [['foo', '../../../bar'], '../../bar'], + [['foo/', '../../../bar'], '../../bar'], + [['foo/x', '../../../bar'], '../bar'], + [['foo/x', './bar'], 'foo/x/bar'], + [['foo/x/', './bar'], 'foo/x/bar'], + [['foo/x/', '.', 'bar'], 'foo/x/bar'], + [['./'], './'], + [['.', './'], './'], + [['.', '.', '.'], '.'], + [['.', './', '.'], '.'], + [['.', '/./', '.'], '.'], + [['.', '/////./', '.'], '.'], + [['.'], '.'], + [['', '.'], '.'], + [['', 'foo'], 'foo'], + [['foo', '/bar'], 'foo/bar'], + [['', '/foo'], '/foo'], + [['', '', '/foo'], '/foo'], + [['', '', 'foo'], 'foo'], + [['foo', ''], 'foo'], + [['foo/', ''], 'foo/'], + [['foo', '', '/bar'], 'foo/bar'], + [['./', '..', '/foo'], '../foo'], + [['./', '..', '..', '/foo'], '../../foo'], + [['.', '..', '..', '/foo'], '../../foo'], + [['', '..', '..', '/foo'], '../../foo'], + [['/'], '/'], + [['/', '.'], '/'], + [['/', '..'], '/'], + [['/', '..', '..'], '/'], + [[''], '.'], + [['', ''], '.'], + [[' /foo'], ' /foo'], + [[' ', 'foo'], ' /foo'], + [[' ', '.'], ' '], + [[' ', '/'], ' /'], + [[' ', ''], ' '], + [['/', 'foo'], '/foo'], + [['/', '/foo'], '/foo'], + [['/', '//foo'], '/foo'], + [['/', '', '/foo'], '/foo'], + [['', '/', 'foo'], '/foo'], + [['', '/', '/foo'], '/foo'], + ], + ], +]; + +// Windows-specific join tests +joinTests.push([ + path.win32.join, + joinTests[0][1].slice(0).concat( + [// Arguments result + // UNC path expected + [['//foo/bar'], '\\\\foo\\bar\\'], + [['\\/foo/bar'], '\\\\foo\\bar\\'], + [['\\\\foo/bar'], '\\\\foo\\bar\\'], + // UNC path expected - server and share separate + [['//foo', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', 'bar'], '\\\\foo\\bar\\'], + [['//foo', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - questionable + [['//foo', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - even more questionable + [['', '//foo', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', '/bar'], '\\\\foo\\bar\\'], + // No UNC path expected (no double slash in first component) + [['\\', 'foo/bar'], '\\foo\\bar'], + [['\\', '/foo/bar'], '\\foo\\bar'], + [['', '/', '/foo/bar'], '\\foo\\bar'], + // No UNC path expected (no non-slashes in first component - + // questionable) + [['//', 'foo/bar'], '\\foo\\bar'], + [['//', '/foo/bar'], '\\foo\\bar'], + [['\\\\', '/', '/foo/bar'], '\\foo\\bar'], + [['//'], '\\'], + // No UNC path expected (share name missing - questionable). + [['//foo'], '\\foo'], + [['//foo/'], '\\foo\\'], + [['//foo', '/'], '\\foo\\'], + [['//foo', '', '/'], '\\foo\\'], + // No UNC path expected (too many leading slashes - questionable) + [['///foo/bar'], '\\foo\\bar'], + [['////foo', 'bar'], '\\foo\\bar'], + [['\\\\\\/foo/bar'], '\\foo\\bar'], + // Drive-relative vs drive-absolute paths. This merely describes the + // status quo, rather than being obviously right + [['c:'], 'c:.'], + [['c:.'], 'c:.'], + [['c:', ''], 'c:.'], + [['', 'c:'], 'c:.'], + [['c:.', '/'], 'c:.\\'], + [['c:.', 'file'], 'c:file'], + [['c:', '/'], 'c:\\'], + [['c:', 'file'], 'c:\\file'], + ] + ), +]); +joinTests.forEach((test) => { + if (!Array.isArray(test[0])) + test[0] = [test[0]]; + test[0].forEach((join) => { + test[1].forEach((test) => { + const actual = join.apply(null, test[0]); + const expected = test[1]; + // For non-Windows specific tests with the Windows join(), we need to try + // replacing the slashes since the non-Windows specific tests' `expected` + // use forward slashes + let actualAlt; + let os; + if (join === path.win32.join) { + actualAlt = actual.replace(backslashRE, '/'); + os = 'win32'; + } else { + os = 'posix'; + } + if (actual !== expected && actualAlt !== expected) { + const delimiter = test[0].map(JSON.stringify).join(','); + const message = `path.${os}.join(${delimiter})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + failures.push(`\n${message}`); + } + }); + }); +}); +assert.strictEqual(failures.length, 0, failures.join('')); diff --git a/cli/tests/node_compat/test/parallel/test-path-makelong.js b/cli/tests/node_compat/test/parallel/test-path-makelong.js new file mode 100644 index 00000000000000..be46b2cb70ba1e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-makelong.js @@ -0,0 +1,94 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const path = require('path'); + +if (common.isWindows) { + const file = fixtures.path('a.js'); + const resolvedFile = path.resolve(file); + + assert.strictEqual(path.toNamespacedPath(file), + `\\\\?\\${resolvedFile}`); + assert.strictEqual(path.toNamespacedPath(`\\\\?\\${file}`), + `\\\\?\\${resolvedFile}`); + assert.strictEqual(path.toNamespacedPath( + '\\\\someserver\\someshare\\somefile'), + '\\\\?\\UNC\\someserver\\someshare\\somefile'); + assert.strictEqual(path.toNamespacedPath( + '\\\\?\\UNC\\someserver\\someshare\\somefile'), + '\\\\?\\UNC\\someserver\\someshare\\somefile'); + assert.strictEqual(path.toNamespacedPath('\\\\.\\pipe\\somepipe'), + '\\\\.\\pipe\\somepipe'); +} + +assert.strictEqual(path.toNamespacedPath(''), ''); +assert.strictEqual(path.toNamespacedPath(null), null); +assert.strictEqual(path.toNamespacedPath(100), 100); +assert.strictEqual(path.toNamespacedPath(path), path); +assert.strictEqual(path.toNamespacedPath(false), false); +assert.strictEqual(path.toNamespacedPath(true), true); + +const emptyObj = {}; +assert.strictEqual(path.posix.toNamespacedPath('/foo/bar'), '/foo/bar'); +assert.strictEqual(path.posix.toNamespacedPath('foo/bar'), 'foo/bar'); +assert.strictEqual(path.posix.toNamespacedPath(null), null); +assert.strictEqual(path.posix.toNamespacedPath(true), true); +assert.strictEqual(path.posix.toNamespacedPath(1), 1); +assert.strictEqual(path.posix.toNamespacedPath(), undefined); +assert.strictEqual(path.posix.toNamespacedPath(emptyObj), emptyObj); +if (common.isWindows) { + // These tests cause resolve() to insert the cwd, so we cannot test them from + // non-Windows platforms (easily) + assert.strictEqual(path.toNamespacedPath(''), ''); + assert.strictEqual(path.win32.toNamespacedPath('foo\\bar').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`); + assert.strictEqual(path.win32.toNamespacedPath('foo/bar').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`); + const currentDeviceLetter = path.parse(process.cwd()).root.substring(0, 2); + assert.strictEqual( + path.win32.toNamespacedPath(currentDeviceLetter).toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}`); + assert.strictEqual(path.win32.toNamespacedPath('C').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\c`); +} +assert.strictEqual(path.win32.toNamespacedPath('C:\\foo'), '\\\\?\\C:\\foo'); +assert.strictEqual(path.win32.toNamespacedPath('C:/foo'), '\\\\?\\C:\\foo'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\foo\\bar'), + '\\\\?\\UNC\\foo\\bar\\'); +assert.strictEqual(path.win32.toNamespacedPath('//foo//bar'), + '\\\\?\\UNC\\foo\\bar\\'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\?\\foo'), '\\\\?\\foo'); +assert.strictEqual(path.win32.toNamespacedPath(null), null); +assert.strictEqual(path.win32.toNamespacedPath(true), true); +assert.strictEqual(path.win32.toNamespacedPath(1), 1); +assert.strictEqual(path.win32.toNamespacedPath(), undefined); +assert.strictEqual(path.win32.toNamespacedPath(emptyObj), emptyObj); diff --git a/cli/tests/node_compat/test/parallel/test-path-normalize.js b/cli/tests/node_compat/test/parallel/test-path-normalize.js new file mode 100644 index 00000000000000..ec5e4f194542ae --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-normalize.js @@ -0,0 +1,79 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.win32.normalize('./fixtures///b/../b/c.js'), + 'fixtures\\b\\c.js'); +assert.strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar'); +assert.strictEqual(path.win32.normalize('a//b//../b'), 'a\\b'); +assert.strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c'); +assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b'); +assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'), + '\\\\server\\share\\dir\\file.ext'); +assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z'); +assert.strictEqual(path.win32.normalize('C:'), 'C:.'); +assert.strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc'); +assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'), + 'C:..\\..\\def'); +assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\'); +assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..'); +assert.strictEqual(path.win32.normalize('..\\foo..\\..\\..\\bar'), + '..\\..\\bar'); +assert.strictEqual(path.win32.normalize('..\\...\\..\\.\\...\\..\\..\\bar'), + '..\\..\\bar'); +assert.strictEqual(path.win32.normalize('../../../foo/../../../bar'), + '..\\..\\..\\..\\..\\bar'); +assert.strictEqual(path.win32.normalize('../../../foo/../../../bar/../../'), + '..\\..\\..\\..\\..\\..\\'); +assert.strictEqual( + path.win32.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '..\\..\\' +); +assert.strictEqual( + path.win32.normalize('../.../../foobar/../../../bar/../../baz'), + '..\\..\\..\\..\\baz' +); +assert.strictEqual(path.win32.normalize('foo/bar\\baz'), 'foo\\bar\\baz'); + +assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), + 'fixtures/b/c.js'); +assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar'); +assert.strictEqual(path.posix.normalize('a//b//../b'), 'a/b'); +assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c'); +assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b'); +assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z'); +assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../'), 'bar/'); +assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz'); +assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../'); +assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..'); +assert.strictEqual(path.posix.normalize('../foo../../../bar'), '../../bar'); +assert.strictEqual(path.posix.normalize('../.../.././.../../../bar'), + '../../bar'); +assert.strictEqual(path.posix.normalize('../../../foo/../../../bar'), + '../../../../../bar'); +assert.strictEqual(path.posix.normalize('../../../foo/../../../bar/../../'), + '../../../../../../'); +assert.strictEqual( + path.posix.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '../../' +); +assert.strictEqual( + path.posix.normalize('../.../../foobar/../../../bar/../../baz'), + '../../../../baz' +); +assert.strictEqual(path.posix.normalize('foo/bar\\baz'), 'foo/bar\\baz'); diff --git a/cli/tests/node_compat/test/parallel/test-path-parse-format.js b/cli/tests/node_compat/test/parallel/test-path-parse-format.js new file mode 100644 index 00000000000000..a6fa63d7573e1a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-parse-format.js @@ -0,0 +1,233 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +const winPaths = [ + // [path, root] + ['C:\\path\\dir\\index.html', 'C:\\'], + ['C:\\another_path\\DIR\\1\\2\\33\\\\index', 'C:\\'], + ['another_path\\DIR with spaces\\1\\2\\33\\index', ''], + ['\\', '\\'], + ['\\foo\\C:', '\\'], + ['file', ''], + ['file:stream', ''], + ['.\\file', ''], + ['C:', 'C:'], + ['C:.', 'C:'], + ['C:..', 'C:'], + ['C:abc', 'C:'], + ['C:\\', 'C:\\'], + ['C:\\abc', 'C:\\' ], + ['', ''], + + // unc + ['\\\\server\\share\\file_path', '\\\\server\\share\\'], + ['\\\\server two\\shared folder\\file path.zip', + '\\\\server two\\shared folder\\'], + ['\\\\teela\\admin$\\system32', '\\\\teela\\admin$\\'], + ['\\\\?\\UNC\\server\\share', '\\\\?\\UNC\\'], +]; + +const winSpecialCaseParseTests = [ + ['t', { base: 't', name: 't', root: '', dir: '', ext: '' }], + ['/foo/bar', { root: '/', dir: '/foo', base: 'bar', ext: '', name: 'bar' }], +]; + +const winSpecialCaseFormatTests = [ + [{ dir: 'some\\dir' }, 'some\\dir\\'], + [{ base: 'index.html' }, 'index.html'], + [{ root: 'C:\\' }, 'C:\\'], + [{ name: 'index', ext: '.html' }, 'index.html'], + [{ dir: 'some\\dir', name: 'index', ext: '.html' }, 'some\\dir\\index.html'], + [{ root: 'C:\\', name: 'index', ext: '.html' }, 'C:\\index.html'], + [{}, ''], +]; + +const unixPaths = [ + // [path, root] + ['/home/user/dir/file.txt', '/'], + ['/home/user/a dir/another File.zip', '/'], + ['/home/user/a dir//another&File.', '/'], + ['/home/user/a$$$dir//another File.zip', '/'], + ['user/dir/another File.zip', ''], + ['file', ''], + ['.\\file', ''], + ['./file', ''], + ['C:\\foo', ''], + ['/', '/'], + ['', ''], + ['.', ''], + ['..', ''], + ['/foo', '/'], + ['/foo.', '/'], + ['/foo.bar', '/'], + ['/.', '/'], + ['/.foo', '/'], + ['/.foo.bar', '/'], + ['/foo/bar.baz', '/'], +]; + +const unixSpecialCaseFormatTests = [ + [{ dir: 'some/dir' }, 'some/dir/'], + [{ base: 'index.html' }, 'index.html'], + [{ root: '/' }, '/'], + [{ name: 'index', ext: '.html' }, 'index.html'], + [{ dir: 'some/dir', name: 'index', ext: '.html' }, 'some/dir/index.html'], + [{ root: '/', name: 'index', ext: '.html' }, '/index.html'], + [{}, ''], +]; + +const errors = [ + { method: 'parse', input: [null] }, + { method: 'parse', input: [{}] }, + { method: 'parse', input: [true] }, + { method: 'parse', input: [1] }, + { method: 'parse', input: [] }, + { method: 'format', input: [null] }, + { method: 'format', input: [''] }, + { method: 'format', input: [true] }, + { method: 'format', input: [1] }, +]; + +checkParseFormat(path.win32, winPaths); +checkParseFormat(path.posix, unixPaths); +checkSpecialCaseParseFormat(path.win32, winSpecialCaseParseTests); +checkErrors(path.win32); +checkErrors(path.posix); +checkFormat(path.win32, winSpecialCaseFormatTests); +checkFormat(path.posix, unixSpecialCaseFormatTests); + +// Test removal of trailing path separators +const trailingTests = [ + [ path.win32.parse, + [['.\\', { root: '', dir: '', base: '.', ext: '', name: '.' }], + ['\\\\', { root: '\\', dir: '\\', base: '', ext: '', name: '' }], + ['\\\\', { root: '\\', dir: '\\', base: '', ext: '', name: '' }], + ['c:\\foo\\\\\\', + { root: 'c:\\', dir: 'c:\\', base: 'foo', ext: '', name: 'foo' }], + ['D:\\foo\\\\\\bar.baz', + { root: 'D:\\', + dir: 'D:\\foo\\\\', + base: 'bar.baz', + ext: '.baz', + name: 'bar' }, + ], + ], + ], + [ path.posix.parse, + [['./', { root: '', dir: '', base: '.', ext: '', name: '.' }], + ['//', { root: '/', dir: '/', base: '', ext: '', name: '' }], + ['///', { root: '/', dir: '/', base: '', ext: '', name: '' }], + ['/foo///', { root: '/', dir: '/', base: 'foo', ext: '', name: 'foo' }], + ['/foo///bar.baz', + { root: '/', dir: '/foo//', base: 'bar.baz', ext: '.baz', name: 'bar' }, + ], + ], + ], +]; +const failures = []; +trailingTests.forEach((test) => { + const parse = test[0]; + const os = parse === path.win32.parse ? 'win32' : 'posix'; + test[1].forEach((test) => { + const actual = parse(test[0]); + const expected = test[1]; + const message = `path.${os}.parse(${JSON.stringify(test[0])})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + const actualKeys = Object.keys(actual); + const expectedKeys = Object.keys(expected); + let failed = (actualKeys.length !== expectedKeys.length); + if (!failed) { + for (let i = 0; i < actualKeys.length; ++i) { + const key = actualKeys[i]; + if (!expectedKeys.includes(key) || actual[key] !== expected[key]) { + failed = true; + break; + } + } + } + if (failed) + failures.push(`\n${message}`); + }); +}); +assert.strictEqual(failures.length, 0, failures.join('')); + +function checkErrors(path) { + errors.forEach(({ method, input }) => { + assert.throws(() => { + path[method].apply(path, input); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); +} + +function checkParseFormat(path, paths) { + paths.forEach(([element, root]) => { + const output = path.parse(element); + assert.strictEqual(typeof output.root, 'string'); + assert.strictEqual(typeof output.dir, 'string'); + assert.strictEqual(typeof output.base, 'string'); + assert.strictEqual(typeof output.ext, 'string'); + assert.strictEqual(typeof output.name, 'string'); + assert.strictEqual(path.format(output), element); + assert.strictEqual(output.root, root); + assert(output.dir.startsWith(output.root)); + assert.strictEqual(output.dir, output.dir ? path.dirname(element) : ''); + assert.strictEqual(output.base, path.basename(element)); + assert.strictEqual(output.ext, path.extname(element)); + }); +} + +function checkSpecialCaseParseFormat(path, testCases) { + testCases.forEach(([element, expect]) => { + assert.deepStrictEqual(path.parse(element), expect); + }); +} + +function checkFormat(path, testCases) { + testCases.forEach(([element, expect]) => { + assert.strictEqual(path.format(element), expect); + }); + + [null, undefined, 1, true, false, 'string'].forEach((pathObject) => { + assert.throws(() => { + path.format(pathObject); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "pathObject" argument must be of type object.' + + common.invalidArgTypeHelper(pathObject) + }); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-path-posix-exists.js b/cli/tests/node_compat/test/parallel/test-path-posix-exists.js new file mode 100644 index 00000000000000..dacac0443abcec --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-posix-exists.js @@ -0,0 +1,13 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('path/posix'), require('path').posix); diff --git a/cli/tests/node_compat/test/parallel/test-path-relative.js b/cli/tests/node_compat/test/parallel/test-path-relative.js new file mode 100644 index 00000000000000..ae37c6b9230130 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-relative.js @@ -0,0 +1,76 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; + +const relativeTests = [ + [ path.win32.relative, + // Arguments result + [['c:/blah\\blah', 'd:/games', 'd:\\games'], + ['c:/aaaa/bbbb', 'c:/aaaa', '..'], + ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], + ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'], + ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], + ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], + ['c:/aaaa/bbbb', 'd:\\', 'd:\\'], + ['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'], + ['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'], + ['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'], + ['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'], + ['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'], + ['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['C:\\baz-quux', 'C:\\baz', '..\\baz'], + ['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'], + ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'], + ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'], + ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz'], + ], + ], + [ path.posix.relative, + // Arguments result + [['/var/lib', '/var', '..'], + ['/var/lib', '/bin', '../../bin'], + ['/var/lib', '/var/lib', ''], + ['/var/lib', '/var/apache', '../apache'], + ['/var/', '/var/lib', 'lib'], + ['/', '/var/lib', 'var/lib'], + ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'], + ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'], + ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'], + ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'], + ['/baz-quux', '/baz', '../baz'], + ['/baz', '/baz-quux', '../baz-quux'], + ['/page1/page2/foo', '/', '../../..'], + ], + ], +]; +relativeTests.forEach((test) => { + const relative = test[0]; + test[1].forEach((test) => { + const actual = relative(test[0], test[1]); + const expected = test[2]; + if (actual !== expected) { + const os = relative === path.win32.relative ? 'win32' : 'posix'; + const message = `path.${os}.relative(${ + test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + failures.push(`\n${message}`); + } + }); +}); +assert.strictEqual(failures.length, 0, failures.join('')); diff --git a/cli/tests/node_compat/test/parallel/test-path-resolve.js b/cli/tests/node_compat/test/parallel/test-path-resolve.js new file mode 100644 index 00000000000000..be010ed83ef007 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-resolve.js @@ -0,0 +1,96 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const child = require('child_process'); +const path = require('path'); + +const failures = []; +const slashRE = /\//g; +const backslashRE = /\\/g; + +const posixyCwd = common.isWindows ? + (() => { + const _ = process.cwd() + .replaceAll(path.sep, path.posix.sep); + return _.slice(_.indexOf(path.posix.sep)); + })() : + process.cwd(); + +const resolveTests = [ + [ path.win32.resolve, + // Arguments result + [[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'], + [['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'], + [['c:/ignore', 'c:/some/file'], 'c:\\some\\file'], + [['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'], + [['.'], process.cwd()], + [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'], + [['c:/', '//'], 'c:\\'], + [['c:/', '//dir'], 'c:\\dir'], + [['c:/', '//server/share'], '\\\\server\\share\\'], + [['c:/', '//server//share'], '\\\\server\\share\\'], + [['c:/', '///some//dir'], 'c:\\some\\dir'], + [['C:\\foo\\tmp.3\\', '..\\tmp.3\\cycles\\root.js'], + 'C:\\foo\\tmp.3\\cycles\\root.js'], + ], + ], + [ path.posix.resolve, + // Arguments result + [[['/var/lib', '../', 'file/'], '/var/file'], + [['/var/lib', '/../', 'file/'], '/file'], + // TODO(wafuwafu13): Enable this + // [['a/b/c/', '../../..'], posixyCwd], + // [['.'], posixyCwd], + [['/some/dir', '.', '/absolute/'], '/absolute'], + [['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js'], + ], + ], +]; +resolveTests.forEach(([resolve, tests]) => { + tests.forEach(([test, expected]) => { + const actual = resolve.apply(null, test); + let actualAlt; + const os = resolve === path.win32.resolve ? 'win32' : 'posix'; + if (resolve === path.win32.resolve && !common.isWindows) + actualAlt = actual.replace(backslashRE, '/'); + else if (resolve !== path.win32.resolve && common.isWindows) + actualAlt = actual.replace(slashRE, '\\'); + + const message = + `path.${os}.resolve(${test.map(JSON.stringify).join(',')})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected && actualAlt !== expected) + failures.push(message); + }); +}); +assert.strictEqual(failures.length, 0, failures.join('\n')); + +if (common.isWindows) { + // Test resolving the current Windows drive letter from a spawned process. + // See https://github.com/nodejs/node/issues/7215 + const currentDriveLetter = path.parse(process.cwd()).root.substring(0, 2); + const resolveFixture = fixtures.path('path-resolve.js'); + // TODO(wafuwafu13): Enable this + // const spawnResult = child.spawnSync( + // process.argv[0], [resolveFixture, currentDriveLetter]); + // const resolvedPath = spawnResult.stdout.toString().trim(); + // assert.strictEqual(resolvedPath.toLowerCase(), process.cwd().toLowerCase()); +} + +if (!common.isWindows) { + // Test handling relative paths to be safe when process.cwd() fails. + process.cwd = () => ''; + assert.strictEqual(process.cwd(), ''); + const resolved = path.resolve(); + const expected = '.'; + // TODO(wafuwafu13): Enable this + // assert.strictEqual(resolved, expected); +} diff --git a/cli/tests/node_compat/test/parallel/test-path-win32-exists.js b/cli/tests/node_compat/test/parallel/test-path-win32-exists.js new file mode 100644 index 00000000000000..6062fbacb1d706 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-win32-exists.js @@ -0,0 +1,13 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('path/win32'), require('path').win32); diff --git a/cli/tests/node_compat/test/parallel/test-path-zero-length-strings.js b/cli/tests/node_compat/test/parallel/test-path-zero-length-strings.js new file mode 100644 index 00000000000000..d8025224b7f9b1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path-zero-length-strings.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// These testcases are specific to one uncommon behavior in path module. Few +// of the functions in path module, treat '' strings as current working +// directory. This test makes sure that the behavior is intact between commits. +// See: https://github.com/nodejs/node/pull/2106 + +require('../common'); +const assert = require('assert'); +const path = require('path'); +const pwd = process.cwd(); + +// Join will internally ignore all the zero-length strings and it will return +// '.' if the joined string is a zero-length string. +assert.strictEqual(path.posix.join(''), '.'); +assert.strictEqual(path.posix.join('', ''), '.'); +assert.strictEqual(path.win32.join(''), '.'); +assert.strictEqual(path.win32.join('', ''), '.'); +assert.strictEqual(path.join(pwd), pwd); +assert.strictEqual(path.join(pwd, ''), pwd); + +// Normalize will return '.' if the input is a zero-length string +assert.strictEqual(path.posix.normalize(''), '.'); +assert.strictEqual(path.win32.normalize(''), '.'); +assert.strictEqual(path.normalize(pwd), pwd); + +// Since '' is not a valid path in any of the common environments, return false +assert.strictEqual(path.posix.isAbsolute(''), false); +assert.strictEqual(path.win32.isAbsolute(''), false); + +// Resolve, internally ignores all the zero-length strings and returns the +// current working directory +assert.strictEqual(path.resolve(''), pwd); +assert.strictEqual(path.resolve('', ''), pwd); + +// Relative, internally calls resolve. So, '' is actually the current directory +assert.strictEqual(path.relative('', pwd), ''); +assert.strictEqual(path.relative(pwd, ''), ''); +assert.strictEqual(path.relative(pwd, pwd), ''); diff --git a/cli/tests/node_compat/test/parallel/test-path.js b/cli/tests/node_compat/test/parallel/test-path.js new file mode 100644 index 00000000000000..03bbfebb237a0e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-path.js @@ -0,0 +1,81 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +// Test thrown TypeErrors +const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN]; + +function fail(fn) { + const args = Array.from(arguments).slice(1); + + assert.throws(() => { + fn.apply(null, args); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} + +typeErrorTests.forEach((test) => { + [path.posix, path.win32].forEach((namespace) => { + fail(namespace.join, test); + fail(namespace.resolve, test); + fail(namespace.normalize, test); + fail(namespace.isAbsolute, test); + fail(namespace.relative, test, 'foo'); + fail(namespace.relative, 'foo', test); + fail(namespace.parse, test); + fail(namespace.dirname, test); + fail(namespace.basename, test); + fail(namespace.extname, test); + + // Undefined is a valid value as the second argument to basename + if (test !== undefined) { + fail(namespace.basename, 'foo', test); + } + }); +}); + +// path.sep tests +// windows +assert.strictEqual(path.win32.sep, '\\'); +// posix +assert.strictEqual(path.posix.sep, '/'); + +// path.delimiter tests +// windows +assert.strictEqual(path.win32.delimiter, ';'); +// posix +assert.strictEqual(path.posix.delimiter, ':'); + +// TODO(wafuwafu13): Enable this +// if (common.isWindows) +// assert.strictEqual(path, path.win32); +// else +// assert.strictEqual(path, path.posix); diff --git a/cli/tests/node_compat/test/parallel/test-process-beforeexit.js b/cli/tests/node_compat/test/parallel/test-process-beforeexit.js new file mode 100644 index 00000000000000..7450bf9449e7b5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-beforeexit.js @@ -0,0 +1,88 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +process.once('beforeExit', common.mustCall(tryImmediate)); + +function tryImmediate() { + setImmediate(common.mustCall(() => { + process.once('beforeExit', common.mustCall(tryTimer)); + })); +} + +function tryTimer() { + setTimeout(common.mustCall(() => { + process.once('beforeExit', common.mustCall(tryListen)); + }), 1); +} + +function tryListen() { + net.createServer() + .listen(0) + .on('listening', common.mustCall(function() { + this.close(); + process.once('beforeExit', common.mustCall(tryRepeatedTimer)); + })); +} + +// Test that a function invoked from the beforeExit handler can use a timer +// to keep the event loop open, which can use another timer to keep the event +// loop open, etc. +// +// After N times, call function `tryNextTick` to test behaviors of the +// `process.nextTick`. +function tryRepeatedTimer() { + const N = 5; + let n = 0; + const repeatedTimer = common.mustCall(function() { + if (++n < N) + setTimeout(repeatedTimer, 1); + else // n == N + process.once('beforeExit', common.mustCall(tryNextTickSetImmediate)); + }, N); + setTimeout(repeatedTimer, 1); +} + +// Test if the callback of `process.nextTick` can be invoked. +function tryNextTickSetImmediate() { + process.nextTick(common.mustCall(function() { + setImmediate(common.mustCall(() => { + process.once('beforeExit', common.mustCall(tryNextTick)); + })); + })); +} + +// Test that `process.nextTick` won't keep the event loop running by itself. +function tryNextTick() { + process.nextTick(common.mustCall(function() { + process.once('beforeExit', common.mustNotCall()); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-process-binding-internalbinding-allowlist.js b/cli/tests/node_compat/test/parallel/test-process-binding-internalbinding-allowlist.js new file mode 100644 index 00000000000000..291bace0cbd043 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-binding-internalbinding-allowlist.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// Assert that allowed internalBinding modules are accessible via +// process.binding(). +assert(process.binding('async_wrap')); +assert(process.binding('buffer')); +assert(process.binding('cares_wrap')); +assert(process.binding('constants')); +assert(process.binding('contextify')); +if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check + assert(process.binding('crypto')); +} +assert(process.binding('fs')); +assert(process.binding('fs_event_wrap')); +assert(process.binding('http_parser')); +if (common.hasIntl) { + assert(process.binding('icu')); +} +assert(process.binding('inspector')); +assert(process.binding('js_stream')); +assert(process.binding('natives')); +assert(process.binding('os')); +assert(process.binding('pipe_wrap')); +assert(process.binding('signal_wrap')); +assert(process.binding('spawn_sync')); +assert(process.binding('stream_wrap')); +assert(process.binding('tcp_wrap')); +if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check + assert(process.binding('tls_wrap')); +} +assert(process.binding('tty_wrap')); +assert(process.binding('udp_wrap')); +assert(process.binding('url')); +assert(process.binding('util')); +assert(process.binding('uv')); +assert(process.binding('v8')); +assert(process.binding('zlib')); diff --git a/cli/tests/node_compat/test/parallel/test-process-env-allowed-flags.js b/cli/tests/node_compat/test/parallel/test-process-env-allowed-flags.js new file mode 100644 index 00000000000000..3ea6d4dda62e26 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-env-allowed-flags.js @@ -0,0 +1,109 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// Assert legit flags are allowed, and bogus flags are disallowed +{ + const goodFlags = [ + '--perf_basic_prof', + '--perf-basic-prof', + 'perf-basic-prof', + '--perf_basic-prof', + 'perf_basic-prof', + 'perf_basic_prof', + '-r', + 'r', + '--stack-trace-limit=100', + '--stack-trace-limit=-=xX_nodejs_Xx=-', + ].concat(process.features.inspector ? [ + '--inspect-brk', + 'inspect-brk', + '--inspect_brk', + ] : []); + + const badFlags = [ + 'INSPECT-BRK', + '--INSPECT-BRK', + '--r', + '-R', + '---inspect-brk', + '--cheeseburgers', + ]; + + goodFlags.forEach((flag) => { + assert.strictEqual( + process.allowedNodeEnvironmentFlags.has(flag), + true, + `flag should be in set: ${flag}` + ); + }); + + badFlags.forEach((flag) => { + assert.strictEqual( + process.allowedNodeEnvironmentFlags.has(flag), + false, + `flag should not be in set: ${flag}` + ); + }); +} + +// Assert all "canonical" flags begin with dash(es) +{ + process.allowedNodeEnvironmentFlags.forEach((flag) => { + assert.match(flag, /^--?[a-zA-Z0-9._-]+$/); + }); +} + +// Assert immutability of process.allowedNodeEnvironmentFlags +{ + assert.strictEqual(Object.isFrozen(process.allowedNodeEnvironmentFlags), + true); + + process.allowedNodeEnvironmentFlags.add('foo'); + assert.strictEqual(process.allowedNodeEnvironmentFlags.has('foo'), false); + Set.prototype.add.call(process.allowedNodeEnvironmentFlags, 'foo'); + assert.strictEqual(process.allowedNodeEnvironmentFlags.has('foo'), false); + + const thisArg = {}; + process.allowedNodeEnvironmentFlags.forEach( + common.mustCallAtLeast(function(flag, _, set) { + assert.notStrictEqual(flag, 'foo'); + assert.strictEqual(this, thisArg); + assert.strictEqual(set, process.allowedNodeEnvironmentFlags); + }), + thisArg + ); + + for (const flag of process.allowedNodeEnvironmentFlags.keys()) { + assert.notStrictEqual(flag, 'foo'); + } + for (const flag of process.allowedNodeEnvironmentFlags.values()) { + assert.notStrictEqual(flag, 'foo'); + } + for (const flag of process.allowedNodeEnvironmentFlags) { + assert.notStrictEqual(flag, 'foo'); + } + for (const [flag] of process.allowedNodeEnvironmentFlags.entries()) { + assert.notStrictEqual(flag, 'foo'); + } + + const size = process.allowedNodeEnvironmentFlags.size; + + process.allowedNodeEnvironmentFlags.clear(); + assert.strictEqual(process.allowedNodeEnvironmentFlags.size, size); + Set.prototype.clear.call(process.allowedNodeEnvironmentFlags); + assert.strictEqual(process.allowedNodeEnvironmentFlags.size, size); + + process.allowedNodeEnvironmentFlags.delete('-r'); + assert.strictEqual(process.allowedNodeEnvironmentFlags.size, size); + Set.prototype.delete.call(process.allowedNodeEnvironmentFlags, '-r'); + assert.strictEqual(process.allowedNodeEnvironmentFlags.size, size); +} diff --git a/cli/tests/node_compat/test/parallel/test-process-exit-from-before-exit.js b/cli/tests/node_compat/test/parallel/test-process-exit-from-before-exit.js new file mode 100644 index 00000000000000..bd6e9eedfe1855 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-exit-from-before-exit.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.on('beforeExit', common.mustCall(function() { + setTimeout(common.mustNotCall(), 5); + process.exit(0); // Should execute immediately even if we schedule new work. + assert.fail(); +})); diff --git a/cli/tests/node_compat/test/parallel/test-process-exit-handler.js b/cli/tests/node_compat/test/parallel/test-process-exit-handler.js new file mode 100644 index 00000000000000..c191373cc28c3e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-exit-handler.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +if (!common.isMainThread) + common.skip('execArgv does not affect Workers'); + +// This test ensures that no asynchronous operations are performed in the 'exit' +// handler. +// https://github.com/nodejs/node/issues/12322 + +process.on('exit', () => { + setTimeout(() => process.abort(), 0); // Should not run. + for (const start = Date.now(); Date.now() - start < 10;); +}); diff --git a/cli/tests/node_compat/test/parallel/test-process-exit-recursive.js b/cli/tests/node_compat/test/parallel/test-process-exit-recursive.js new file mode 100644 index 00000000000000..69080ea9c685a0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-exit-recursive.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// Recursively calling .exit() should not overflow the call stack +let nexits = 0; + +process.on('exit', function(code) { + assert.strictEqual(nexits++, 0); + assert.strictEqual(code, 1); + + // Now override the exit code of 1 with 0 so that the test passes + process.exit(0); +}); + +process.exit(1); diff --git a/cli/tests/node_compat/test/parallel/test-process-exit.js b/cli/tests/node_compat/test/parallel/test-process-exit.js new file mode 100644 index 00000000000000..2775776240aef5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-exit.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// Calling .exit() from within "exit" should not overflow the call stack +let nexits = 0; + +process.on('exit', function(code) { + assert.strictEqual(nexits++, 0); + assert.strictEqual(code, 0); + process.exit(); +}); + +// "exit" should be emitted unprovoked diff --git a/cli/tests/node_compat/test/parallel/test-process-kill-pid.js b/cli/tests/node_compat/test/parallel/test-process-kill-pid.js new file mode 100644 index 00000000000000..93651eacff5b1d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-kill-pid.js @@ -0,0 +1,116 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Test variants of pid +// +// null: TypeError +// undefined: TypeError +// +// 'SIGTERM': TypeError +// +// String(process.pid): TypeError +// +// Nan, Infinity, -Infinity: TypeError +// +// 0, String(0): our group process +// +// process.pid, String(process.pid): ourself + +['SIGTERM', null, undefined, NaN, Infinity, -Infinity].forEach((val) => { + assert.throws(() => process.kill(val), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "pid" argument must be of type number.' + + common.invalidArgTypeHelper(val) + }); +}); + +// Test that kill throws an error for unknown signal names +assert.throws(() => process.kill(0, 'test'), { + code: 'ERR_UNKNOWN_SIGNAL', + name: 'TypeError', + message: 'Unknown signal: test' +}); + +// Test that kill throws an error for invalid signal numbers +assert.throws(() => process.kill(0, 987), { + code: 'EINVAL', + name: 'Error', + message: 'kill EINVAL' +}); + +// Test kill argument processing in valid cases. +// +// Monkey patch _kill so that we don't actually send any signals, particularly +// that we don't kill our process group, or try to actually send ANY signals on +// windows, which doesn't support them. +function kill(tryPid, trySig, expectPid, expectSig) { + let getPid; + let getSig; + const origKill = process._kill; + process._kill = function(pid, sig) { + getPid = pid; + getSig = sig; + + // un-monkey patch process._kill + process._kill = origKill; + }; + + process.kill(tryPid, trySig); + + assert.strictEqual(getPid.toString(), expectPid.toString()); + assert.strictEqual(getSig, expectSig); +} + +// Note that SIGHUP and SIGTERM map to 1 and 15 respectively, even on Windows +// (for Windows, libuv maps 1 and 15 to the correct behavior). + +kill(0, 'SIGHUP', 0, 1); +kill(0, undefined, 0, 15); +kill('0', 'SIGHUP', 0, 1); +kill('0', undefined, 0, 15); + +// Confirm that numeric signal arguments are supported + +kill(0, 1, 0, 1); +kill(0, 15, 0, 15); + +// Negative numbers are meaningful on unix +kill(-1, 'SIGHUP', -1, 1); +kill(-1, undefined, -1, 15); +kill('-1', 'SIGHUP', -1, 1); +kill('-1', undefined, -1, 15); + +kill(process.pid, 'SIGHUP', process.pid, 1); +kill(process.pid, undefined, process.pid, 15); +kill(String(process.pid), 'SIGHUP', process.pid, 1); +kill(String(process.pid), undefined, process.pid, 15); diff --git a/cli/tests/node_compat/test/parallel/test-process-uptime.js b/cli/tests/node_compat/test/parallel/test-process-uptime.js new file mode 100644 index 00000000000000..3d432f1302da8e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-process-uptime.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +console.error(process.uptime()); +// Add some wiggle room for different platforms. +// Verify that the returned value is in seconds - +// 15 seconds should be a good estimate. +assert.ok(process.uptime() <= 15); + +const original = process.uptime(); + +setTimeout(function() { + const uptime = process.uptime(); + assert.ok(original < uptime); +}, 10); diff --git a/cli/tests/node_compat/test/parallel/test-promise-unhandled-silent.js b/cli/tests/node_compat/test/parallel/test-promise-unhandled-silent.js new file mode 100644 index 00000000000000..a2b30f74ce2b71 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-promise-unhandled-silent.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --unhandled-rejections=none +'use strict'; + +const common = require('../common'); + +// Verify that ignoring unhandled rejection works fine and that no warning is +// logged. + +new Promise(() => { + throw new Error('One'); +}); + +Promise.reject('test'); + +process.on('warning', common.mustNotCall('warning')); +process.on('uncaughtException', common.mustNotCall('uncaughtException')); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); + +process.on('unhandledRejection', common.mustCall(2)); + +setTimeout(common.mustCall(), 2); diff --git a/cli/tests/node_compat/test/parallel/test-promise-unhandled-throw-handler.js b/cli/tests/node_compat/test/parallel/test-promise-unhandled-throw-handler.js new file mode 100644 index 00000000000000..57614c3249da4e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-promise-unhandled-throw-handler.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --unhandled-rejections=throw +'use strict'; + +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); + +// Verify that the unhandledRejection handler prevents triggering +// uncaught exceptions + +const err1 = new Error('One'); + +const errors = [err1, null]; + +const ref = new Promise(() => { + throw err1; +}); +// Explicitly reject `null`. +Promise.reject(null); + +process.on('warning', common.mustNotCall('warning')); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); +process.on('exit', assert.strictEqual.bind(null, 0)); +process.on('uncaughtException', common.mustNotCall('uncaughtException')); + +const timer = setTimeout(() => console.log(ref), 1000); + +const counter = new Countdown(2, () => { + clearTimeout(timer); +}); + +process.on('unhandledRejection', common.mustCall((err) => { + counter.dec(); + const knownError = errors.shift(); + assert.deepStrictEqual(err, knownError); +}, 2)); diff --git a/cli/tests/node_compat/test/parallel/test-punycode.js b/cli/tests/node_compat/test/parallel/test-punycode.js new file mode 100644 index 00000000000000..cb65954a020c6c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-punycode.js @@ -0,0 +1,280 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --pending-deprecation + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const punycodeWarning = + 'The `punycode` module is deprecated. Please use a userland alternative ' + + 'instead.'; +common.expectWarning('DeprecationWarning', punycodeWarning, 'DEP0040'); + +const punycode = require('punycode'); +const assert = require('assert'); + +assert.strictEqual(punycode.encode('ü'), 'tda'); +assert.strictEqual(punycode.encode('Goethe'), 'Goethe-'); +assert.strictEqual(punycode.encode('Bücher'), 'Bcher-kva'); +assert.strictEqual( + punycode.encode( + 'Willst du die Blüthe des frühen, die Früchte des späteren Jahres' + ), + 'Willst du die Blthe des frhen, die Frchte des spteren Jahres-x9e96lkal' +); +assert.strictEqual(punycode.encode('日本語'), 'wgv71a119e'); +assert.strictEqual(punycode.encode('𩸽'), 'x73l'); + +assert.strictEqual(punycode.decode('tda'), 'ü'); +assert.strictEqual(punycode.decode('Goethe-'), 'Goethe'); +assert.strictEqual(punycode.decode('Bcher-kva'), 'Bücher'); +assert.strictEqual( + punycode.decode( + 'Willst du die Blthe des frhen, die Frchte des spteren Jahres-x9e96lkal' + ), + 'Willst du die Blüthe des frühen, die Früchte des späteren Jahres' +); +assert.strictEqual(punycode.decode('wgv71a119e'), '日本語'); +assert.strictEqual(punycode.decode('x73l'), '𩸽'); +assert.throws(() => { + punycode.decode(' '); +}, /^RangeError: Invalid input$/); +assert.throws(() => { + punycode.decode('α-'); +}, /^RangeError: Illegal input >= 0x80 \(not a basic code point\)$/); +assert.throws(() => { + punycode.decode('あ'); +}, /^RangeError: Overflow: input needs wider integers to process$/); + +// http://tools.ietf.org/html/rfc3492#section-7.1 +const tests = [ + // (A) Arabic (Egyptian) + { + encoded: 'egbpdaj6bu4bxfgehfvwxn', + decoded: '\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644\u0645' + + '\u0648\u0634\u0639\u0631\u0628\u064A\u061F' + }, + + // (B) Chinese (simplified) + { + encoded: 'ihqwcrb4cv8a8dqg056pqjye', + decoded: '\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587' + }, + + // (C) Chinese (traditional) + { + encoded: 'ihqwctvzc91f659drss3x8bo0yb', + decoded: '\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587' + }, + + // (D) Czech: Proprostnemluvesky + { + encoded: 'Proprostnemluvesky-uyb24dma41a', + decoded: '\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074\u011B' + + '\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D\u0065\u0073\u006B\u0079' + }, + + // (E) Hebrew + { + encoded: '4dbcagdahymbxekheh6e0a7fei0b', + decoded: '\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8\u05DC' + + '\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2\u05D1\u05E8\u05D9\u05EA' + }, + + // (F) Hindi (Devanagari) + { + encoded: 'i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd', + decoded: '\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D\u0926' + + '\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939\u0940\u0902\u092C' + + '\u094B\u0932\u0938\u0915\u0924\u0947\u0939\u0948\u0902' + }, + + // (G) Japanese (kanji and hiragana) + { + encoded: 'n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa', + decoded: '\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092\u8A71' + + '\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B' + }, + + // (H) Korean (Hangul syllables) + { + encoded: '989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879' + + 'ccm6fea98c', + decoded: '\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774\uD55C' + + '\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74\uC5BC\uB9C8\uB098' + + '\uC88B\uC744\uAE4C' + }, + + // (I) Russian (Cyrillic) + { + encoded: 'b1abfaaepdrnnbgefbadotcwatmq2g4l', + decoded: '\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E\u043D' + + '\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440\u044F\u0442\u043F' + + '\u043E\u0440\u0443\u0441\u0441\u043A\u0438' + }, + + // (J) Spanish: PorqunopuedensimplementehablarenEspaol + { + encoded: 'PorqunopuedensimplementehablarenEspaol-fmd56a', + decoded: '\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070\u0075' + + '\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070\u006C\u0065\u006D' + + '\u0065\u006E\u0074\u0065\u0068\u0061\u0062\u006C\u0061\u0072\u0065' + + '\u006E\u0045\u0073\u0070\u0061\u00F1\u006F\u006C' + }, + + // (K) Vietnamese: Tisaohkhngth + // chnitingVit + { + encoded: 'TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g', + decoded: '\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B\u0068' + + '\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068\u1EC9\u006E\u00F3' + + '\u0069\u0074\u0069\u1EBF\u006E\u0067\u0056\u0069\u1EC7\u0074' + }, + + // (L) 3B + { + encoded: '3B-ww4c5e180e575a65lsy2b', + decoded: '\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F' + }, + + // (M) -with-SUPER-MONKEYS + { + encoded: '-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n', + decoded: '\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074\u0068' + + '\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D\u004F\u004E\u004B' + + '\u0045\u0059\u0053' + }, + + // (N) Hello-Another-Way- + { + encoded: 'Hello-Another-Way--fc4qua05auwb3674vfr0b', + decoded: '\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F\u0074' + + '\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D\u305D\u308C\u305E' + + '\u308C\u306E\u5834\u6240' + }, + + // (O) 2 + { + encoded: '2-u9tlzr9756bt3uc0v', + decoded: '\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032' + }, + + // (P) MajiKoi5 + { + encoded: 'MajiKoi5-783gue6qz075azm5e', + decoded: '\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059\u308B' + + '\u0035\u79D2\u524D' + }, + + // (Q) de + { + encoded: 'de-jg4avhby1noc0d', + decoded: '\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0' + }, + + // (R) + { + encoded: 'd9juau41awczczp', + decoded: '\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067' + }, + + // (S) -> $1.00 <- + { + encoded: '-> $1.00 <--', + decoded: '\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020\u003C' + + '\u002D' + }, +]; + +let errors = 0; +const handleError = (error, name) => { + console.error( + `FAIL: ${name} expected ${error.expected}, got ${error.actual}` + ); + errors++; +}; + +const regexNonASCII = /[^\x20-\x7E]/; +const testBattery = { + encode: (test) => assert.strictEqual( + punycode.encode(test.decoded), + test.encoded + ), + decode: (test) => assert.strictEqual( + punycode.decode(test.encoded), + test.decoded + ), + toASCII: (test) => assert.strictEqual( + punycode.toASCII(test.decoded), + regexNonASCII.test(test.decoded) ? + `xn--${test.encoded}` : + test.decoded + ), + toUnicode: (test) => assert.strictEqual( + punycode.toUnicode( + regexNonASCII.test(test.decoded) ? + `xn--${test.encoded}` : + test.decoded + ), + regexNonASCII.test(test.decoded) ? + test.decoded.toLowerCase() : + test.decoded + ) +}; + +tests.forEach((testCase) => { + Object.keys(testBattery).forEach((key) => { + try { + testBattery[key](testCase); + } catch (error) { + handleError(error, key); + } + }); +}); + +// BMP code point +assert.strictEqual(punycode.ucs2.encode([0x61]), 'a'); +// Supplementary code point (surrogate pair) +assert.strictEqual(punycode.ucs2.encode([0x1D306]), '\uD834\uDF06'); +// high surrogate +assert.strictEqual(punycode.ucs2.encode([0xD800]), '\uD800'); +// High surrogate followed by non-surrogates +assert.strictEqual(punycode.ucs2.encode([0xD800, 0x61, 0x62]), '\uD800ab'); +// low surrogate +assert.strictEqual(punycode.ucs2.encode([0xDC00]), '\uDC00'); +// Low surrogate followed by non-surrogates +assert.strictEqual(punycode.ucs2.encode([0xDC00, 0x61, 0x62]), '\uDC00ab'); + +assert.strictEqual(errors, 0); + +// test map domain +assert.strictEqual(punycode.toASCII('Bücher@日本語.com'), + 'Bücher@xn--wgv71a119e.com'); +assert.strictEqual(punycode.toUnicode('Bücher@xn--wgv71a119e.com'), + 'Bücher@日本語.com'); diff --git a/cli/tests/node_compat/test/parallel/test-querystring-escape.js b/cli/tests/node_compat/test/parallel/test-querystring-escape.js new file mode 100644 index 00000000000000..47f0ce1f1b40a4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-querystring-escape.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +const qs = require('querystring'); + +assert.strictEqual(qs.escape(5), '5'); +assert.strictEqual(qs.escape('test'), 'test'); +assert.strictEqual(qs.escape({}), '%5Bobject%20Object%5D'); +assert.strictEqual(qs.escape([5, 10]), '5%2C10'); +assert.strictEqual(qs.escape('Ŋōđĕ'), '%C5%8A%C5%8D%C4%91%C4%95'); +assert.strictEqual(qs.escape('testŊōđĕ'), 'test%C5%8A%C5%8D%C4%91%C4%95'); +assert.strictEqual(qs.escape(`${String.fromCharCode(0xD800 + 1)}test`), + '%F0%90%91%B4est'); + +assert.throws( + () => qs.escape(String.fromCharCode(0xD800 + 1)), + { + code: 'ERR_INVALID_URI', + name: 'URIError', + message: 'URI malformed' + } +); + +// Using toString for objects +assert.strictEqual( + qs.escape({ test: 5, toString: () => 'test', valueOf: () => 10 }), + 'test' +); + +// `toString` is not callable, must throw an error. +// Error message will vary between different JavaScript engines, so only check +// that it is a `TypeError`. +assert.throws(() => qs.escape({ toString: 5 }), TypeError); + +// Should use valueOf instead of non-callable toString. +assert.strictEqual(qs.escape({ toString: 5, valueOf: () => 'test' }), 'test'); + +// Error message will vary between different JavaScript engines, so only check +// that it is a `TypeError`. +assert.throws(() => qs.escape(Symbol('test')), TypeError); diff --git a/cli/tests/node_compat/test/parallel/test-querystring-maxKeys-non-finite.js b/cli/tests/node_compat/test/parallel/test-querystring-maxKeys-non-finite.js new file mode 100644 index 00000000000000..c87889853476bf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-querystring-maxKeys-non-finite.js @@ -0,0 +1,65 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// This test was originally written to test a regression +// that was introduced by +// https://github.com/nodejs/node/pull/2288#issuecomment-179543894 +require('../common'); + +const assert = require('assert'); +const parse = require('querystring').parse; + +// Taken from express-js/body-parser +// https://github.com/expressjs/body-parser/blob/ed25264fb494cf0c8bc992b8257092cd4f694d5e/test/urlencoded.js#L636-L651 +function createManyParams(count) { + let str = ''; + + if (count === 0) { + return str; + } + + str += '0=0'; + + for (let i = 1; i < count; i++) { + const n = i.toString(36); + str += `&${n}=${n}`; + } + + return str; +} + +const count = 10000; +const originalMaxLength = 1000; +const params = createManyParams(count); + +// thealphanerd +// 27def4f introduced a change to parse that would cause Infinity +// to be passed to String.prototype.split as an argument for limit +// In this instance split will always return an empty array +// this test confirms that the output of parse is the expected length +// when passed Infinity as the argument for maxKeys +const resultInfinity = parse(params, undefined, undefined, { + maxKeys: Infinity +}); +const resultNaN = parse(params, undefined, undefined, { + maxKeys: NaN +}); +const resultInfinityString = parse(params, undefined, undefined, { + maxKeys: 'Infinity' +}); +const resultNaNString = parse(params, undefined, undefined, { + maxKeys: 'NaN' +}); + +// Non Finite maxKeys should return the length of input +assert.strictEqual(Object.keys(resultInfinity).length, count); +assert.strictEqual(Object.keys(resultNaN).length, count); +// Strings maxKeys should return the maxLength +// defined by parses internals +assert.strictEqual(Object.keys(resultInfinityString).length, originalMaxLength); +assert.strictEqual(Object.keys(resultNaNString).length, originalMaxLength); diff --git a/cli/tests/node_compat/test/parallel/test-querystring-multichar-separator.js b/cli/tests/node_compat/test/parallel/test-querystring-multichar-separator.js new file mode 100644 index 00000000000000..5d0cf62ca5c78f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-querystring-multichar-separator.js @@ -0,0 +1,32 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const qs = require('querystring'); + +function check(actual, expected) { + assert(!(actual instanceof Object)); + assert.deepStrictEqual(Object.keys(actual).sort(), + Object.keys(expected).sort()); + Object.keys(expected).forEach(function(key) { + assert.deepStrictEqual(actual[key], expected[key]); + }); +} + +check(qs.parse('foo=>bar&&bar=>baz', '&&', '=>'), + { foo: 'bar', bar: 'baz' }); + +check(qs.stringify({ foo: 'bar', bar: 'baz' }, '&&', '=>'), + 'foo=>bar&&bar=>baz'); + +check(qs.parse('foo==>bar, bar==>baz', ', ', '==>'), + { foo: 'bar', bar: 'baz' }); + +check(qs.stringify({ foo: 'bar', bar: 'baz' }, ', ', '==>'), + 'foo==>bar, bar==>baz'); diff --git a/cli/tests/node_compat/test/parallel/test-querystring.js b/cli/tests/node_compat/test/parallel/test-querystring.js new file mode 100644 index 00000000000000..933f6b1ba6a91a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-querystring.js @@ -0,0 +1,490 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.12.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const inspect = require('util').inspect; + +// test using assert +const qs = require('querystring'); + +function createWithNoPrototype(properties) { + const noProto = Object.create(null); + properties.forEach((property) => { + noProto[property.key] = property.value; + }); + return noProto; +} +// Folding block, commented to pass gjslint +// {{{ +// [ wonkyQS, canonicalQS, obj ] +const qsTestCases = [ + ['__proto__=1', + '__proto__=1', + createWithNoPrototype([{ key: '__proto__', value: '1' }])], + ['__defineGetter__=asdf', + '__defineGetter__=asdf', + JSON.parse('{"__defineGetter__":"asdf"}')], + ['foo=918854443121279438895193', + 'foo=918854443121279438895193', + { 'foo': '918854443121279438895193' }], + ['foo=bar', 'foo=bar', { 'foo': 'bar' }], + ['foo=bar&foo=quux', 'foo=bar&foo=quux', { 'foo': ['bar', 'quux'] }], + ['foo=1&bar=2', 'foo=1&bar=2', { 'foo': '1', 'bar': '2' }], + ['my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F', + 'my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F', + { 'my weird field': 'q1!2"\'w$5&7/z8)?' }], + ['foo%3Dbaz=bar', 'foo%3Dbaz=bar', { 'foo=baz': 'bar' }], + ['foo=baz=bar', 'foo=baz%3Dbar', { 'foo': 'baz=bar' }], + ['str=foo&arr=1&arr=2&arr=3&somenull=&undef=', + 'str=foo&arr=1&arr=2&arr=3&somenull=&undef=', + { 'str': 'foo', + 'arr': ['1', '2', '3'], + 'somenull': '', + 'undef': '' }], + [' foo = bar ', '%20foo%20=%20bar%20', { ' foo ': ' bar ' }], + ['foo=%zx', 'foo=%25zx', { 'foo': '%zx' }], + ['foo=%EF%BF%BD', 'foo=%EF%BF%BD', { 'foo': '\ufffd' }], + // See: https://github.com/joyent/node/issues/1707 + ['hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz', + 'hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz', + { hasOwnProperty: 'x', + toString: 'foo', + valueOf: 'bar', + __defineGetter__: 'baz' }], + // See: https://github.com/joyent/node/issues/3058 + ['foo&bar=baz', 'foo=&bar=baz', { foo: '', bar: 'baz' }], + ['a=b&c&d=e', 'a=b&c=&d=e', { a: 'b', c: '', d: 'e' }], + ['a=b&c=&d=e', 'a=b&c=&d=e', { a: 'b', c: '', d: 'e' }], + ['a=b&=c&d=e', 'a=b&=c&d=e', { 'a': 'b', '': 'c', 'd': 'e' }], + ['a=b&=&c=d', 'a=b&=&c=d', { 'a': 'b', '': '', 'c': 'd' }], + ['&&foo=bar&&', 'foo=bar', { foo: 'bar' }], + ['&', '', {}], + ['&&&&', '', {}], + ['&=&', '=', { '': '' }], + ['&=&=', '=&=', { '': [ '', '' ] }], + ['=', '=', { '': '' }], + ['+', '%20=', { ' ': '' }], + ['+=', '%20=', { ' ': '' }], + ['+&', '%20=', { ' ': '' }], + ['=+', '=%20', { '': ' ' }], + ['+=&', '%20=', { ' ': '' }], + ['a&&b', 'a=&b=', { 'a': '', 'b': '' }], + ['a=a&&b=b', 'a=a&b=b', { 'a': 'a', 'b': 'b' }], + ['&a', 'a=', { 'a': '' }], + ['&=', '=', { '': '' }], + ['a&a&', 'a=&a=', { a: [ '', '' ] }], + ['a&a&a&', 'a=&a=&a=', { a: [ '', '', '' ] }], + ['a&a&a&a&', 'a=&a=&a=&a=', { a: [ '', '', '', '' ] }], + ['a=&a=value&a=', 'a=&a=value&a=', { a: [ '', 'value', '' ] }], + ['foo+bar=baz+quux', 'foo%20bar=baz%20quux', { 'foo bar': 'baz quux' }], + ['+foo=+bar', '%20foo=%20bar', { ' foo': ' bar' }], + ['a+', 'a%20=', { 'a ': '' }], + ['=a+', '=a%20', { '': 'a ' }], + ['a+&', 'a%20=', { 'a ': '' }], + ['=a+&', '=a%20', { '': 'a ' }], + ['%20+', '%20%20=', { ' ': '' }], + ['=%20+', '=%20%20', { '': ' ' }], + ['%20+&', '%20%20=', { ' ': '' }], + ['=%20+&', '=%20%20', { '': ' ' }], + [null, '', {}], + [undefined, '', {}], +]; + +// [ wonkyQS, canonicalQS, obj ] +const qsColonTestCases = [ + ['foo:bar', 'foo:bar', { 'foo': 'bar' }], + ['foo:bar;foo:quux', 'foo:bar;foo:quux', { 'foo': ['bar', 'quux'] }], + ['foo:1&bar:2;baz:quux', + 'foo:1%26bar%3A2;baz:quux', + { 'foo': '1&bar:2', 'baz': 'quux' }], + ['foo%3Abaz:bar', 'foo%3Abaz:bar', { 'foo:baz': 'bar' }], + ['foo:baz:bar', 'foo:baz%3Abar', { 'foo': 'baz:bar' }], +]; + +// [wonkyObj, qs, canonicalObj] +function extendedFunction() {} +extendedFunction.prototype = { a: 'b' }; +const qsWeirdObjects = [ + // eslint-disable-next-line node-core/no-unescaped-regexp-dot + [{ regexp: /./g }, 'regexp=', { 'regexp': '' }], + // eslint-disable-next-line node-core/no-unescaped-regexp-dot + [{ regexp: new RegExp('.', 'g') }, 'regexp=', { 'regexp': '' }], + [{ fn: () => {} }, 'fn=', { 'fn': '' }], + [{ fn: new Function('') }, 'fn=', { 'fn': '' }], + [{ math: Math }, 'math=', { 'math': '' }], + [{ e: extendedFunction }, 'e=', { 'e': '' }], + [{ d: new Date() }, 'd=', { 'd': '' }], + [{ d: Date }, 'd=', { 'd': '' }], + [ + { f: new Boolean(false), t: new Boolean(true) }, + 'f=&t=', + { 'f': '', 't': '' }, + ], + [{ f: false, t: true }, 'f=false&t=true', { 'f': 'false', 't': 'true' }], + [{ n: null }, 'n=', { 'n': '' }], + [{ nan: NaN }, 'nan=', { 'nan': '' }], + [{ inf: Infinity }, 'inf=', { 'inf': '' }], + [{ a: [], b: [] }, '', {}], + [{ a: 1, b: [] }, 'a=1', { 'a': '1' }], +]; + +// TODO(wafuwafu13): Enable this when `vm` is implemented. +// const vm = require('vm'); +// const foreignObject = vm.runInNewContext('({"foo": ["bar", "baz"]})'); + +const qsNoMungeTestCases = [ + ['', {}], + ['foo=bar&foo=baz', { 'foo': ['bar', 'baz'] }], + // ['foo=bar&foo=baz', foreignObject], + ['blah=burp', { 'blah': 'burp' }], + ['a=!-._~\'()*', { 'a': '!-._~\'()*' }], + ['a=abcdefghijklmnopqrstuvwxyz', { 'a': 'abcdefghijklmnopqrstuvwxyz' }], + ['a=ABCDEFGHIJKLMNOPQRSTUVWXYZ', { 'a': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }], + ['a=0123456789', { 'a': '0123456789' }], + ['gragh=1&gragh=3&goo=2', { 'gragh': ['1', '3'], 'goo': '2' }], + ['frappucino=muffin&goat%5B%5D=scone&pond=moose', + { 'frappucino': 'muffin', 'goat[]': 'scone', 'pond': 'moose' }], + ['trololol=yes&lololo=no', { 'trololol': 'yes', 'lololo': 'no' }], +]; + +const qsUnescapeTestCases = [ + ['there is nothing to unescape here', + 'there is nothing to unescape here'], + ['there%20are%20several%20spaces%20that%20need%20to%20be%20unescaped', + 'there are several spaces that need to be unescaped'], + ['there%2Qare%0-fake%escaped values in%%%%this%9Hstring', + 'there%2Qare%0-fake%escaped values in%%%%this%9Hstring'], + ['%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%30%31%32%33%34%35%36%37', + ' !"#$%&\'()*+,-./01234567'], + ['%%2a', '%*'], + ['%2sf%2a', '%2sf*'], + ['%2%2af%2a', '%2*f*'], +]; + +assert.strictEqual(qs.parse('id=918854443121279438895193').id, + '918854443121279438895193'); + +function check(actual, expected, input) { + assert(!(actual instanceof Object)); + const actualKeys = Object.keys(actual).sort(); + const expectedKeys = Object.keys(expected).sort(); + let msg; + if (typeof input === 'string') { + msg = `Input: ${inspect(input)}\n` + + `Actual keys: ${inspect(actualKeys)}\n` + + `Expected keys: ${inspect(expectedKeys)}`; + } + assert.deepStrictEqual(actualKeys, expectedKeys, msg); + expectedKeys.forEach((key) => { + if (typeof input === 'string') { + msg = `Input: ${inspect(input)}\n` + + `Key: ${inspect(key)}\n` + + `Actual value: ${inspect(actual[key])}\n` + + `Expected value: ${inspect(expected[key])}`; + } else { + msg = undefined; + } + assert.deepStrictEqual(actual[key], expected[key], msg); + }); +} + +// Test that the canonical qs is parsed properly. +qsTestCases.forEach((testCase) => { + check(qs.parse(testCase[0]), testCase[2], testCase[0]); +}); + +// Test that the colon test cases can do the same +qsColonTestCases.forEach((testCase) => { + check(qs.parse(testCase[0], ';', ':'), testCase[2], testCase[0]); +}); + +// Test the weird objects, that they get parsed properly +qsWeirdObjects.forEach((testCase) => { + check(qs.parse(testCase[1]), testCase[2], testCase[1]); +}); + +qsNoMungeTestCases.forEach((testCase) => { + assert.deepStrictEqual(qs.stringify(testCase[1], '&', '='), testCase[0]); +}); + +// Test the nested qs-in-qs case +{ + const f = qs.parse('a=b&q=x%3Dy%26y%3Dz'); + check(f, createWithNoPrototype([ + { key: 'a', value: 'b' }, + { key: 'q', value: 'x=y&y=z' }, + ])); + + f.q = qs.parse(f.q); + const expectedInternal = createWithNoPrototype([ + { key: 'x', value: 'y' }, + { key: 'y', value: 'z' }, + ]); + check(f.q, expectedInternal); +} + +// nested in colon +{ + const f = qs.parse('a:b;q:x%3Ay%3By%3Az', ';', ':'); + check(f, createWithNoPrototype([ + { key: 'a', value: 'b' }, + { key: 'q', value: 'x:y;y:z' }, + ])); + f.q = qs.parse(f.q, ';', ':'); + const expectedInternal = createWithNoPrototype([ + { key: 'x', value: 'y' }, + { key: 'y', value: 'z' }, + ]); + check(f.q, expectedInternal); +} + +// Now test stringifying + +// basic +qsTestCases.forEach((testCase) => { + assert.strictEqual(qs.stringify(testCase[2]), testCase[1]); +}); + +qsColonTestCases.forEach((testCase) => { + assert.strictEqual(qs.stringify(testCase[2], ';', ':'), testCase[1]); +}); + +qsWeirdObjects.forEach((testCase) => { + assert.strictEqual(qs.stringify(testCase[0]), testCase[1]); +}); + +// BigInt values + +assert.strictEqual(qs.stringify({ foo: 2n ** 1023n }), + 'foo=' + 2n ** 1023n); +assert.strictEqual(qs.stringify([0n, 1n, 2n]), + '0=0&1=1&2=2'); + +assert.strictEqual(qs.stringify({ foo: 2n ** 1023n }, + null, + null, + { encodeURIComponent: (c) => c }), + 'foo=' + 2n ** 1023n); +assert.strictEqual(qs.stringify([0n, 1n, 2n], + null, + null, + { encodeURIComponent: (c) => c }), + '0=0&1=1&2=2'); + +// Invalid surrogate pair throws URIError +assert.throws( + () => qs.stringify({ foo: '\udc00' }), + { + code: 'ERR_INVALID_URI', + name: 'URIError', + message: 'URI malformed' + } +); + +// Coerce numbers to string +assert.strictEqual(qs.stringify({ foo: 0 }), 'foo=0'); +assert.strictEqual(qs.stringify({ foo: -0 }), 'foo=0'); +assert.strictEqual(qs.stringify({ foo: 3 }), 'foo=3'); +assert.strictEqual(qs.stringify({ foo: -72.42 }), 'foo=-72.42'); +assert.strictEqual(qs.stringify({ foo: NaN }), 'foo='); +assert.strictEqual(qs.stringify({ foo: 1e21 }), 'foo=1e%2B21'); +assert.strictEqual(qs.stringify({ foo: Infinity }), 'foo='); + +// nested +{ + const f = qs.stringify({ + a: 'b', + q: qs.stringify({ + x: 'y', + y: 'z' + }) + }); + assert.strictEqual(f, 'a=b&q=x%3Dy%26y%3Dz'); +} + +qs.parse(undefined); // Should not throw. + +// nested in colon +{ + const f = qs.stringify({ + a: 'b', + q: qs.stringify({ + x: 'y', + y: 'z' + }, ';', ':') + }, ';', ':'); + assert.strictEqual(f, 'a:b;q:x%3Ay%3By%3Az'); +} + +// empty string +assert.strictEqual(qs.stringify(), ''); +assert.strictEqual(qs.stringify(0), ''); +assert.strictEqual(qs.stringify([]), ''); +assert.strictEqual(qs.stringify(null), ''); +assert.strictEqual(qs.stringify(true), ''); + +check(qs.parse(), {}); + +// empty sep +check(qs.parse('a', []), { a: '' }); + +// empty eq +check(qs.parse('a', null, []), { '': 'a' }); + +// Test limiting +assert.strictEqual( + Object.keys(qs.parse('a=1&b=1&c=1', null, null, { maxKeys: 1 })).length, + 1); + +// Test limiting with a case that starts from `&` +assert.strictEqual( + Object.keys(qs.parse('&a', null, null, { maxKeys: 1 })).length, + 0); + +// Test removing limit +{ + function testUnlimitedKeys() { + const query = {}; + + for (let i = 0; i < 2000; i++) query[i] = i; + + const url = qs.stringify(query); + + assert.strictEqual( + Object.keys(qs.parse(url, null, null, { maxKeys: 0 })).length, + 2000); + } + + testUnlimitedKeys(); +} + +{ + const b = qs.unescapeBuffer('%d3%f2Ug%1f6v%24%5e%98%cb' + + '%0d%ac%a2%2f%9d%eb%d8%a2%e6'); + // + assert.strictEqual(b[0], 0xd3); + assert.strictEqual(b[1], 0xf2); + assert.strictEqual(b[2], 0x55); + assert.strictEqual(b[3], 0x67); + assert.strictEqual(b[4], 0x1f); + assert.strictEqual(b[5], 0x36); + assert.strictEqual(b[6], 0x76); + assert.strictEqual(b[7], 0x24); + assert.strictEqual(b[8], 0x5e); + assert.strictEqual(b[9], 0x98); + assert.strictEqual(b[10], 0xcb); + assert.strictEqual(b[11], 0x0d); + assert.strictEqual(b[12], 0xac); + assert.strictEqual(b[13], 0xa2); + assert.strictEqual(b[14], 0x2f); + assert.strictEqual(b[15], 0x9d); + assert.strictEqual(b[16], 0xeb); + assert.strictEqual(b[17], 0xd8); + assert.strictEqual(b[18], 0xa2); + assert.strictEqual(b[19], 0xe6); +} + +assert.strictEqual(qs.unescapeBuffer('a+b', true).toString(), 'a b'); +assert.strictEqual(qs.unescapeBuffer('a+b').toString(), 'a+b'); +assert.strictEqual(qs.unescapeBuffer('a%').toString(), 'a%'); +assert.strictEqual(qs.unescapeBuffer('a%2').toString(), 'a%2'); +assert.strictEqual(qs.unescapeBuffer('a%20').toString(), 'a '); +assert.strictEqual(qs.unescapeBuffer('a%2g').toString(), 'a%2g'); +assert.strictEqual(qs.unescapeBuffer('a%%').toString(), 'a%%'); + +// Test invalid encoded string +check(qs.parse('%\u0100=%\u0101'), { '%Ā': '%ā' }); + +// Test custom decode +{ + function demoDecode(str) { + return str + str; + } + + check( + qs.parse('a=a&b=b&c=c', null, null, { decodeURIComponent: demoDecode }), + { aa: 'aa', bb: 'bb', cc: 'cc' }); + check( + qs.parse('a=a&b=b&c=c', null, '==', { decodeURIComponent: (str) => str }), + { 'a=a': '', 'b=b': '', 'c=c': '' }); +} + +// TODO(wafuwafu13): Enable this +// // Test QueryString.unescape +// { +// function errDecode(str) { +// throw new Error('To jump to the catch scope'); +// } + +// check(qs.parse('a=a', null, null, { decodeURIComponent: errDecode }), +// { a: 'a' }); +// } + +// Test custom encode +{ + function demoEncode(str) { + return str[0]; + } + + const obj = { aa: 'aa', bb: 'bb', cc: 'cc' }; + assert.strictEqual( + qs.stringify(obj, null, null, { encodeURIComponent: demoEncode }), + 'a=a&b=b&c=c'); +} + +// Test custom encode for different types +{ + const obj = { number: 1, bigint: 2n, true: true, false: false, object: {} }; + assert.strictEqual( + qs.stringify(obj, null, null, { encodeURIComponent: (v) => v }), + 'number=1&bigint=2&true=true&false=false&object='); +} + +// Test QueryString.unescapeBuffer +qsUnescapeTestCases.forEach((testCase) => { + assert.strictEqual(qs.unescape(testCase[0]), testCase[1]); + assert.strictEqual(qs.unescapeBuffer(testCase[0]).toString(), testCase[1]); +}); + +// TODO(wafuwafu13): Enable this +// // Test overriding .unescape +// { +// const prevUnescape = qs.unescape; +// qs.unescape = (str) => { +// return str.replace(/o/g, '_'); +// }; +// check( +// qs.parse('foo=bor'), +// createWithNoPrototype([{ key: 'f__', value: 'b_r' }])); +// qs.unescape = prevUnescape; +// } + +// Test separator and "equals" parsing order +check(qs.parse('foo&bar', '&', '&'), { foo: '', bar: '' }); diff --git a/cli/tests/node_compat/test/parallel/test-readline-csi.js b/cli/tests/node_compat/test/parallel/test-readline-csi.js new file mode 100644 index 00000000000000..e9a87b138f5937 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-csi.js @@ -0,0 +1,183 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const { Writable } = require('stream'); +const { CSI } = require('internal/readline/utils'); + +{ + assert(CSI); + assert.strictEqual(CSI.kClearToLineBeginning, '\x1b[1K'); + assert.strictEqual(CSI.kClearToLineEnd, '\x1b[0K'); + assert.strictEqual(CSI.kClearLine, '\x1b[2K'); + assert.strictEqual(CSI.kClearScreenDown, '\x1b[0J'); + assert.strictEqual(CSI`1${2}3`, '\x1b[123'); +} + +class TestWritable extends Writable { + constructor() { + super(); + this.data = ''; + } + _write(chunk, encoding, callback) { + this.data += chunk.toString(); + callback(); + } +} + +const writable = new TestWritable(); + +assert.strictEqual(readline.clearScreenDown(writable), true); +assert.deepStrictEqual(writable.data, CSI.kClearScreenDown); +assert.strictEqual(readline.clearScreenDown(writable, common.mustCall()), true); + +// Verify that clearScreenDown() throws on invalid callback. +assert.throws(() => { + readline.clearScreenDown(writable, null); +}, /ERR_INVALID_ARG_TYPE/); + +// Verify that clearScreenDown() does not throw on null or undefined stream. +assert.strictEqual(readline.clearScreenDown(null, common.mustCall((err) => { + assert.strictEqual(err, null); +})), true); +assert.strictEqual(readline.clearScreenDown(undefined, common.mustCall()), + true); + +writable.data = ''; +assert.strictEqual(readline.clearLine(writable, -1), true); +assert.deepStrictEqual(writable.data, CSI.kClearToLineBeginning); + +writable.data = ''; +assert.strictEqual(readline.clearLine(writable, 1), true); +assert.deepStrictEqual(writable.data, CSI.kClearToLineEnd); + +writable.data = ''; +assert.strictEqual(readline.clearLine(writable, 0), true); +assert.deepStrictEqual(writable.data, CSI.kClearLine); + +writable.data = ''; +assert.strictEqual(readline.clearLine(writable, -1, common.mustCall()), true); +assert.deepStrictEqual(writable.data, CSI.kClearToLineBeginning); + +// Verify that clearLine() throws on invalid callback. +assert.throws(() => { + readline.clearLine(writable, 0, null); +}, /ERR_INVALID_ARG_TYPE/); + +// Verify that clearLine() does not throw on null or undefined stream. +assert.strictEqual(readline.clearLine(null, 0), true); +assert.strictEqual(readline.clearLine(undefined, 0), true); +assert.strictEqual(readline.clearLine(null, 0, common.mustCall((err) => { + assert.strictEqual(err, null); +})), true); +assert.strictEqual(readline.clearLine(undefined, 0, common.mustCall()), true); + +// Nothing is written when moveCursor 0, 0 +[ + [0, 0, ''], + [1, 0, '\x1b[1C'], + [-1, 0, '\x1b[1D'], + [0, 1, '\x1b[1B'], + [0, -1, '\x1b[1A'], + [1, 1, '\x1b[1C\x1b[1B'], + [-1, 1, '\x1b[1D\x1b[1B'], + [-1, -1, '\x1b[1D\x1b[1A'], + [1, -1, '\x1b[1C\x1b[1A'], +].forEach((set) => { + writable.data = ''; + assert.strictEqual(readline.moveCursor(writable, set[0], set[1]), true); + assert.deepStrictEqual(writable.data, set[2]); + writable.data = ''; + assert.strictEqual( + readline.moveCursor(writable, set[0], set[1], common.mustCall()), + true + ); + assert.deepStrictEqual(writable.data, set[2]); +}); + +// Verify that moveCursor() throws on invalid callback. +assert.throws(() => { + readline.moveCursor(writable, 1, 1, null); +}, /ERR_INVALID_ARG_TYPE/); + +// Verify that moveCursor() does not throw on null or undefined stream. +assert.strictEqual(readline.moveCursor(null, 1, 1), true); +assert.strictEqual(readline.moveCursor(undefined, 1, 1), true); +assert.strictEqual(readline.moveCursor(null, 1, 1, common.mustCall((err) => { + assert.strictEqual(err, null); +})), true); +assert.strictEqual(readline.moveCursor(undefined, 1, 1, common.mustCall()), + true); + +// Undefined or null as stream should not throw. +assert.strictEqual(readline.cursorTo(null), true); +assert.strictEqual(readline.cursorTo(), true); +assert.strictEqual(readline.cursorTo(null, 1, 1, common.mustCall()), true); +assert.strictEqual(readline.cursorTo(undefined, 1, 1, common.mustCall((err) => { + assert.strictEqual(err, null); +})), true); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 'a'), true); +assert.strictEqual(writable.data, ''); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 'a', 'b'), true); +assert.strictEqual(writable.data, ''); + +writable.data = ''; +assert.throws( + () => readline.cursorTo(writable, 'a', 1), + { + name: 'TypeError', + code: 'ERR_INVALID_CURSOR_POS', + message: 'Cannot set cursor row without setting its column' + }); +assert.strictEqual(writable.data, ''); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 1, 'a'), true); +assert.strictEqual(writable.data, '\x1b[2G'); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 1), true); +assert.strictEqual(writable.data, '\x1b[2G'); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 1, 2), true); +assert.strictEqual(writable.data, '\x1b[3;2H'); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 1, 2, common.mustCall()), true); +assert.strictEqual(writable.data, '\x1b[3;2H'); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 1, common.mustCall()), true); +assert.strictEqual(writable.data, '\x1b[2G'); + +// Verify that cursorTo() throws on invalid callback. +assert.throws(() => { + readline.cursorTo(writable, 1, 1, null); +}, /ERR_INVALID_ARG_TYPE/); + +// Verify that cursorTo() throws if x or y is NaN. +assert.throws(() => { + readline.cursorTo(writable, NaN); +}, /ERR_INVALID_ARG_VALUE/); + +assert.throws(() => { + readline.cursorTo(writable, 1, NaN); +}, /ERR_INVALID_ARG_VALUE/); + +assert.throws(() => { + readline.cursorTo(writable, NaN, NaN); +}, /ERR_INVALID_ARG_VALUE/); diff --git a/cli/tests/node_compat/test/parallel/test-readline-emit-keypress-events.js b/cli/tests/node_compat/test/parallel/test-readline-emit-keypress-events.js new file mode 100644 index 00000000000000..ad001d603e8282 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-emit-keypress-events.js @@ -0,0 +1,79 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// emitKeypressEvents is thoroughly tested in test-readline-keys.js. +// However, that test calls it implicitly. This is just a quick sanity check +// to verify that it works when called explicitly. + +require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const PassThrough = require('stream').PassThrough; + +const expectedSequence = ['f', 'o', 'o']; +const expectedKeys = [ + { sequence: 'f', name: 'f', ctrl: false, meta: false, shift: false }, + { sequence: 'o', name: 'o', ctrl: false, meta: false, shift: false }, + { sequence: 'o', name: 'o', ctrl: false, meta: false, shift: false }, +]; + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + + readline.emitKeypressEvents(stream); + stream.on('keypress', (s, k) => { + sequence.push(s); + keys.push(k); + }); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + + stream.on('keypress', (s, k) => { + sequence.push(s); + keys.push(k); + }); + readline.emitKeypressEvents(stream); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + const keypressListener = (s, k) => { + sequence.push(s); + keys.push(k); + }; + + stream.on('keypress', keypressListener); + readline.emitKeypressEvents(stream); + stream.removeListener('keypress', keypressListener); + stream.write('foo'); + + assert.deepStrictEqual(sequence, []); + assert.deepStrictEqual(keys, []); + + stream.on('keypress', keypressListener); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} diff --git a/cli/tests/node_compat/test/parallel/test-readline-interface-escapecodetimeout.js b/cli/tests/node_compat/test/parallel/test-readline-interface-escapecodetimeout.js new file mode 100644 index 00000000000000..3a0da7ccb4bdf8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-interface-escapecodetimeout.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test ensures that the escapeCodeTimeout option set correctly + +const assert = require('assert'); +const readline = require('readline'); +const EventEmitter = require('events').EventEmitter; + +class FakeInput extends EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: 50 + }); + assert.strictEqual(rli.escapeCodeTimeout, 50); + rli.close(); +} + +[ + null, + {}, + NaN, + '50', +].forEach((invalidInput) => { + assert.throws(() => { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: invalidInput + }); + rli.close(); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE' + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-readline-interface.js b/cli/tests/node_compat/test/parallel/test-readline-interface.js new file mode 100644 index 00000000000000..e8e48dd1ef8d9b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-interface.js @@ -0,0 +1,1217 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +common.skipIfDumbTerminal(); + +const assert = require('assert'); +const readline = require('readline'); +const util = require('util'); +const { + getStringWidth, + stripVTControlCharacters +} = require('internal/util/inspect'); +const { EventEmitter, getEventListeners } = require('events'); +const { Writable, Readable } = require('stream'); + +class FakeInput extends EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +function isWarned(emitter) { + for (const name in emitter) { + const listeners = emitter[name]; + if (listeners.warned) return true; + } + return false; +} + +function getInterface(options) { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + ...options, + }); + return [rli, fi]; +} + +function assertCursorRowsAndCols(rli, rows, cols) { + const cursorPos = rli.getCursorPos(); + assert.strictEqual(cursorPos.rows, rows); + assert.strictEqual(cursorPos.cols, cols); +} + +{ + const input = new FakeInput(); + const rl = readline.Interface({ input }); + assert(rl instanceof readline.Interface); +} + +[ + undefined, + 50, + 0, + 100.5, + 5000, +].forEach((crlfDelay) => { + const [rli] = getInterface({ crlfDelay }); + assert.strictEqual(rli.crlfDelay, Math.max(crlfDelay || 100, 100)); + rli.close(); +}); + +{ + const input = new FakeInput(); + + // Constructor throws if completer is not a function or undefined + ['not an array', 123, 123n, {}, true, Symbol(), null].forEach((invalid) => { + assert.throws(() => { + readline.createInterface({ + input, + completer: invalid + }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + // Constructor throws if history is not an array + ['not an array', 123, 123n, {}, true, Symbol(), null].forEach((history) => { + assert.throws(() => { + readline.createInterface({ + input, + history, + }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + // Constructor throws if historySize is not a positive number + ['not a number', -1, NaN, {}, true, Symbol(), null].forEach((historySize) => { + assert.throws(() => { + readline.createInterface({ + input, + historySize, + }); + }, { + name: 'RangeError', + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + // Check for invalid tab sizes. + assert.throws( + () => new readline.Interface({ + input, + tabSize: 0 + }), + { + message: 'The value of "tabSize" is out of range. ' + + 'It must be >= 1 && < 4294967296. Received 0', + code: 'ERR_OUT_OF_RANGE' + } + ); + + assert.throws( + () => new readline.Interface({ + input, + tabSize: '4' + }), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + + assert.throws( + () => new readline.Interface({ + input, + tabSize: 4.5 + }), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "tabSize" is out of range. ' + + 'It must be an integer. Received 4.5' + } + ); +} + +// Sending a single character with no newline +{ + const fi = new FakeInput(); + const rli = new readline.Interface(fi, {}); + rli.on('line', common.mustNotCall()); + fi.emit('data', 'a'); + rli.close(); +} + +// Sending multiple newlines at once that does not end with a new line and a +// `end` event(last line is). \r should behave like \n when alone. +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + fi.emit('data', expectedLines.join('\r')); + rli.close(); +} + +// \r at start of input should output blank line +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLines = ['', 'foo' ]; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length)); + fi.emit('data', '\rfoo\r'); + rli.close(); +} + +// \t does not become part of the input when there is a completer function +{ + const completer = (line) => [[], line]; + const [rli, fi] = getInterface({ terminal: true, completer }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'foo'); + })); + for (const character of '\tfo\to\t') { + fi.emit('data', character); + } + fi.emit('data', '\n'); + rli.close(); +} + +// \t when there is no completer function should behave like an ordinary +// character +{ + const [rli, fi] = getInterface({ terminal: true }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '\t'); + })); + fi.emit('data', '\t'); + fi.emit('data', '\n'); + rli.close(); +} + +// Adding history lines should emit the history event with +// the history array +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('history', common.mustCall((history) => { + const expectedHistory = expectedLines.slice(0, history.length).reverse(); + assert.deepStrictEqual(history, expectedHistory); + }, expectedLines.length)); + for (const line of expectedLines) { + fi.emit('data', `${line}\n`); + } + rli.close(); +} + +// Altering the history array in the listener should not alter +// the line being processed +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLine = 'foo'; + rli.on('history', common.mustCall((history) => { + assert.strictEqual(history[0], expectedLine); + history.shift(); + })); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLine); + assert.strictEqual(rli.history.length, 0); + })); + fi.emit('data', `${expectedLine}\n`); + rli.close(); +} + +// Duplicate lines are removed from history when +// `options.removeHistoryDuplicates` is `true` +{ + const [rli, fi] = getInterface({ + terminal: true, + removeHistoryDuplicates: true + }); + const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat']; + // ['foo', 'baz', 'bar', bat']; + let callCount = 0; + rli.on('line', (line) => { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + fi.emit('data', `${expectedLines.join('\n')}\n`); + assert.strictEqual(callCount, expectedLines.length); + fi.emit('keypress', '.', { name: 'up' }); // 'bat' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.notStrictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'baz' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'foo' + assert.notStrictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(callCount, 0); + fi.emit('keypress', '.', { name: 'down' }); // 'baz' + assert.strictEqual(rli.line, 'baz'); + assert.strictEqual(rli.historyIndex, 2); + fi.emit('keypress', '.', { name: 'n', ctrl: true }); // 'bar' + assert.strictEqual(rli.line, 'bar'); + assert.strictEqual(rli.historyIndex, 1); + fi.emit('keypress', '.', { name: 'n', ctrl: true }); + assert.strictEqual(rli.line, 'bat'); + assert.strictEqual(rli.historyIndex, 0); + // Activate the substring history search. + fi.emit('keypress', '.', { name: 'down' }); // 'bat' + assert.strictEqual(rli.line, 'bat'); + assert.strictEqual(rli.historyIndex, -1); + // Deactivate substring history search. + fi.emit('keypress', '.', { name: 'backspace' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + // Activate the substring history search. + fi.emit('keypress', '.', { name: 'down' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + fi.emit('keypress', '.', { name: 'down' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + fi.emit('keypress', '.', { name: 'up' }); // 'bat' + assert.strictEqual(rli.historyIndex, 0); + assert.strictEqual(rli.line, 'bat'); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.strictEqual(rli.historyIndex, 1); + assert.strictEqual(rli.line, 'bar'); + fi.emit('keypress', '.', { name: 'up' }); // 'baz' + assert.strictEqual(rli.historyIndex, 2); + assert.strictEqual(rli.line, 'baz'); + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 4); + assert.strictEqual(rli.line, 'ba'); + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 4); + assert.strictEqual(rli.line, 'ba'); + // Deactivate substring history search and reset history index. + fi.emit('keypress', '.', { name: 'right' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + // Substring history search activated. + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 0); + assert.strictEqual(rli.line, 'bat'); + rli.close(); +} + +// Duplicate lines are not removed from history when +// `options.removeHistoryDuplicates` is `false` +{ + const [rli, fi] = getInterface({ + terminal: true, + removeHistoryDuplicates: false + }); + const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat']; + let callCount = 0; + rli.on('line', (line) => { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + fi.emit('data', `${expectedLines.join('\n')}\n`); + assert.strictEqual(callCount, expectedLines.length); + fi.emit('keypress', '.', { name: 'up' }); // 'bat' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.notStrictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'baz' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'foo' + assert.strictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(callCount, 0); + rli.close(); +} + +// Regression test for repl freeze, #1968: +// check that nothing fails if 'keypress' event throws. +{ + const [rli, fi] = getInterface({ terminal: true }); + const keys = []; + const err = new Error('bad thing happened'); + fi.on('keypress', (key) => { + keys.push(key); + if (key === 'X') { + throw err; + } + }); + assert.throws( + () => fi.emit('data', 'fooX'), + (e) => { + assert.strictEqual(e, err); + return true; + } + ); + fi.emit('data', 'bar'); + assert.strictEqual(keys.join(''), 'fooXbar'); + rli.close(); +} + +// History is bound +{ + const [rli, fi] = getInterface({ terminal: true, historySize: 2 }); + const lines = ['line 1', 'line 2', 'line 3']; + fi.emit('data', lines.join('\n') + '\n'); + assert.strictEqual(rli.history.length, 2); + assert.strictEqual(rli.history[0], 'line 3'); + assert.strictEqual(rli.history[1], 'line 2'); +} + +// Question +{ + const [rli] = getInterface({ terminal: true }); + const expectedLines = ['foo']; + rli.question(expectedLines[0], () => rli.close()); + assertCursorRowsAndCols(rli, 0, expectedLines[0].length); + rli.close(); +} + +// Sending a multi-line question +{ + const [rli] = getInterface({ terminal: true }); + const expectedLines = ['foo', 'bar']; + rli.question(expectedLines.join('\n'), () => rli.close()); + assertCursorRowsAndCols( + rli, expectedLines.length - 1, expectedLines.slice(-1)[0].length); + rli.close(); +} + +{ + // Beginning and end of line + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + fi.emit('keypress', '.', { ctrl: true, name: 'e' }); + assertCursorRowsAndCols(rli, 0, 19); + rli.close(); +} + +{ + // Back and Forward one character + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + assertCursorRowsAndCols(rli, 0, 19); + + // Back one character + fi.emit('keypress', '.', { ctrl: true, name: 'b' }); + assertCursorRowsAndCols(rli, 0, 18); + // Back one character + fi.emit('keypress', '.', { ctrl: true, name: 'b' }); + assertCursorRowsAndCols(rli, 0, 17); + // Forward one character + fi.emit('keypress', '.', { ctrl: true, name: 'f' }); + assertCursorRowsAndCols(rli, 0, 18); + // Forward one character + fi.emit('keypress', '.', { ctrl: true, name: 'f' }); + assertCursorRowsAndCols(rli, 0, 19); + rli.close(); +} + +// Back and Forward one astral character +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Move left one character/code point + fi.emit('keypress', '.', { name: 'left' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Move right one character/code point + fi.emit('keypress', '.', { name: 'right' }); + assertCursorRowsAndCols(rli, 0, 2); + + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '💻'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Two astral characters left +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Move left one character/code point + fi.emit('keypress', '.', { name: 'left' }); + assertCursorRowsAndCols(rli, 0, 0); + + fi.emit('data', '🐕'); + assertCursorRowsAndCols(rli, 0, 2); + + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '🐕💻'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Two astral characters right +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Move left one character/code point + fi.emit('keypress', '.', { name: 'right' }); + assertCursorRowsAndCols(rli, 0, 2); + + fi.emit('data', '🐕'); + assertCursorRowsAndCols(rli, 0, 4); + + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '💻🐕'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +{ + // `wordLeft` and `wordRight` + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + assertCursorRowsAndCols(rli, 0, 16); + fi.emit('keypress', '.', { meta: true, name: 'b' }); + assertCursorRowsAndCols(rli, 0, 10); + fi.emit('keypress', '.', { ctrl: true, name: 'right' }); + assertCursorRowsAndCols(rli, 0, 16); + fi.emit('keypress', '.', { meta: true, name: 'f' }); + assertCursorRowsAndCols(rli, 0, 19); + rli.close(); +} + +// `deleteWordLeft` +[ + { ctrl: true, name: 'w' }, + { ctrl: true, name: 'backspace' }, + { meta: true, name: 'backspace' }, +].forEach((deleteWordLeftKey) => { + let [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick fox'); + })); + fi.emit('keypress', '.', deleteWordLeftKey); + fi.emit('data', '\n'); + rli.close(); + + // No effect if pressed at beginning of line + [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick brown fox'); + })); + fi.emit('keypress', '.', deleteWordLeftKey); + fi.emit('data', '\n'); + rli.close(); +}); + +// `deleteWordRight` +[ + { ctrl: true, name: 'delete' }, + { meta: true, name: 'delete' }, + { meta: true, name: 'd' }, +].forEach((deleteWordRightKey) => { + let [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick fox'); + })); + fi.emit('keypress', '.', deleteWordRightKey); + fi.emit('data', '\n'); + rli.close(); + + // No effect if pressed at end of line + [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick brown fox'); + })); + fi.emit('keypress', '.', deleteWordRightKey); + fi.emit('data', '\n'); + rli.close(); +}); + +// deleteLeft +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + assertCursorRowsAndCols(rli, 0, 19); + + // Delete left character + fi.emit('keypress', '.', { ctrl: true, name: 'h' }); + assertCursorRowsAndCols(rli, 0, 18); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick brown fo'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteLeft astral character +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + assertCursorRowsAndCols(rli, 0, 2); + // Delete left character + fi.emit('keypress', '.', { ctrl: true, name: 'h' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteRight +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + + // Go to the start of the line + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Delete right character + fi.emit('keypress', '.', { ctrl: true, name: 'd' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'he quick brown fox'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteRight astral character +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Go to the start of the line + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Delete right character + fi.emit('keypress', '.', { ctrl: true, name: 'd' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteLineLeft +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + assertCursorRowsAndCols(rli, 0, 19); + + // Delete from current to start of line + fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'backspace' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteLineRight +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + + // Go to the start of the line + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Delete from current to end of line + fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Close readline interface +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('keypress', '.', { ctrl: true, name: 'c' }); + assert(rli.closed); +} + +// Multi-line input cursor position +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.columns = 10; + fi.emit('data', 'multi-line text'); + assertCursorRowsAndCols(rli, 1, 5); + rli.close(); +} + +// Multi-line input cursor position and long tabs +{ + const [rli, fi] = getInterface({ tabSize: 16, terminal: true, prompt: '' }); + fi.columns = 10; + fi.emit('data', 'multi-line\ttext \t'); + assert.strictEqual(rli.cursor, 17); + assertCursorRowsAndCols(rli, 3, 2); + rli.close(); +} + +// Check for the default tab size. +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick\tbrown\tfox'); + assert.strictEqual(rli.cursor, 19); + // The first tab is 7 spaces long, the second one 3 spaces. + assertCursorRowsAndCols(rli, 0, 27); +} + +// Multi-line prompt cursor position +{ + const [rli, fi] = getInterface({ + terminal: true, + prompt: '\nfilledline\nwraping text\n> ' + }); + fi.columns = 10; + fi.emit('data', 't'); + assertCursorRowsAndCols(rli, 4, 3); + rli.close(); +} + +// Clear the whole screen +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + const lines = ['line 1', 'line 2', 'line 3']; + fi.emit('data', lines.join('\n')); + fi.emit('keypress', '.', { ctrl: true, name: 'l' }); + assertCursorRowsAndCols(rli, 0, 6); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'line 3'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Wide characters should be treated as two columns. +assert.strictEqual(getStringWidth('a'), 1); +assert.strictEqual(getStringWidth('あ'), 2); +assert.strictEqual(getStringWidth('谢'), 2); +assert.strictEqual(getStringWidth('고'), 2); +assert.strictEqual(getStringWidth(String.fromCodePoint(0x1f251)), 2); +assert.strictEqual(getStringWidth('abcde'), 5); +assert.strictEqual(getStringWidth('古池や'), 6); +assert.strictEqual(getStringWidth('ノード.js'), 9); +assert.strictEqual(getStringWidth('你好'), 4); +assert.strictEqual(getStringWidth('안녕하세요'), 10); +assert.strictEqual(getStringWidth('A\ud83c\ude00BC'), 5); +assert.strictEqual(getStringWidth('👨‍👩‍👦‍👦'), 8); +assert.strictEqual(getStringWidth('🐕𐐷あ💻😀'), 9); +// TODO(BridgeAR): This should have a width of 4. +assert.strictEqual(getStringWidth('⓬⓪'), 2); +assert.strictEqual(getStringWidth('\u0301\u200D\u200E'), 0); + +// Check if vt control chars are stripped +assert.strictEqual(stripVTControlCharacters('\u001b[31m> \u001b[39m'), '> '); +assert.strictEqual( + stripVTControlCharacters('\u001b[31m> \u001b[39m> '), + '> > ' +); +assert.strictEqual(stripVTControlCharacters('\u001b[31m\u001b[39m'), ''); +assert.strictEqual(stripVTControlCharacters('> '), '> '); +assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m'), 2); +assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m> '), 4); +assert.strictEqual(getStringWidth('\u001b[31m\u001b[39m'), 0); +assert.strictEqual(getStringWidth('> '), 2); + +// FIXME(bartlomieju): this causes hang +// Check EventEmitter memory leak +// for (let i = 0; i < 12; i++) { +// const rl = readline.createInterface({ +// input: process.stdin, +// output: process.stdout +// }); +// rl.close(); +// assert.strictEqual(isWarned(process.stdin._events), false); +// assert.strictEqual(isWarned(process.stdout._events), false); +// } + +[true, false].forEach((terminal) => { + // Disable history + { + const [rli, fi] = getInterface({ terminal, historySize: 0 }); + assert.strictEqual(rli.historySize, 0); + + fi.emit('data', 'asdf\n'); + assert.deepStrictEqual(rli.history, []); + rli.close(); + } + + // Default history size 30 + { + const [rli, fi] = getInterface({ terminal }); + assert.strictEqual(rli.historySize, 30); + + fi.emit('data', 'asdf\n'); + assert.deepStrictEqual(rli.history, terminal ? ['asdf'] : []); + rli.close(); + } + + // Sending a full line + { + const [rli, fi] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'asdf'); + })); + fi.emit('data', 'asdf\n'); + } + + // Sending a blank line + { + const [rli, fi] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + } + + // Sending a single character with no newline and then a newline + { + const [rli, fi] = getInterface({ terminal }); + let called = false; + rli.on('line', (line) => { + called = true; + assert.strictEqual(line, 'a'); + }); + fi.emit('data', 'a'); + assert.ok(!called); + fi.emit('data', '\n'); + assert.ok(called); + rli.close(); + } + + // Sending multiple newlines at once + { + const [rli, fi] = getInterface({ terminal }); + const expectedLines = ['foo', 'bar', 'baz']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length)); + fi.emit('data', `${expectedLines.join('\n')}\n`); + rli.close(); + } + + // Sending multiple newlines at once that does not end with a new line + { + const [rli, fi] = getInterface({ terminal }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + fi.emit('data', expectedLines.join('\n')); + rli.close(); + } + + // Sending multiple newlines at once that does not end with a new(empty) + // line and a `end` event + { + const [rli, fi] = getInterface({ terminal }); + const expectedLines = ['foo', 'bar', 'baz', '']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + rli.on('close', common.mustCall()); + fi.emit('data', expectedLines.join('\n')); + fi.emit('end'); + rli.close(); + } + + // Sending a multi-byte utf8 char over multiple writes + { + const buf = Buffer.from('☮', 'utf8'); + const [rli, fi] = getInterface({ terminal }); + let callCount = 0; + rli.on('line', (line) => { + callCount++; + assert.strictEqual(line, buf.toString('utf8')); + }); + for (const i of buf) { + fi.emit('data', Buffer.from([i])); + } + assert.strictEqual(callCount, 0); + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + } + + // Calling readline without `new` + { + const [rli, fi] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'asdf'); + })); + fi.emit('data', 'asdf\n'); + rli.close(); + } + + // Calling the question callback + { + const [rli] = getInterface({ terminal }); + rli.question('foo?', common.mustCall((answer) => { + assert.strictEqual(answer, 'bar'); + })); + rli.write('bar\n'); + rli.close(); + } + + // Calling the question multiple times + { + const [rli] = getInterface({ terminal }); + rli.question('foo?', common.mustCall((answer) => { + assert.strictEqual(answer, 'baz'); + })); + rli.question('bar?', common.mustNotCall(() => { + })); + rli.write('baz\n'); + rli.close(); + } + + // Calling the promisified question + { + const [rli] = getInterface({ terminal }); + const question = util.promisify(rli.question).bind(rli); + question('foo?') + .then(common.mustCall((answer) => { + assert.strictEqual(answer, 'bar'); + })); + rli.write('bar\n'); + rli.close(); + } + + // Aborting a question + { + const ac = new AbortController(); + const signal = ac.signal; + const [rli] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'bar'); + })); + rli.question('hello?', { signal }, common.mustNotCall()); + ac.abort(); + rli.write('bar\n'); + rli.close(); + } + + // Aborting a promisified question + { + const ac = new AbortController(); + const signal = ac.signal; + const [rli] = getInterface({ terminal }); + const question = util.promisify(rli.question).bind(rli); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'bar'); + })); + question('hello?', { signal }) + .then(common.mustNotCall()) + .catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })); + ac.abort(); + rli.write('bar\n'); + rli.close(); + } + + // pre-aborted signal + { + const signal = AbortSignal.abort(); + const [rli] = getInterface({ terminal }); + rli.pause(); + rli.on('resume', common.mustNotCall()); + rli.question('hello?', { signal }, common.mustNotCall()); + rli.close(); + } + + // pre-aborted signal promisified question + { + const signal = AbortSignal.abort(); + const [rli] = getInterface({ terminal }); + const question = util.promisify(rli.question).bind(rli); + rli.on('resume', common.mustNotCall()); + rli.pause(); + question('hello?', { signal }) + .then(common.mustNotCall()) + .catch(common.mustCall((error) => { + assert.strictEqual(error.name, 'AbortError'); + })); + rli.close(); + } + + // Can create a new readline Interface with a null output argument + { + const [rli, fi] = getInterface({ output: null, terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'asdf'); + })); + fi.emit('data', 'asdf\n'); + + rli.setPrompt('ddd> '); + rli.prompt(); + rli.write("really shouldn't be seeing this"); + rli.question('What do you think of node.js? ', (answer) => { + console.log('Thank you for your valuable feedback:', answer); + rli.close(); + }); + } + + // Calling the getPrompt method + { + const expectedPrompts = ['$ ', '> ']; + const [rli] = getInterface({ terminal }); + for (const prompt of expectedPrompts) { + rli.setPrompt(prompt); + assert.strictEqual(rli.getPrompt(), prompt); + } + } + + { + const expected = terminal ? + ['\u001b[1G', '\u001b[0J', '$ ', '\u001b[3G'] : + ['$ ']; + + const output = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + assert.strictEqual(chunk.toString(), expected.shift()); + cb(); + rl.close(); + }, expected.length) + }); + + const rl = readline.createInterface({ + input: new Readable({ read: common.mustCall() }), + output, + prompt: '$ ', + terminal + }); + + rl.prompt(); + + assert.strictEqual(rl.getPrompt(), '$ '); + } + + { + const fi = new FakeInput(); + assert.deepStrictEqual(fi.listeners(terminal ? 'keypress' : 'data'), []); + } + + // Emit two line events when the delay + // between \r and \n exceeds crlfDelay + { + const crlfDelay = 200; + const [rli, fi] = getInterface({ terminal, crlfDelay }); + let callCount = 0; + rli.on('line', () => { + callCount++; + }); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 2); + rli.close(); + }), crlfDelay + 10); + } + + // For the purposes of the following tests, we do not care about the exact + // value of crlfDelay, only that the behaviour conforms to what's expected. + // Setting it to Infinity allows the test to succeed even under extreme + // CPU stress. + const crlfDelay = Infinity; + + // Set crlfDelay to `Infinity` is allowed + { + const delay = 200; + const [rli, fi] = getInterface({ terminal, crlfDelay }); + let callCount = 0; + rli.on('line', () => { + callCount++; + }); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + }), delay); + } + + // Sending multiple newlines at once that does not end with a new line + // and a `end` event(last line is) + + // \r\n should emit one line event, not two + { + const [rli, fi] = getInterface({ terminal, crlfDelay }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + fi.emit('data', expectedLines.join('\r\n')); + rli.close(); + } + + // \r\n should emit one line event when split across multiple writes. + { + const [rli, fi] = getInterface({ terminal, crlfDelay }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + let callCount = 0; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }, expectedLines.length)); + expectedLines.forEach((line) => { + fi.emit('data', `${line}\r`); + fi.emit('data', '\n'); + }); + rli.close(); + } + + // Emit one line event when the delay between \r and \n is + // over the default crlfDelay but within the setting value. + { + const delay = 125; + const [rli, fi] = getInterface({ terminal, crlfDelay }); + let callCount = 0; + rli.on('line', () => callCount++); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + }), delay); + } +}); + +// Ensure that the _wordLeft method works even for large input +{ + const input = new Readable({ + read() { + this.push('\x1B[1;5D'); // CTRL + Left + this.push(null); + }, + }); + const output = new Writable({ + write: common.mustCall((data, encoding, cb) => { + assert.strictEqual(rl.cursor, rl.line.length - 1); + cb(); + }), + }); + const rl = new readline.createInterface({ + input, + output, + terminal: true, + }); + rl.line = `a${' '.repeat(1e6)}a`; + rl.cursor = rl.line.length; +} + +// FIXME(bartlomieju): these tests depend on "event_target" module +// { +// const fi = new FakeInput(); +// const signal = AbortSignal.abort(); + +// const rl = readline.createInterface({ +// input: fi, +// output: fi, +// signal, +// }); +// rl.on('close', common.mustCall()); +// assert.strictEqual(getEventListeners(signal, 'abort').length, 0); +// } + +// { +// const fi = new FakeInput(); +// const ac = new AbortController(); +// const { signal } = ac; +// const rl = readline.createInterface({ +// input: fi, +// output: fi, +// signal, +// }); +// assert.strictEqual(getEventListeners(signal, 'abort').length, 1); +// rl.on('close', common.mustCall()); +// ac.abort(); +// assert.strictEqual(getEventListeners(signal, 'abort').length, 0); +// } + +// { +// const fi = new FakeInput(); +// const ac = new AbortController(); +// const { signal } = ac; +// const rl = readline.createInterface({ +// input: fi, +// output: fi, +// signal, +// }); +// assert.strictEqual(getEventListeners(signal, 'abort').length, 1); +// rl.close(); +// assert.strictEqual(getEventListeners(signal, 'abort').length, 0); +// } + +{ + // Constructor throws if signal is not an abort signal + assert.throws(() => { + readline.createInterface({ + input: new FakeInput(), + signal: {}, + }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-readline-keys.js b/cli/tests/node_compat/test/parallel/test-readline-keys.js new file mode 100644 index 00000000000000..e6c2e4e77822b3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-keys.js @@ -0,0 +1,351 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const PassThrough = require('stream').PassThrough; +const assert = require('assert'); +const Interface = require('readline').Interface; + +class FakeInput extends PassThrough {} + +function extend(k) { + return Object.assign({ ctrl: false, meta: false, shift: false }, k); +} + + +const fi = new FakeInput(); +const fo = new FakeInput(); +new Interface({ input: fi, output: fo, terminal: true }); + +let keys = []; +fi.on('keypress', (s, k) => { + keys.push(k); +}); + + +function addTest(sequences, expectedKeys) { + if (!Array.isArray(sequences)) { + sequences = [ sequences ]; + } + + if (!Array.isArray(expectedKeys)) { + expectedKeys = [ expectedKeys ]; + } + + expectedKeys = expectedKeys.map(extend); + + keys = []; + + sequences.forEach((sequence) => { + fi.write(sequence); + }); + assert.deepStrictEqual(keys, expectedKeys); +} + +// Simulate key interval test cases +// Returns a function that takes `next` test case and returns a thunk +// that can be called to run tests in sequence +// e.g. +// addKeyIntervalTest(..) +// (addKeyIntervalTest(..) +// (addKeyIntervalTest(..)(noop)))() +// where noop is a terminal function(() => {}). + +const addKeyIntervalTest = (sequences, expectedKeys, interval = 550, + assertDelay = 550) => { + const fn = common.mustCall((next) => () => { + + if (!Array.isArray(sequences)) { + sequences = [ sequences ]; + } + + if (!Array.isArray(expectedKeys)) { + expectedKeys = [ expectedKeys ]; + } + + expectedKeys = expectedKeys.map(extend); + + const keys = []; + fi.on('keypress', (s, k) => keys.push(k)); + + const emitKeys = ([head, ...tail]) => { + if (head) { + fi.write(head); + setTimeout(() => emitKeys(tail), interval); + } else { + setTimeout(() => { + next(); + assert.deepStrictEqual(keys, expectedKeys); + }, assertDelay); + } + }; + emitKeys(sequences); + }); + return fn; +}; + +// Regular alphanumerics +addTest('io.JS', [ + { name: 'i', sequence: 'i' }, + { name: 'o', sequence: 'o' }, + { name: undefined, sequence: '.' }, + { name: 'j', sequence: 'J', shift: true }, + { name: 's', sequence: 'S', shift: true }, +]); + +// Named characters +addTest('\n\r\t\x1b\n\x1b\r\x1b\t', [ + { name: 'enter', sequence: '\n' }, + { name: 'return', sequence: '\r' }, + { name: 'tab', sequence: '\t' }, + { name: 'enter', sequence: '\x1b\n', meta: true }, + { name: 'return', sequence: '\x1b\r', meta: true }, + { name: 'tab', sequence: '\x1b\t', meta: true }, +]); + +// Space and backspace +addTest('\b\x7f\x1b\b\x1b\x7f\x1b\x1b \x1b ', [ + { name: 'backspace', sequence: '\b' }, + { name: 'backspace', sequence: '\x7f' }, + { name: 'backspace', sequence: '\x1b\b', meta: true }, + { name: 'backspace', sequence: '\x1b\x7f', meta: true }, + { name: 'space', sequence: '\x1b\x1b ', meta: true }, + { name: 'space', sequence: ' ' }, + { name: 'space', sequence: '\x1b ', meta: true }, +]); + +// Escape key +addTest('\x1b\x1b\x1b', [ + { name: 'escape', sequence: '\x1b\x1b\x1b', meta: true }, +]); + +// Escape sequence +addTest('\x1b]', [{ name: undefined, sequence: '\x1B]', meta: true }]); + +// Control keys +addTest('\x01\x0b\x10', [ + { name: 'a', sequence: '\x01', ctrl: true }, + { name: 'k', sequence: '\x0b', ctrl: true }, + { name: 'p', sequence: '\x10', ctrl: true }, +]); + +// Alt keys +addTest('a\x1baA\x1bA', [ + { name: 'a', sequence: 'a' }, + { name: 'a', sequence: '\x1ba', meta: true }, + { name: 'a', sequence: 'A', shift: true }, + { name: 'a', sequence: '\x1bA', meta: true, shift: true }, +]); + +// xterm/gnome ESC [ letter (with modifiers) +addTest('\x1b[2P\x1b[3P\x1b[4P\x1b[5P\x1b[6P\x1b[7P\x1b[8P\x1b[3Q\x1b[8Q\x1b[3R\x1b[8R\x1b[3S\x1b[8S', [ + { name: 'f1', sequence: '\x1b[2P', code: '[P', shift: true, meta: false, ctrl: false }, + { name: 'f1', sequence: '\x1b[3P', code: '[P', shift: false, meta: true, ctrl: false }, + { name: 'f1', sequence: '\x1b[4P', code: '[P', shift: true, meta: true, ctrl: false }, + { name: 'f1', sequence: '\x1b[5P', code: '[P', shift: false, meta: false, ctrl: true }, + { name: 'f1', sequence: '\x1b[6P', code: '[P', shift: true, meta: false, ctrl: true }, + { name: 'f1', sequence: '\x1b[7P', code: '[P', shift: false, meta: true, ctrl: true }, + { name: 'f1', sequence: '\x1b[8P', code: '[P', shift: true, meta: true, ctrl: true }, + { name: 'f2', sequence: '\x1b[3Q', code: '[Q', meta: true }, + { name: 'f2', sequence: '\x1b[8Q', code: '[Q', shift: true, meta: true, ctrl: true }, + { name: 'f3', sequence: '\x1b[3R', code: '[R', meta: true }, + { name: 'f3', sequence: '\x1b[8R', code: '[R', shift: true, meta: true, ctrl: true }, + { name: 'f4', sequence: '\x1b[3S', code: '[S', meta: true }, + { name: 'f4', sequence: '\x1b[8S', code: '[S', shift: true, meta: true, ctrl: true }, +]); + +// xterm/gnome ESC O letter +addTest('\x1bOP\x1bOQ\x1bOR\x1bOS', [ + { name: 'f1', sequence: '\x1bOP', code: 'OP' }, + { name: 'f2', sequence: '\x1bOQ', code: 'OQ' }, + { name: 'f3', sequence: '\x1bOR', code: 'OR' }, + { name: 'f4', sequence: '\x1bOS', code: 'OS' }, +]); + +// xterm/rxvt ESC [ number ~ */ +addTest('\x1b[11~\x1b[12~\x1b[13~\x1b[14~', [ + { name: 'f1', sequence: '\x1b[11~', code: '[11~' }, + { name: 'f2', sequence: '\x1b[12~', code: '[12~' }, + { name: 'f3', sequence: '\x1b[13~', code: '[13~' }, + { name: 'f4', sequence: '\x1b[14~', code: '[14~' }, +]); + +// From Cygwin and used in libuv +addTest('\x1b[[A\x1b[[B\x1b[[C\x1b[[D\x1b[[E', [ + { name: 'f1', sequence: '\x1b[[A', code: '[[A' }, + { name: 'f2', sequence: '\x1b[[B', code: '[[B' }, + { name: 'f3', sequence: '\x1b[[C', code: '[[C' }, + { name: 'f4', sequence: '\x1b[[D', code: '[[D' }, + { name: 'f5', sequence: '\x1b[[E', code: '[[E' }, +]); + +// Common +addTest('\x1b[15~\x1b[17~\x1b[18~\x1b[19~\x1b[20~\x1b[21~\x1b[23~\x1b[24~', [ + { name: 'f5', sequence: '\x1b[15~', code: '[15~' }, + { name: 'f6', sequence: '\x1b[17~', code: '[17~' }, + { name: 'f7', sequence: '\x1b[18~', code: '[18~' }, + { name: 'f8', sequence: '\x1b[19~', code: '[19~' }, + { name: 'f9', sequence: '\x1b[20~', code: '[20~' }, + { name: 'f10', sequence: '\x1b[21~', code: '[21~' }, + { name: 'f11', sequence: '\x1b[23~', code: '[23~' }, + { name: 'f12', sequence: '\x1b[24~', code: '[24~' }, +]); + +// xterm ESC [ letter +addTest('\x1b[A\x1b[B\x1b[C\x1b[D\x1b[E\x1b[F\x1b[H', [ + { name: 'up', sequence: '\x1b[A', code: '[A' }, + { name: 'down', sequence: '\x1b[B', code: '[B' }, + { name: 'right', sequence: '\x1b[C', code: '[C' }, + { name: 'left', sequence: '\x1b[D', code: '[D' }, + { name: 'clear', sequence: '\x1b[E', code: '[E' }, + { name: 'end', sequence: '\x1b[F', code: '[F' }, + { name: 'home', sequence: '\x1b[H', code: '[H' }, +]); + +// xterm/gnome ESC O letter +addTest('\x1bOA\x1bOB\x1bOC\x1bOD\x1bOE\x1bOF\x1bOH', [ + { name: 'up', sequence: '\x1bOA', code: 'OA' }, + { name: 'down', sequence: '\x1bOB', code: 'OB' }, + { name: 'right', sequence: '\x1bOC', code: 'OC' }, + { name: 'left', sequence: '\x1bOD', code: 'OD' }, + { name: 'clear', sequence: '\x1bOE', code: 'OE' }, + { name: 'end', sequence: '\x1bOF', code: 'OF' }, + { name: 'home', sequence: '\x1bOH', code: 'OH' }, +]); + +// Old xterm shift-arrows +addTest('\x1bO2A\x1bO2B', [ + { name: 'up', sequence: '\x1bO2A', code: 'OA', shift: true }, + { name: 'down', sequence: '\x1bO2B', code: 'OB', shift: true }, +]); + +// xterm/rxvt ESC [ number ~ +addTest('\x1b[1~\x1b[2~\x1b[3~\x1b[4~\x1b[5~\x1b[6~', [ + { name: 'home', sequence: '\x1b[1~', code: '[1~' }, + { name: 'insert', sequence: '\x1b[2~', code: '[2~' }, + { name: 'delete', sequence: '\x1b[3~', code: '[3~' }, + { name: 'end', sequence: '\x1b[4~', code: '[4~' }, + { name: 'pageup', sequence: '\x1b[5~', code: '[5~' }, + { name: 'pagedown', sequence: '\x1b[6~', code: '[6~' }, +]); + +// putty +addTest('\x1b[[5~\x1b[[6~', [ + { name: 'pageup', sequence: '\x1b[[5~', code: '[[5~' }, + { name: 'pagedown', sequence: '\x1b[[6~', code: '[[6~' }, +]); + +// rxvt +addTest('\x1b[7~\x1b[8~', [ + { name: 'home', sequence: '\x1b[7~', code: '[7~' }, + { name: 'end', sequence: '\x1b[8~', code: '[8~' }, +]); + +// gnome terminal +addTest('\x1b[A\x1b[B\x1b[2A\x1b[2B', [ + { name: 'up', sequence: '\x1b[A', code: '[A' }, + { name: 'down', sequence: '\x1b[B', code: '[B' }, + { name: 'up', sequence: '\x1b[2A', code: '[A', shift: true }, + { name: 'down', sequence: '\x1b[2B', code: '[B', shift: true }, +]); + +// `rxvt` keys with modifiers. +addTest('\x1b[20~\x1b[2$\x1b[2^\x1b[3$\x1b[3^\x1b[5$\x1b[5^\x1b[6$\x1b[6^\x1b[7$\x1b[7^\x1b[8$\x1b[8^', [ + { name: 'f9', sequence: '\x1b[20~', code: '[20~' }, + { name: 'insert', sequence: '\x1b[2$', code: '[2$', shift: true }, + { name: 'insert', sequence: '\x1b[2^', code: '[2^', ctrl: true }, + { name: 'delete', sequence: '\x1b[3$', code: '[3$', shift: true }, + { name: 'delete', sequence: '\x1b[3^', code: '[3^', ctrl: true }, + { name: 'pageup', sequence: '\x1b[5$', code: '[5$', shift: true }, + { name: 'pageup', sequence: '\x1b[5^', code: '[5^', ctrl: true }, + { name: 'pagedown', sequence: '\x1b[6$', code: '[6$', shift: true }, + { name: 'pagedown', sequence: '\x1b[6^', code: '[6^', ctrl: true }, + { name: 'home', sequence: '\x1b[7$', code: '[7$', shift: true }, + { name: 'home', sequence: '\x1b[7^', code: '[7^', ctrl: true }, + { name: 'end', sequence: '\x1b[8$', code: '[8$', shift: true }, + { name: 'end', sequence: '\x1b[8^', code: '[8^', ctrl: true }, +]); + +// Misc +addTest('\x1b[Z', [ + { name: 'tab', sequence: '\x1b[Z', code: '[Z', shift: true }, +]); + +// xterm + modifiers +addTest('\x1b[20;5~\x1b[6;5^', [ + { name: 'f9', sequence: '\x1b[20;5~', code: '[20~', ctrl: true }, + { name: 'pagedown', sequence: '\x1b[6;5^', code: '[6^', ctrl: true }, +]); + +addTest('\x1b[H\x1b[5H\x1b[1;5H', [ + { name: 'home', sequence: '\x1b[H', code: '[H' }, + { name: 'home', sequence: '\x1b[5H', code: '[H', ctrl: true }, + { name: 'home', sequence: '\x1b[1;5H', code: '[H', ctrl: true }, +]); + +// Escape sequences broken into multiple data chunks +addTest('\x1b[D\x1b[C\x1b[D\x1b[C'.split(''), [ + { name: 'left', sequence: '\x1b[D', code: '[D' }, + { name: 'right', sequence: '\x1b[C', code: '[C' }, + { name: 'left', sequence: '\x1b[D', code: '[D' }, + { name: 'right', sequence: '\x1b[C', code: '[C' }, +]); + +// Escape sequences mixed with regular ones +addTest('\x1b[DD\x1b[2DD\x1b[2^D', [ + { name: 'left', sequence: '\x1b[D', code: '[D' }, + { name: 'd', sequence: 'D', shift: true }, + { name: 'left', sequence: '\x1b[2D', code: '[D', shift: true }, + { name: 'd', sequence: 'D', shift: true }, + { name: 'insert', sequence: '\x1b[2^', code: '[2^', ctrl: true }, + { name: 'd', sequence: 'D', shift: true }, +]); + +// Color sequences +addTest('\x1b[31ma\x1b[39ma', [ + { name: 'undefined', sequence: '\x1b[31m', code: '[31m' }, + { name: 'a', sequence: 'a' }, + { name: 'undefined', sequence: '\x1b[39m', code: '[39m' }, + { name: 'a', sequence: 'a' }, +]); + +// `rxvt` keys with modifiers. +addTest('\x1b[a\x1b[b\x1b[c\x1b[d\x1b[e', [ + { name: 'up', sequence: '\x1b[a', code: '[a', shift: true }, + { name: 'down', sequence: '\x1b[b', code: '[b', shift: true }, + { name: 'right', sequence: '\x1b[c', code: '[c', shift: true }, + { name: 'left', sequence: '\x1b[d', code: '[d', shift: true }, + { name: 'clear', sequence: '\x1b[e', code: '[e', shift: true }, +]); + +addTest('\x1bOa\x1bOb\x1bOc\x1bOd\x1bOe', [ + { name: 'up', sequence: '\x1bOa', code: 'Oa', ctrl: true }, + { name: 'down', sequence: '\x1bOb', code: 'Ob', ctrl: true }, + { name: 'right', sequence: '\x1bOc', code: 'Oc', ctrl: true }, + { name: 'left', sequence: '\x1bOd', code: 'Od', ctrl: true }, + { name: 'clear', sequence: '\x1bOe', code: 'Oe', ctrl: true }, +]); + +// Reduce array of addKeyIntervalTest(..) right to left +// with () => {} as initial function. +const runKeyIntervalTests = [ + // Escape character + addKeyIntervalTest('\x1b', [ + { name: 'escape', sequence: '\x1b', meta: true }, + ]), + // Chain of escape characters. + addKeyIntervalTest('\x1b\x1b\x1b\x1b'.split(''), [ + { name: 'escape', sequence: '\x1b', meta: true }, + { name: 'escape', sequence: '\x1b', meta: true }, + { name: 'escape', sequence: '\x1b', meta: true }, + { name: 'escape', sequence: '\x1b', meta: true }, + ]), +].reverse().reduce((acc, fn) => fn(acc), () => {}); + +// Run key interval tests one after another. +runKeyIntervalTests(); diff --git a/cli/tests/node_compat/test/parallel/test-readline-position.js b/cli/tests/node_compat/test/parallel/test-readline-position.js new file mode 100644 index 00000000000000..0ffdf4b18e1716 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-position.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); +const readline = require('readline'); +const assert = require('assert'); + +const ctrlU = { ctrl: true, name: 'u' }; + +common.skipIfDumbTerminal(); + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input, + prompt: '' + }); + + const tests = [ + [1, 'a'], + [2, 'ab'], + [2, '丁'], + [0, '\u0301'], // COMBINING ACUTE ACCENT + [1, 'a\u0301'], // á + [0, '\u20DD'], // COMBINING ENCLOSING CIRCLE + [2, 'a\u20DDb'], // a⃝b + [0, '\u200E'], // LEFT-TO-RIGHT MARK + ]; + + for (const [cursor, string] of tests) { + rl.write(string); + assert.strictEqual(rl.getCursorPos().cols, cursor); + rl.write(null, ctrlU); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-readline-promises-csi.mjs b/cli/tests/node_compat/test/parallel/test-readline-promises-csi.mjs new file mode 100644 index 00000000000000..fb61b52d95e05d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-promises-csi.mjs @@ -0,0 +1,233 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals + + +import '../common/index.mjs'; +import assert from 'assert'; +import { Readline } from 'readline/promises'; +import { setImmediate } from 'timers/promises'; +import { Writable } from 'stream'; + +import utils from 'internal/readline/utils'; +const { CSI } = utils; + +const INVALID_ARG = { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', +}; + +class TestWritable extends Writable { + data = ''; + _write(chunk, encoding, callback) { + this.data += chunk.toString(); + callback(); + } +} + +[ + undefined, null, + 0, 1, 1n, 1.1, NaN, Infinity, + true, false, + Symbol(), + '', '1', + [], {}, () => {}, +].forEach((arg) => + assert.throws(() => new Readline(arg), INVALID_ARG) +); + +{ + const writable = new TestWritable(); + const readline = new Readline(writable); + + await readline.clearScreenDown().commit(); + assert.deepStrictEqual(writable.data, CSI.kClearScreenDown); + await readline.clearScreenDown().commit(); + + writable.data = ''; + await readline.clearScreenDown().rollback(); + assert.strictEqual(writable.data, ''); + + writable.data = ''; + await readline.clearLine(-1).commit(); + assert.deepStrictEqual(writable.data, CSI.kClearToLineBeginning); + + writable.data = ''; + await readline.clearLine(1).commit(); + assert.deepStrictEqual(writable.data, CSI.kClearToLineEnd); + + writable.data = ''; + await readline.clearLine(0).commit(); + assert.deepStrictEqual(writable.data, CSI.kClearLine); + + writable.data = ''; + await readline.clearLine(-1).commit(); + assert.deepStrictEqual(writable.data, CSI.kClearToLineBeginning); + + await readline.clearLine(0, null).commit(); + + // Nothing is written when moveCursor 0, 0 + for (const set of + [ + [0, 0, ''], + [1, 0, '\x1b[1C'], + [-1, 0, '\x1b[1D'], + [0, 1, '\x1b[1B'], + [0, -1, '\x1b[1A'], + [1, 1, '\x1b[1C\x1b[1B'], + [-1, 1, '\x1b[1D\x1b[1B'], + [-1, -1, '\x1b[1D\x1b[1A'], + [1, -1, '\x1b[1C\x1b[1A'], + ]) { + writable.data = ''; + await readline.moveCursor(set[0], set[1]).commit(); + assert.deepStrictEqual(writable.data, set[2]); + writable.data = ''; + await readline.moveCursor(set[0], set[1]).commit(); + assert.deepStrictEqual(writable.data, set[2]); + } + + + await readline.moveCursor(1, 1, null).commit(); + + writable.data = ''; + [ + undefined, null, + true, false, + Symbol(), + '', '1', + [], {}, () => {}, + ].forEach((arg) => + assert.throws(() => readline.cursorTo(arg), INVALID_ARG) + ); + assert.strictEqual(writable.data, ''); + + writable.data = ''; + assert.throws(() => readline.cursorTo('a', 'b'), INVALID_ARG); + assert.strictEqual(writable.data, ''); + + writable.data = ''; + assert.throws(() => readline.cursorTo('a', 1), INVALID_ARG); + assert.strictEqual(writable.data, ''); + + writable.data = ''; + assert.throws(() => readline.cursorTo(1, 'a'), INVALID_ARG); + assert.strictEqual(writable.data, ''); + + writable.data = ''; + await readline.cursorTo(1).commit(); + assert.strictEqual(writable.data, '\x1b[2G'); + + writable.data = ''; + await readline.cursorTo(1, 2).commit(); + assert.strictEqual(writable.data, '\x1b[3;2H'); + + writable.data = ''; + await readline.cursorTo(1, 2).commit(); + assert.strictEqual(writable.data, '\x1b[3;2H'); + + writable.data = ''; + await readline.cursorTo(1).cursorTo(1, 2).commit(); + assert.strictEqual(writable.data, '\x1b[2G\x1b[3;2H'); + + writable.data = ''; + await readline.cursorTo(1).commit(); + assert.strictEqual(writable.data, '\x1b[2G'); + + // Verify that cursorTo() rejects if x or y is NaN. + [1.1, NaN, Infinity].forEach((arg) => { + assert.throws(() => readline.cursorTo(arg), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + }); + }); + + [1.1, NaN, Infinity].forEach((arg) => { + assert.throws(() => readline.cursorTo(1, arg), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + }); + }); + + assert.throws(() => readline.cursorTo(NaN, NaN), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + }); +} + +{ + const error = new Error(); + const writable = new class extends Writable { + _write() { throw error; } + }(); + const readline = new Readline(writable); + + await assert.rejects(readline.cursorTo(1).commit(), error); +} + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + + await readline.clearScreenDown(); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, CSI.kClearScreenDown); +} + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + for (const [dir, data] of + [ + [-1, CSI.kClearToLineBeginning], + [1, CSI.kClearToLineEnd], + [0, CSI.kClearLine], + ]) { + writable.data = ''; + readline.clearLine(dir); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, data); + } +} + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + for (const [x, y, data] of + [ + [0, 0, ''], + [1, 0, '\x1b[1C'], + [-1, 0, '\x1b[1D'], + [0, 1, '\x1b[1B'], + [0, -1, '\x1b[1A'], + [1, 1, '\x1b[1C\x1b[1B'], + [-1, 1, '\x1b[1D\x1b[1B'], + [-1, -1, '\x1b[1D\x1b[1A'], + [1, -1, '\x1b[1C\x1b[1A'], + ]) { + writable.data = ''; + readline.moveCursor(x, y); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, data); + } +} + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + for (const [x, y, data] of + [ + [1, undefined, '\x1b[2G'], + [1, 2, '\x1b[3;2H'], + ]) { + writable.data = ''; + readline.cursorTo(x, y); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, data); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-readline-promises-interface.js b/cli/tests/node_compat/test/parallel/test-readline-promises-interface.js new file mode 100644 index 00000000000000..fbbfb46ad34695 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-promises-interface.js @@ -0,0 +1,1149 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +common.skipIfDumbTerminal(); + +const assert = require('assert'); +const readline = require('readline/promises'); +const { + getStringWidth, + stripVTControlCharacters +} = require('internal/util/inspect'); +const EventEmitter = require('events').EventEmitter; +const { Writable, Readable } = require('stream'); + +class FakeInput extends EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +function isWarned(emitter) { + for (const name in emitter) { + const listeners = emitter[name]; + if (listeners.warned) return true; + } + return false; +} + +function getInterface(options) { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + ...options, + }); + return [rli, fi]; +} + +function assertCursorRowsAndCols(rli, rows, cols) { + const cursorPos = rli.getCursorPos(); + assert.strictEqual(cursorPos.rows, rows); + assert.strictEqual(cursorPos.cols, cols); +} + +[ + undefined, + 50, + 0, + 100.5, + 5000, +].forEach((crlfDelay) => { + const [rli] = getInterface({ crlfDelay }); + assert.strictEqual(rli.crlfDelay, Math.max(crlfDelay || 100, 100)); + rli.close(); +}); + +{ + const input = new FakeInput(); + + // Constructor throws if completer is not a function or undefined + assert.throws(() => { + readline.createInterface({ + input, + completer: 'string is not valid' + }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE' + }); + + assert.throws(() => { + readline.createInterface({ + input, + completer: '' + }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE' + }); + + assert.throws(() => { + readline.createInterface({ + input, + completer: false + }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE' + }); + + // Constructor throws if history is not an array + ['not an array', 123, 123n, {}, true, Symbol(), null].forEach((history) => { + assert.throws(() => { + readline.createInterface({ + input, + history, + }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + // Constructor throws if historySize is not a positive number + ['not a number', -1, NaN, {}, true, Symbol(), null].forEach((historySize) => { + assert.throws(() => { + readline.createInterface({ + input, + historySize, + }); + }, { + name: 'RangeError', + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + // Check for invalid tab sizes. + assert.throws( + () => new readline.Interface({ + input, + tabSize: 0 + }), + { code: 'ERR_OUT_OF_RANGE' } + ); + + assert.throws( + () => new readline.Interface({ + input, + tabSize: '4' + }), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + + assert.throws( + () => new readline.Interface({ + input, + tabSize: 4.5 + }), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "tabSize" is out of range. ' + + 'It must be an integer. Received 4.5' + } + ); +} + +// Sending a single character with no newline +{ + const fi = new FakeInput(); + const rli = new readline.Interface(fi, {}); + rli.on('line', common.mustNotCall()); + fi.emit('data', 'a'); + rli.close(); +} + +// Sending multiple newlines at once that does not end with a new line and a +// `end` event(last line is). \r should behave like \n when alone. +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + fi.emit('data', expectedLines.join('\r')); + rli.close(); +} + +// \r at start of input should output blank line +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLines = ['', 'foo' ]; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length)); + fi.emit('data', '\rfoo\r'); + rli.close(); +} + +// \t does not become part of the input when there is a completer function +{ + const completer = (line) => [[], line]; + const [rli, fi] = getInterface({ terminal: true, completer }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'foo'); + })); + for (const character of '\tfo\to\t') { + fi.emit('data', character); + } + fi.emit('data', '\n'); + rli.close(); +} + +// \t when there is no completer function should behave like an ordinary +// character +{ + const [rli, fi] = getInterface({ terminal: true }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '\t'); + })); + fi.emit('data', '\t'); + fi.emit('data', '\n'); + rli.close(); +} + +// Adding history lines should emit the history event with +// the history array +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('history', common.mustCall((history) => { + const expectedHistory = expectedLines.slice(0, history.length).reverse(); + assert.deepStrictEqual(history, expectedHistory); + }, expectedLines.length)); + for (const line of expectedLines) { + fi.emit('data', `${line}\n`); + } + rli.close(); +} + +// Altering the history array in the listener should not alter +// the line being processed +{ + const [rli, fi] = getInterface({ terminal: true }); + const expectedLine = 'foo'; + rli.on('history', common.mustCall((history) => { + assert.strictEqual(history[0], expectedLine); + history.shift(); + })); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLine); + assert.strictEqual(rli.history.length, 0); + })); + fi.emit('data', `${expectedLine}\n`); + rli.close(); +} + +// Duplicate lines are removed from history when +// `options.removeHistoryDuplicates` is `true` +{ + const [rli, fi] = getInterface({ + terminal: true, + removeHistoryDuplicates: true + }); + const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat']; + // ['foo', 'baz', 'bar', bat']; + let callCount = 0; + rli.on('line', function(line) { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + fi.emit('data', `${expectedLines.join('\n')}\n`); + assert.strictEqual(callCount, expectedLines.length); + fi.emit('keypress', '.', { name: 'up' }); // 'bat' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.notStrictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'baz' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'foo' + assert.notStrictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(callCount, 0); + fi.emit('keypress', '.', { name: 'down' }); // 'baz' + assert.strictEqual(rli.line, 'baz'); + assert.strictEqual(rli.historyIndex, 2); + fi.emit('keypress', '.', { name: 'n', ctrl: true }); // 'bar' + assert.strictEqual(rli.line, 'bar'); + assert.strictEqual(rli.historyIndex, 1); + fi.emit('keypress', '.', { name: 'n', ctrl: true }); + assert.strictEqual(rli.line, 'bat'); + assert.strictEqual(rli.historyIndex, 0); + // Activate the substring history search. + fi.emit('keypress', '.', { name: 'down' }); // 'bat' + assert.strictEqual(rli.line, 'bat'); + assert.strictEqual(rli.historyIndex, -1); + // Deactivate substring history search. + fi.emit('keypress', '.', { name: 'backspace' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + // Activate the substring history search. + fi.emit('keypress', '.', { name: 'down' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + fi.emit('keypress', '.', { name: 'down' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + fi.emit('keypress', '.', { name: 'up' }); // 'bat' + assert.strictEqual(rli.historyIndex, 0); + assert.strictEqual(rli.line, 'bat'); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.strictEqual(rli.historyIndex, 1); + assert.strictEqual(rli.line, 'bar'); + fi.emit('keypress', '.', { name: 'up' }); // 'baz' + assert.strictEqual(rli.historyIndex, 2); + assert.strictEqual(rli.line, 'baz'); + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 4); + assert.strictEqual(rli.line, 'ba'); + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 4); + assert.strictEqual(rli.line, 'ba'); + // Deactivate substring history search and reset history index. + fi.emit('keypress', '.', { name: 'right' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + // Substring history search activated. + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 0); + assert.strictEqual(rli.line, 'bat'); + rli.close(); +} + +// Duplicate lines are not removed from history when +// `options.removeHistoryDuplicates` is `false` +{ + const [rli, fi] = getInterface({ + terminal: true, + removeHistoryDuplicates: false + }); + const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat']; + let callCount = 0; + rli.on('line', function(line) { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + fi.emit('data', `${expectedLines.join('\n')}\n`); + assert.strictEqual(callCount, expectedLines.length); + fi.emit('keypress', '.', { name: 'up' }); // 'bat' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.notStrictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'baz' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'bar' + assert.strictEqual(rli.line, expectedLines[--callCount]); + fi.emit('keypress', '.', { name: 'up' }); // 'foo' + assert.strictEqual(rli.line, expectedLines[--callCount]); + assert.strictEqual(callCount, 0); + rli.close(); +} + +// Regression test for repl freeze, #1968: +// check that nothing fails if 'keypress' event throws. +{ + const [rli, fi] = getInterface({ terminal: true }); + const keys = []; + const err = new Error('bad thing happened'); + fi.on('keypress', function(key) { + keys.push(key); + if (key === 'X') { + throw err; + } + }); + assert.throws( + () => fi.emit('data', 'fooX'), + (e) => { + assert.strictEqual(e, err); + return true; + } + ); + fi.emit('data', 'bar'); + assert.strictEqual(keys.join(''), 'fooXbar'); + rli.close(); +} + +// History is bound +{ + const [rli, fi] = getInterface({ terminal: true, historySize: 2 }); + const lines = ['line 1', 'line 2', 'line 3']; + fi.emit('data', lines.join('\n') + '\n'); + assert.strictEqual(rli.history.length, 2); + assert.strictEqual(rli.history[0], 'line 3'); + assert.strictEqual(rli.history[1], 'line 2'); +} + +// Question +{ + const [rli] = getInterface({ terminal: true }); + const expectedLines = ['foo']; + rli.question(expectedLines[0]).then(() => rli.close()); + assertCursorRowsAndCols(rli, 0, expectedLines[0].length); + rli.close(); +} + +// Sending a multi-line question +{ + const [rli] = getInterface({ terminal: true }); + const expectedLines = ['foo', 'bar']; + rli.question(expectedLines.join('\n')).then(() => rli.close()); + assertCursorRowsAndCols( + rli, expectedLines.length - 1, expectedLines.slice(-1)[0].length); + rli.close(); +} + +{ + // Beginning and end of line + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + fi.emit('keypress', '.', { ctrl: true, name: 'e' }); + assertCursorRowsAndCols(rli, 0, 19); + rli.close(); +} + +{ + // Back and Forward one character + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + assertCursorRowsAndCols(rli, 0, 19); + + // Back one character + fi.emit('keypress', '.', { ctrl: true, name: 'b' }); + assertCursorRowsAndCols(rli, 0, 18); + // Back one character + fi.emit('keypress', '.', { ctrl: true, name: 'b' }); + assertCursorRowsAndCols(rli, 0, 17); + // Forward one character + fi.emit('keypress', '.', { ctrl: true, name: 'f' }); + assertCursorRowsAndCols(rli, 0, 18); + // Forward one character + fi.emit('keypress', '.', { ctrl: true, name: 'f' }); + assertCursorRowsAndCols(rli, 0, 19); + rli.close(); +} + +// Back and Forward one astral character +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Move left one character/code point + fi.emit('keypress', '.', { name: 'left' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Move right one character/code point + fi.emit('keypress', '.', { name: 'right' }); + assertCursorRowsAndCols(rli, 0, 2); + + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '💻'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Two astral characters left +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Move left one character/code point + fi.emit('keypress', '.', { name: 'left' }); + assertCursorRowsAndCols(rli, 0, 0); + + fi.emit('data', '🐕'); + assertCursorRowsAndCols(rli, 0, 2); + + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '🐕💻'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Two astral characters right +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Move left one character/code point + fi.emit('keypress', '.', { name: 'right' }); + assertCursorRowsAndCols(rli, 0, 2); + + fi.emit('data', '🐕'); + assertCursorRowsAndCols(rli, 0, 4); + + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, '💻🐕'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +{ + // `wordLeft` and `wordRight` + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + assertCursorRowsAndCols(rli, 0, 16); + fi.emit('keypress', '.', { meta: true, name: 'b' }); + assertCursorRowsAndCols(rli, 0, 10); + fi.emit('keypress', '.', { ctrl: true, name: 'right' }); + assertCursorRowsAndCols(rli, 0, 16); + fi.emit('keypress', '.', { meta: true, name: 'f' }); + assertCursorRowsAndCols(rli, 0, 19); + rli.close(); +} + +// `deleteWordLeft` +[ + { ctrl: true, name: 'w' }, + { ctrl: true, name: 'backspace' }, + { meta: true, name: 'backspace' }, +].forEach((deleteWordLeftKey) => { + let [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick fox'); + })); + fi.emit('keypress', '.', deleteWordLeftKey); + fi.emit('data', '\n'); + rli.close(); + + // No effect if pressed at beginning of line + [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick brown fox'); + })); + fi.emit('keypress', '.', deleteWordLeftKey); + fi.emit('data', '\n'); + rli.close(); +}); + +// `deleteWordRight` +[ + { ctrl: true, name: 'delete' }, + { meta: true, name: 'delete' }, + { meta: true, name: 'd' }, +].forEach((deleteWordRightKey) => { + let [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + fi.emit('keypress', '.', { ctrl: true, name: 'left' }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick fox'); + })); + fi.emit('keypress', '.', deleteWordRightKey); + fi.emit('data', '\n'); + rli.close(); + + // No effect if pressed at end of line + [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick brown fox'); + })); + fi.emit('keypress', '.', deleteWordRightKey); + fi.emit('data', '\n'); + rli.close(); +}); + +// deleteLeft +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + assertCursorRowsAndCols(rli, 0, 19); + + // Delete left character + fi.emit('keypress', '.', { ctrl: true, name: 'h' }); + assertCursorRowsAndCols(rli, 0, 18); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'the quick brown fo'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteLeft astral character +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + assertCursorRowsAndCols(rli, 0, 2); + // Delete left character + fi.emit('keypress', '.', { ctrl: true, name: 'h' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteRight +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + + // Go to the start of the line + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Delete right character + fi.emit('keypress', '.', { ctrl: true, name: 'd' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'he quick brown fox'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteRight astral character +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', '💻'); + + // Go to the start of the line + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Delete right character + fi.emit('keypress', '.', { ctrl: true, name: 'd' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteLineLeft +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + assertCursorRowsAndCols(rli, 0, 19); + + // Delete from current to start of line + fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'backspace' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// deleteLineRight +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick brown fox'); + + // Go to the start of the line + fi.emit('keypress', '.', { ctrl: true, name: 'a' }); + assertCursorRowsAndCols(rli, 0, 0); + + // Delete from current to end of line + fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' }); + assertCursorRowsAndCols(rli, 0, 0); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Close readline interface +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('keypress', '.', { ctrl: true, name: 'c' }); + assert(rli.closed); +} + +// Multi-line input cursor position +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.columns = 10; + fi.emit('data', 'multi-line text'); + assertCursorRowsAndCols(rli, 1, 5); + rli.close(); +} + +// Multi-line input cursor position and long tabs +{ + const [rli, fi] = getInterface({ tabSize: 16, terminal: true, prompt: '' }); + fi.columns = 10; + fi.emit('data', 'multi-line\ttext \t'); + assert.strictEqual(rli.cursor, 17); + assertCursorRowsAndCols(rli, 3, 2); + rli.close(); +} + +// Check for the default tab size. +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + fi.emit('data', 'the quick\tbrown\tfox'); + assert.strictEqual(rli.cursor, 19); + // The first tab is 7 spaces long, the second one 3 spaces. + assertCursorRowsAndCols(rli, 0, 27); +} + +// Multi-line prompt cursor position +{ + const [rli, fi] = getInterface({ + terminal: true, + prompt: '\nfilledline\nwraping text\n> ' + }); + fi.columns = 10; + fi.emit('data', 't'); + assertCursorRowsAndCols(rli, 4, 3); + rli.close(); +} + +// Clear the whole screen +{ + const [rli, fi] = getInterface({ terminal: true, prompt: '' }); + const lines = ['line 1', 'line 2', 'line 3']; + fi.emit('data', lines.join('\n')); + fi.emit('keypress', '.', { ctrl: true, name: 'l' }); + assertCursorRowsAndCols(rli, 0, 6); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'line 3'); + })); + fi.emit('data', '\n'); + rli.close(); +} + +// Wide characters should be treated as two columns. +assert.strictEqual(getStringWidth('a'), 1); +assert.strictEqual(getStringWidth('あ'), 2); +assert.strictEqual(getStringWidth('谢'), 2); +assert.strictEqual(getStringWidth('고'), 2); +assert.strictEqual(getStringWidth(String.fromCodePoint(0x1f251)), 2); +assert.strictEqual(getStringWidth('abcde'), 5); +assert.strictEqual(getStringWidth('古池や'), 6); +assert.strictEqual(getStringWidth('ノード.js'), 9); +assert.strictEqual(getStringWidth('你好'), 4); +assert.strictEqual(getStringWidth('안녕하세요'), 10); +assert.strictEqual(getStringWidth('A\ud83c\ude00BC'), 5); +assert.strictEqual(getStringWidth('👨‍👩‍👦‍👦'), 8); +assert.strictEqual(getStringWidth('🐕𐐷あ💻😀'), 9); +// TODO(BridgeAR): This should have a width of 4. +assert.strictEqual(getStringWidth('⓬⓪'), 2); +assert.strictEqual(getStringWidth('\u0301\u200D\u200E'), 0); + +// Check if vt control chars are stripped +assert.strictEqual(stripVTControlCharacters('\u001b[31m> \u001b[39m'), '> '); +assert.strictEqual( + stripVTControlCharacters('\u001b[31m> \u001b[39m> '), + '> > ' +); +assert.strictEqual(stripVTControlCharacters('\u001b[31m\u001b[39m'), ''); +assert.strictEqual(stripVTControlCharacters('> '), '> '); +assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m'), 2); +assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m> '), 4); +assert.strictEqual(getStringWidth('\u001b[31m\u001b[39m'), 0); +assert.strictEqual(getStringWidth('> '), 2); + +// Check EventEmitter memory leak +for (let i = 0; i < 12; i++) { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + rl.close(); + assert.strictEqual(isWarned(process.stdin._events), false); + assert.strictEqual(isWarned(process.stdout._events), false); +} + +[true, false].forEach(function(terminal) { + // Disable history + { + const [rli, fi] = getInterface({ terminal, historySize: 0 }); + assert.strictEqual(rli.historySize, 0); + + fi.emit('data', 'asdf\n'); + assert.deepStrictEqual(rli.history, []); + rli.close(); + } + + // Default history size 30 + { + const [rli, fi] = getInterface({ terminal }); + assert.strictEqual(rli.historySize, 30); + + fi.emit('data', 'asdf\n'); + assert.deepStrictEqual(rli.history, terminal ? ['asdf'] : []); + rli.close(); + } + + // Sending a full line + { + const [rli, fi] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'asdf'); + })); + fi.emit('data', 'asdf\n'); + } + + // Ensure that options.signal.removeEventListener was called + { + const ac = new AbortController(); + const signal = ac.signal; + const [rli] = getInterface({ terminal }); + signal.removeEventListener = common.mustCall( + (event, onAbortFn) => { + assert.strictEqual(event, 'abort'); + assert.strictEqual(onAbortFn.name, 'onAbort'); + }); + + rli.question('hello?', { signal }).then(common.mustCall()); + + rli.write('bar\n'); + ac.abort(); + rli.close(); + } + + // Sending a blank line + { + const [rli, fi] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, ''); + })); + fi.emit('data', '\n'); + } + + // Sending a single character with no newline and then a newline + { + const [rli, fi] = getInterface({ terminal }); + let called = false; + rli.on('line', (line) => { + called = true; + assert.strictEqual(line, 'a'); + }); + fi.emit('data', 'a'); + assert.ok(!called); + fi.emit('data', '\n'); + assert.ok(called); + rli.close(); + } + + // Sending multiple newlines at once + { + const [rli, fi] = getInterface({ terminal }); + const expectedLines = ['foo', 'bar', 'baz']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length)); + fi.emit('data', `${expectedLines.join('\n')}\n`); + rli.close(); + } + + // Sending multiple newlines at once that does not end with a new line + { + const [rli, fi] = getInterface({ terminal }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + fi.emit('data', expectedLines.join('\n')); + rli.close(); + } + + // Sending multiple newlines at once that does not end with a new(empty) + // line and a `end` event + { + const [rli, fi] = getInterface({ terminal }); + const expectedLines = ['foo', 'bar', 'baz', '']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + rli.on('close', common.mustCall()); + fi.emit('data', expectedLines.join('\n')); + fi.emit('end'); + rli.close(); + } + + // Sending a multi-byte utf8 char over multiple writes + { + const buf = Buffer.from('☮', 'utf8'); + const [rli, fi] = getInterface({ terminal }); + let callCount = 0; + rli.on('line', function(line) { + callCount++; + assert.strictEqual(line, buf.toString('utf8')); + }); + for (const i of buf) { + fi.emit('data', Buffer.from([i])); + } + assert.strictEqual(callCount, 0); + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + } + + // Calling readline without `new` + { + const [rli, fi] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'asdf'); + })); + fi.emit('data', 'asdf\n'); + rli.close(); + } + + // Calling the question callback + { + const [rli] = getInterface({ terminal }); + rli.question('foo?').then(common.mustCall((answer) => { + assert.strictEqual(answer, 'bar'); + })); + rli.write('bar\n'); + rli.close(); + } + + // Calling the question callback with abort signal + { + const [rli] = getInterface({ terminal }); + const { signal } = new AbortController(); + rli.question('foo?', { signal }).then(common.mustCall((answer) => { + assert.strictEqual(answer, 'bar'); + })); + rli.write('bar\n'); + rli.close(); + } + + // Aborting a question + { + const ac = new AbortController(); + const signal = ac.signal; + const [rli] = getInterface({ terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'bar'); + })); + assert.rejects(rli.question('hello?', { signal }), { name: 'AbortError' }) + .then(common.mustCall()); + ac.abort(); + rli.write('bar\n'); + rli.close(); + } + + (async () => { + const [rli] = getInterface({ terminal }); + const signal = AbortSignal.abort('boom'); + await assert.rejects(rli.question('hello', { signal }), { + cause: 'boom', + }); + rli.close(); + })().then(common.mustCall()); + + // Throw an error when question is executed with an aborted signal + { + const ac = new AbortController(); + const signal = ac.signal; + ac.abort(); + const [rli] = getInterface({ terminal }); + assert.rejects( + rli.question('hello?', { signal }), + { + name: 'AbortError' + } + ).then(common.mustCall()); + rli.close(); + } + + // Call question after close + { + const [rli, fi] = getInterface({ terminal }); + rli.question('What\'s your name?').then(common.mustCall((name) => { + assert.strictEqual(name, 'Node.js'); + rli.close(); + rli.question('How are you?') + .then(common.mustNotCall(), common.expectsError({ + code: 'ERR_USE_AFTER_CLOSE', + name: 'Error' + })); + assert.notStrictEqual(rli.getPrompt(), 'How are you?'); + })); + fi.emit('data', 'Node.js\n'); + } + + + // Can create a new readline Interface with a null output argument + { + const [rli, fi] = getInterface({ output: null, terminal }); + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'asdf'); + })); + fi.emit('data', 'asdf\n'); + + rli.setPrompt('ddd> '); + rli.prompt(); + rli.write("really shouldn't be seeing this"); + rli.question('What do you think of node.js? ', function(answer) { + console.log('Thank you for your valuable feedback:', answer); + rli.close(); + }); + } + + // Calling the getPrompt method + { + const expectedPrompts = ['$ ', '> ']; + const [rli] = getInterface({ terminal }); + for (const prompt of expectedPrompts) { + rli.setPrompt(prompt); + assert.strictEqual(rli.getPrompt(), prompt); + } + } + + { + const expected = terminal ? + ['\u001b[1G', '\u001b[0J', '$ ', '\u001b[3G'] : + ['$ ']; + + const output = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + assert.strictEqual(chunk.toString(), expected.shift()); + cb(); + rl.close(); + }, expected.length) + }); + + const rl = readline.createInterface({ + input: new Readable({ read: common.mustCall() }), + output, + prompt: '$ ', + terminal + }); + + rl.prompt(); + + assert.strictEqual(rl.getPrompt(), '$ '); + } + + { + const fi = new FakeInput(); + assert.deepStrictEqual(fi.listeners(terminal ? 'keypress' : 'data'), []); + } + + // Emit two line events when the delay + // between \r and \n exceeds crlfDelay + { + const crlfDelay = 200; + const [rli, fi] = getInterface({ terminal, crlfDelay }); + let callCount = 0; + rli.on('line', function(line) { + callCount++; + }); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 2); + rli.close(); + }), crlfDelay + 10); + } + + // For the purposes of the following tests, we do not care about the exact + // value of crlfDelay, only that the behaviour conforms to what's expected. + // Setting it to Infinity allows the test to succeed even under extreme + // CPU stress. + const crlfDelay = Infinity; + + // Set crlfDelay to `Infinity` is allowed + { + const delay = 200; + const [rli, fi] = getInterface({ terminal, crlfDelay }); + let callCount = 0; + rli.on('line', function(line) { + callCount++; + }); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + }), delay); + } + + // Sending multiple newlines at once that does not end with a new line + // and a `end` event(last line is) + + // \r\n should emit one line event, not two + { + const [rli, fi] = getInterface({ terminal, crlfDelay }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines.shift()); + }, expectedLines.length - 1)); + fi.emit('data', expectedLines.join('\r\n')); + rli.close(); + } + + // \r\n should emit one line event when split across multiple writes. + { + const [rli, fi] = getInterface({ terminal, crlfDelay }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + let callCount = 0; + rli.on('line', common.mustCall((line) => { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }, expectedLines.length)); + expectedLines.forEach((line) => { + fi.emit('data', `${line}\r`); + fi.emit('data', '\n'); + }); + rli.close(); + } + + // Emit one line event when the delay between \r and \n is + // over the default crlfDelay but within the setting value. + { + const delay = 125; + const [rli, fi] = getInterface({ terminal, crlfDelay }); + let callCount = 0; + rli.on('line', () => callCount++); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + }), delay); + } +}); + +// Ensure that the _wordLeft method works even for large input +{ + const input = new Readable({ + read() { + this.push('\x1B[1;5D'); // CTRL + Left + this.push(null); + }, + }); + const output = new Writable({ + write: common.mustCall((data, encoding, cb) => { + assert.strictEqual(rl.cursor, rl.line.length - 1); + cb(); + }), + }); + const rl = new readline.createInterface({ + input, + output, + terminal: true, + }); + rl.line = `a${' '.repeat(1e6)}a`; + rl.cursor = rl.line.length; +} diff --git a/cli/tests/node_compat/test/parallel/test-readline-reopen.js b/cli/tests/node_compat/test/parallel/test-readline-reopen.js new file mode 100644 index 00000000000000..1a9ddcf9ea17f5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-reopen.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Regression test for https://github.com/nodejs/node/issues/13557 +// Tests that multiple subsequent readline instances can re-use an input stream. + +const common = require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const { PassThrough } = require('stream'); + +const input = new PassThrough(); +const output = new PassThrough(); + +const rl1 = readline.createInterface({ + input, + output, + terminal: true +}); + +rl1.on('line', common.mustCall(rl1OnLine)); + +// Write a line plus the first byte of a UTF-8 multibyte character to make sure +// that it doesn’t get lost when closing the readline instance. +input.write(Buffer.concat([ + Buffer.from('foo\n'), + Buffer.from([ 0xe2 ]), // Exactly one third of a ☃ snowman. +])); + +function rl1OnLine(line) { + assert.strictEqual(line, 'foo'); + rl1.close(); + const rl2 = readline.createInterface({ + input, + output, + terminal: true + }); + + rl2.on('line', common.mustCall((line) => { + assert.strictEqual(line, '☃bar'); + rl2.close(); + })); + input.write(Buffer.from([0x98, 0x83])); // The rest of the ☃ snowman. + input.write('bar\n'); +} diff --git a/cli/tests/node_compat/test/parallel/test-readline-set-raw-mode.js b/cli/tests/node_compat/test/parallel/test-readline-set-raw-mode.js new file mode 100644 index 00000000000000..0b09cc49351288 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-set-raw-mode.js @@ -0,0 +1,97 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const Stream = require('stream'); + +const stream = new Stream(); +let expectedRawMode = true; +let rawModeCalled = false; +let resumeCalled = false; +let pauseCalled = false; + +stream.setRawMode = function(mode) { + rawModeCalled = true; + assert.strictEqual(mode, expectedRawMode); +}; +stream.resume = function() { + resumeCalled = true; +}; +stream.pause = function() { + pauseCalled = true; +}; + +// When the "readline" starts in "terminal" mode, +// then setRawMode(true) should be called +const rli = readline.createInterface({ + input: stream, + output: stream, + terminal: true +}); +assert(rli.terminal); +assert(rawModeCalled); +assert(resumeCalled); +assert(!pauseCalled); + + +// pause() should call *not* call setRawMode() +rawModeCalled = false; +resumeCalled = false; +pauseCalled = false; +rli.pause(); +assert(!rawModeCalled); +assert(!resumeCalled); +assert(pauseCalled); + + +// resume() should *not* call setRawMode() +rawModeCalled = false; +resumeCalled = false; +pauseCalled = false; +rli.resume(); +assert(!rawModeCalled); +assert(resumeCalled); +assert(!pauseCalled); + + +// close() should call setRawMode(false) +expectedRawMode = false; +rawModeCalled = false; +resumeCalled = false; +pauseCalled = false; +rli.close(); +assert(rawModeCalled); +assert(!resumeCalled); +assert(pauseCalled); + +assert.deepStrictEqual(stream.listeners('keypress'), []); +// One data listener for the keypress events. +assert.strictEqual(stream.listeners('data').length, 1); diff --git a/cli/tests/node_compat/test/parallel/test-readline-undefined-columns.js b/cli/tests/node_compat/test/parallel/test-readline-undefined-columns.js new file mode 100644 index 00000000000000..b185b0d93076a3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline-undefined-columns.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const PassThrough = require('stream').PassThrough; +const readline = require('readline'); + +common.skipIfDumbTerminal(); + +// Checks that tab completion still works +// when output column size is undefined + +const iStream = new PassThrough(); +const oStream = new PassThrough(); + +readline.createInterface({ + terminal: true, + input: iStream, + output: oStream, + completer: function(line, cb) { + cb(null, [['process.stdout', 'process.stdin', 'process.stderr'], line]); + } +}); + +let output = ''; + +oStream.on('data', function(data) { + output += data; +}); + +oStream.on('end', common.mustCall(() => { + const expect = 'process.stdout\r\n' + + 'process.stdin\r\n' + + 'process.stderr'; + assert.match(output, new RegExp(expect)); +})); + +iStream.write('process.s\t'); + +// Completion works. +assert.match(output, /process\.std\b/); +// Completion doesn’t show all results yet. +assert.doesNotMatch(output, /stdout/); + +iStream.write('\t'); +oStream.end(); diff --git a/cli/tests/node_compat/test/parallel/test-readline.js b/cli/tests/node_compat/test/parallel/test-readline.js new file mode 100644 index 00000000000000..36b729cd592c76 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-readline.js @@ -0,0 +1,158 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); +const readline = require('readline'); +const assert = require('assert'); + +common.skipIfDumbTerminal(); + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustCall((data) => { + assert.strictEqual(data, 'abc'); + })); + + input.end('abc'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustNotCall('must not be called before newline')); + + input.write('abc'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustCall((data) => { + assert.strictEqual(data, 'abc'); + })); + + input.write('abc\n'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.write('foo'); + assert.strictEqual(rl.cursor, 3); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + end: ['\x1b[F', { ctrl: true, name: 'e' }], + }, + gnome: { + home: ['\x1bOH', { ctrl: true, name: 'a' }], + end: ['\x1bOF', { ctrl: true, name: 'e' }] + }, + rxvt: { + home: ['\x1b[7', { ctrl: true, name: 'a' }], + end: ['\x1b[8', { ctrl: true, name: 'e' }] + }, + putty: { + home: ['\x1b[1~', { ctrl: true, name: 'a' }], + end: ['\x1b[>~', { ctrl: true, name: 'e' }] + } + }; + + [key.xterm, key.gnome, key.rxvt, key.putty].forEach(function(key) { + rl.write.apply(rl, key.home); + assert.strictEqual(rl.cursor, 0); + rl.write.apply(rl, key.end); + assert.strictEqual(rl.cursor, 3); + }); + +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + metab: ['\x1bb', { meta: true, name: 'b' }], + metaf: ['\x1bf', { meta: true, name: 'f' }], + } + }; + + rl.write('foo bar.hop/zoo'); + rl.write.apply(rl, key.xterm.home); + [ + { cursor: 4, key: key.xterm.metaf }, + { cursor: 7, key: key.xterm.metaf }, + { cursor: 8, key: key.xterm.metaf }, + { cursor: 11, key: key.xterm.metaf }, + { cursor: 12, key: key.xterm.metaf }, + { cursor: 15, key: key.xterm.metaf }, + { cursor: 12, key: key.xterm.metab }, + { cursor: 11, key: key.xterm.metab }, + { cursor: 8, key: key.xterm.metab }, + { cursor: 7, key: key.xterm.metab }, + { cursor: 4, key: key.xterm.metab }, + { cursor: 0, key: key.xterm.metab }, + ].forEach(function(action) { + rl.write.apply(rl, action.key); + assert.strictEqual(rl.cursor, action.cursor); + }); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + metad: ['\x1bd', { meta: true, name: 'd' }] + } + }; + + rl.write('foo bar.hop/zoo'); + rl.write.apply(rl, key.xterm.home); + [ + 'bar.hop/zoo', + '.hop/zoo', + 'hop/zoo', + '/zoo', + 'zoo', + '', + ].forEach(function(expectedLine) { + rl.write.apply(rl, key.xterm.metad); + assert.strictEqual(rl.cursor, 0); + assert.strictEqual(rl.line, expectedLine); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stdin-from-file-spawn.js b/cli/tests/node_compat/test/parallel/test-stdin-from-file-spawn.js new file mode 100644 index 00000000000000..89a0860a325da4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stdin-from-file-spawn.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.8.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// TODO(cjihrig): 'run -A require.ts' should not be needed in +// execSync() call at the bottom of this test. + +'use strict'; +const common = require('../common'); +const process = require('process'); + +let defaultShell; +if (process.platform === 'linux' || process.platform === 'darwin') { + defaultShell = '/bin/sh'; +} else if (process.platform === 'win32') { + defaultShell = 'cmd.exe'; +} else { + common.skip('This is test exists only on Linux/Win32/OSX'); +} + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const tmpdir = require('../common/tmpdir'); + +const tmpDir = tmpdir.path; +tmpdir.refresh(); +const tmpCmdFile = path.join(tmpDir, 'test-stdin-from-file-spawn-cmd'); +const tmpJsFile = path.join(tmpDir, 'test-stdin-from-file-spawn.js'); +fs.writeFileSync(tmpCmdFile, 'echo hello'); +fs.writeFileSync(tmpJsFile, ` +'use strict'; +const { spawn } = require('child_process'); +// Reference the object to invoke the getter +process.stdin; +setTimeout(() => { + let ok = false; + const child = spawn(process.env.SHELL || '${defaultShell}', + [], { stdio: ['inherit', 'pipe'] }); + child.stdout.on('data', () => { + ok = true; + }); + child.on('close', () => { + process.exit(ok ? 0 : -1); + }); +}, 100); +`); + +execSync(`${process.argv[0]} run -A require.ts ${tmpJsFile} < ${tmpCmdFile}`); diff --git a/cli/tests/node_compat/test/parallel/test-stream-add-abort-signal.js b/cli/tests/node_compat/test/parallel/test-stream-add-abort-signal.js new file mode 100644 index 00000000000000..b88f3e981294e9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-add-abort-signal.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); +const { addAbortSignal, Readable } = require('stream'); +const { + addAbortSignalNoValidate, +} = require('internal/streams/add-abort-signal'); + +{ + assert.throws(() => { + addAbortSignal('INVALID_SIGNAL'); + }, /ERR_INVALID_ARG_TYPE/); + + const ac = new AbortController(); + assert.throws(() => { + addAbortSignal(ac.signal, 'INVALID_STREAM'); + }, /ERR_INVALID_ARG_TYPE/); +} + +{ + const r = new Readable({ + read: () => {}, + }); + assert.deepStrictEqual(r, addAbortSignalNoValidate('INVALID_SIGNAL', r)); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-aliases-legacy.js b/cli/tests/node_compat/test/parallel/test-stream-aliases-legacy.js new file mode 100644 index 00000000000000..bcae28143a7bec --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-aliases-legacy.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +// Verify that all individual aliases are left in place. + +assert.strictEqual(stream.Readable, require('_stream_readable')); +assert.strictEqual(stream.Writable, require('_stream_writable')); +assert.strictEqual(stream.Duplex, require('_stream_duplex')); +assert.strictEqual(stream.Transform, require('_stream_transform')); +assert.strictEqual(stream.PassThrough, require('_stream_passthrough')); diff --git a/cli/tests/node_compat/test/parallel/test-stream-asIndexedPairs.mjs b/cli/tests/node_compat/test/parallel/test-stream-asIndexedPairs.mjs new file mode 100644 index 00000000000000..f7f8b6d7cad273 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-asIndexedPairs.mjs @@ -0,0 +1,60 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +import '../common/index.mjs'; +import { Readable } from 'stream'; +import { deepStrictEqual, rejects, throws } from 'assert'; + +{ + // asIndexedPairs with a synchronous stream + const pairs = await Readable.from([1, 2, 3]).asIndexedPairs().toArray(); + deepStrictEqual(pairs, [[0, 1], [1, 2], [2, 3]]); + const empty = await Readable.from([]).asIndexedPairs().toArray(); + deepStrictEqual(empty, []); +} + +{ + // asIndexedPairs works an asynchronous streams + const asyncFrom = (...args) => Readable.from(...args).map(async (x) => x); + const pairs = await asyncFrom([1, 2, 3]).asIndexedPairs().toArray(); + deepStrictEqual(pairs, [[0, 1], [1, 2], [2, 3]]); + const empty = await asyncFrom([]).asIndexedPairs().toArray(); + deepStrictEqual(empty, []); +} + +{ + // Does not enumerate an infinite stream + const infinite = () => Readable.from(async function* () { + while (true) yield 1; + }()); + const pairs = await infinite().asIndexedPairs().take(3).toArray(); + deepStrictEqual(pairs, [[0, 1], [1, 1], [2, 1]]); + const empty = await infinite().asIndexedPairs().take(0).toArray(); + deepStrictEqual(empty, []); +} + +{ + // AbortSignal + await rejects(async () => { + const ac = new AbortController(); + const { signal } = ac; + const p = Readable.from([1, 2, 3]).asIndexedPairs({ signal }).toArray(); + ac.abort(); + await p; + }, { name: 'AbortError' }); + + await rejects(async () => { + const signal = AbortSignal.abort(); + await Readable.from([1, 2, 3]).asIndexedPairs({ signal }).toArray(); + }, /AbortError/); +} + +{ + // Error cases + throws(() => Readable.from([1]).asIndexedPairs(1), /ERR_INVALID_ARG_TYPE/); + throws(() => Readable.from([1]).asIndexedPairs({ signal: true }), /ERR_INVALID_ARG_TYPE/); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-auto-destroy.js b/cli/tests/node_compat/test/parallel/test-stream-auto-destroy.js new file mode 100644 index 00000000000000..4b47ca88591b93 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-auto-destroy.js @@ -0,0 +1,119 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + autoDestroy: true, + read() { + this.push('hello'); + this.push('world'); + this.push(null); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let ended = false; + + r.resume(); + + r.on('end', common.mustCall(() => { + ended = true; + })); + + r.on('close', common.mustCall(() => { + assert(ended); + })); +} + +{ + const w = new stream.Writable({ + autoDestroy: true, + write(data, enc, cb) { + cb(null); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let finished = false; + + w.write('hello'); + w.write('world'); + w.end(); + + w.on('finish', common.mustCall(() => { + finished = true; + })); + + w.on('close', common.mustCall(() => { + assert(finished); + })); +} + +{ + const t = new stream.Transform({ + autoDestroy: true, + transform(data, enc, cb) { + cb(null, data); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let ended = false; + let finished = false; + + t.write('hello'); + t.write('world'); + t.end(); + + t.resume(); + + t.on('end', common.mustCall(() => { + ended = true; + })); + + t.on('finish', common.mustCall(() => { + finished = true; + })); + + t.on('close', common.mustCall(() => { + assert(ended); + assert(finished); + })); +} + +{ + const r = new stream.Readable({ + read() { + r2.emit('error', new Error('fail')); + } + }); + const r2 = new stream.Readable({ + autoDestroy: true, + destroy: common.mustCall((err, cb) => cb()) + }); + + r.pipe(r2); +} + +{ + const r = new stream.Readable({ + read() { + w.emit('error', new Error('fail')); + } + }); + const w = new stream.Writable({ + autoDestroy: true, + destroy: common.mustCall((err, cb) => cb()) + }); + + r.pipe(w); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js b/cli/tests/node_compat/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js new file mode 100644 index 00000000000000..564c64467fe28e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); + +const encode = new PassThrough({ + highWaterMark: 1 +}); + +const decode = new PassThrough({ + highWaterMark: 1 +}); + +const send = common.mustCall((buf) => { + encode.write(buf); +}, 4); + +let i = 0; +const onData = common.mustCall(() => { + if (++i === 2) { + send(Buffer.from([0x3])); + send(Buffer.from([0x4])); + } +}, 4); + +encode.pipe(decode).on('data', onData); + +send(Buffer.from([0x1])); +send(Buffer.from([0x2])); diff --git a/cli/tests/node_compat/test/parallel/test-stream-backpressure.js b/cli/tests/node_compat/test/parallel/test-stream-backpressure.js new file mode 100644 index 00000000000000..f971e2ec5b4764 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-backpressure.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let pushes = 0; +const total = 65500 + 40 * 1024; +const rs = new stream.Readable({ + read: common.mustCall(function() { + if (pushes++ === 10) { + this.push(null); + return; + } + + const length = this._readableState.length; + + // We are at most doing two full runs of _reads + // before stopping, because Readable is greedy + // to keep its buffer full + assert(length <= total); + + this.push(Buffer.alloc(65500)); + for (let i = 0; i < 40; i++) { + this.push(Buffer.alloc(1024)); + } + + // We will be over highWaterMark at this point + // but a new call to _read is scheduled anyway. + }, 11) +}); + +const ws = stream.Writable({ + write: common.mustCall(function(data, enc, cb) { + setImmediate(cb); + }, 41 * 10) +}); + +rs.pipe(ws); diff --git a/cli/tests/node_compat/test/parallel/test-stream-big-packet.js b/cli/tests/node_compat/test/parallel/test-stream-big-packet.js new file mode 100644 index 00000000000000..f523acd8b33c3d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-big-packet.js @@ -0,0 +1,72 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let passed = false; + +class TestStream extends stream.Transform { + _transform(chunk, encoding, done) { + if (!passed) { + // Char 'a' only exists in the last write + passed = chunk.toString().includes('a'); + } + done(); + } +} + +const s1 = new stream.Transform({ + transform(chunk, encoding, cb) { + process.nextTick(cb, null, chunk); + } +}); +const s2 = new stream.PassThrough(); +const s3 = new TestStream(); +s1.pipe(s3); +// Don't let s2 auto close which may close s3 +s2.pipe(s3, { end: false }); + +// We must write a buffer larger than highWaterMark +const big = Buffer.alloc(s1.writableHighWaterMark + 1, 'x'); + +// Since big is larger than highWaterMark, it will be buffered internally. +assert(!s1.write(big)); +// 'tiny' is small enough to pass through internal buffer. +assert(s2.write('tiny')); + +// Write some small data in next IO loop, which will never be written to s3 +// Because 'drain' event is not emitted from s1 and s1 is still paused +setImmediate(s1.write.bind(s1), 'later'); + +// Assert after two IO loops when all operations have been done. +process.on('exit', function() { + assert(passed, 'Large buffer is not handled properly by Writable Stream'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-big-push.js b/cli/tests/node_compat/test/parallel/test-stream-big-push.js new file mode 100644 index 00000000000000..c14a9c15ec868a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-big-push.js @@ -0,0 +1,81 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const str = 'asdfasdfasdfasdfasdf'; + +const r = new stream.Readable({ + highWaterMark: 5, + encoding: 'utf8' +}); + +let reads = 0; + +function _read() { + if (reads === 0) { + setTimeout(() => { + r.push(str); + }, 1); + reads++; + } else if (reads === 1) { + const ret = r.push(str); + assert.strictEqual(ret, false); + reads++; + } else { + r.push(null); + } +} + +r._read = common.mustCall(_read, 3); + +r.on('end', common.mustCall()); + +// Push some data in to start. +// We've never gotten any read event at this point. +const ret = r.push(str); +// Should be false. > hwm +assert(!ret); +let chunk = r.read(); +assert.strictEqual(chunk, str); +chunk = r.read(); +assert.strictEqual(chunk, null); + +r.once('readable', () => { + // This time, we'll get *all* the remaining data, because + // it's been added synchronously, as the read WOULD take + // us below the hwm, and so it triggered a _read() again, + // which synchronously added more, which we then return. + chunk = r.read(); + assert.strictEqual(chunk, str + str); + + chunk = r.read(); + assert.strictEqual(chunk, null); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-buffer-list.js b/cli/tests/node_compat/test/parallel/test-stream-buffer-list.js new file mode 100644 index 00000000000000..a74dc56ab52d1d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-buffer-list.js @@ -0,0 +1,91 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const BufferList = require('internal/streams/buffer_list'); + +// Test empty buffer list. +const emptyList = new BufferList(); + +emptyList.shift(); +assert.deepStrictEqual(emptyList, new BufferList()); + +assert.strictEqual(emptyList.join(','), ''); + +assert.deepStrictEqual(emptyList.concat(0), Buffer.alloc(0)); + +const buf = Buffer.from('foo'); + +function testIterator(list, count) { + // test iterator + let len = 0; + // eslint-disable-next-line no-unused-vars + for (const x of list) { + len++; + } + assert.strictEqual(len, count); +} + +// Test buffer list with one element. +const list = new BufferList(); +testIterator(list, 0); + +list.push(buf); +testIterator(list, 1); +for (const x of list) { + assert.strictEqual(x, buf); +} + +const copy = list.concat(3); +testIterator(copy, 3); + +assert.notStrictEqual(copy, buf); +assert.deepStrictEqual(copy, buf); + +assert.strictEqual(list.join(','), 'foo'); + +const shifted = list.shift(); +testIterator(list, 0); +assert.strictEqual(shifted, buf); +assert.deepStrictEqual(list, new BufferList()); + +{ + const list = new BufferList(); + list.push('foo'); + list.push('bar'); + list.push('foo'); + list.push('bar'); + assert.strictEqual(list.consume(6, true), 'foobar'); + assert.strictEqual(list.consume(6, true), 'foobar'); +} + +{ + const list = new BufferList(); + list.push('foo'); + list.push('bar'); + assert.strictEqual(list.consume(5, true), 'fooba'); +} + +{ + const list = new BufferList(); + list.push(buf); + list.push(buf); + list.push(buf); + list.push(buf); + assert.strictEqual(list.consume(6).toString(), 'foofoo'); + assert.strictEqual(list.consume(6).toString(), 'foofoo'); +} + +{ + const list = new BufferList(); + list.push(buf); + list.push(buf); + assert.strictEqual(list.consume(5).toString(), 'foofo'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-catch-rejections.js b/cli/tests/node_compat/test/parallel/test-stream-catch-rejections.js new file mode 100644 index 00000000000000..066d68193e529f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-catch-rejections.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + captureRejections: true, + read() { + } + }); + r.push('hello'); + r.push('world'); + + const err = new Error('kaboom'); + + r.on('error', common.mustCall((_err) => { + assert.strictEqual(err, _err); + assert.strictEqual(r.destroyed, true); + })); + + r.on('data', async () => { + throw err; + }); +} + +{ + const w = new stream.Writable({ + captureRejections: true, + highWaterMark: 1, + write(chunk, enc, cb) { + process.nextTick(cb); + } + }); + + const err = new Error('kaboom'); + + w.write('hello', () => { + w.write('world'); + }); + + w.on('error', common.mustCall((_err) => { + assert.strictEqual(err, _err); + assert.strictEqual(w.destroyed, true); + })); + + w.on('drain', common.mustCall(async () => { + throw err; + }, 2)); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-construct.js b/cli/tests/node_compat/test/parallel/test-stream-construct.js new file mode 100644 index 00000000000000..06634c00e6603b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-construct.js @@ -0,0 +1,287 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Writable, Readable, Duplex } = require('stream'); +const assert = require('assert'); + +{ + // Multiple callback. + new Writable({ + construct: common.mustCall((callback) => { + callback(); + callback(); + }) + }).on('error', common.expectsError({ + name: 'Error', + code: 'ERR_MULTIPLE_CALLBACK' + })); +} + +{ + // Multiple callback. + new Readable({ + construct: common.mustCall((callback) => { + callback(); + callback(); + }) + }).on('error', common.expectsError({ + name: 'Error', + code: 'ERR_MULTIPLE_CALLBACK' + })); +} + +{ + // Synchronous error. + + new Writable({ + construct: common.mustCall((callback) => { + callback(new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Synchronous error. + + new Readable({ + construct: common.mustCall((callback) => { + callback(new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Asynchronous error. + + new Writable({ + construct: common.mustCall((callback) => { + process.nextTick(callback, new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Asynchronous error. + + new Readable({ + construct: common.mustCall((callback) => { + process.nextTick(callback, new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +function testDestroy(factory) { + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(null, () => { + assert.strictEqual(constructed, true); + }); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(); + } + + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'kaboom'); + })); + s.destroy(new Error('kaboom'), (err) => { + assert.strictEqual(err.message, 'kaboom'); + assert.strictEqual(constructed, true); + }); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('error', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(new Error()); + } +} +testDestroy((opts) => new Readable({ + read: common.mustNotCall(), + ...opts +})); +testDestroy((opts) => new Writable({ + write: common.mustNotCall(), + final: common.mustNotCall(), + ...opts +})); + +{ + let constructed = false; + const r = new Readable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + read: common.mustCall(() => { + assert.strictEqual(constructed, true); + r.push(null); + }) + }); + r.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + r.on('data', common.mustNotCall()); +} + +{ + let constructed = false; + const w = new Writable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }), + final: common.mustCall((cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }) + }); + w.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + w.end('data'); +} + +{ + let constructed = false; + const w = new Writable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + write: common.mustNotCall(), + final: common.mustCall((cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }) + }); + w.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + w.end(); +} + +{ + new Duplex({ + construct: common.mustCall() + }); +} + +{ + // https://github.com/nodejs/node/issues/34448 + + let constructed = false; + const d = new Duplex({ + readable: false, + construct: common.mustCall((callback) => { + setImmediate(common.mustCall(() => { + constructed = true; + callback(); + })); + }), + write(chunk, encoding, callback) { + callback(); + }, + read() { + this.push(null); + } + }); + d.resume(); + d.end('foo'); + d.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); +} + +{ + // Construct should not cause stream to read. + new Readable({ + construct: common.mustCall((callback) => { + callback(); + }), + read: common.mustNotCall() + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-destroy-event-order.js b/cli/tests/node_compat/test/parallel/test-stream-destroy-event-order.js new file mode 100644 index 00000000000000..3ad7bd02ac9523 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-destroy-event-order.js @@ -0,0 +1,31 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const rs = new Readable({ + read() {} +}); + +let closed = false; +let errored = false; + +rs.on('close', common.mustCall(() => { + closed = true; + assert(errored); +})); + +rs.on('error', common.mustCall((err) => { + errored = true; + assert(!closed); +})); + +rs.destroy(new Error('kaboom')); diff --git a/cli/tests/node_compat/test/parallel/test-stream-duplex-destroy.js b/cli/tests/node_compat/test/parallel/test-stream-duplex-destroy.js new file mode 100644 index 00000000000000..fad7ddfd6bca14 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-duplex-destroy.js @@ -0,0 +1,264 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Duplex } = require('stream'); +const assert = require('assert'); + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {} + }); + + duplex.resume(); + + duplex.on('end', common.mustNotCall()); + duplex.on('finish', common.mustNotCall()); + duplex.on('close', common.mustCall()); + + duplex.destroy(); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {} + }); + duplex.resume(); + + const expected = new Error('kaboom'); + + duplex.on('end', common.mustNotCall()); + duplex.on('finish', common.mustNotCall()); + duplex.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + duplex.destroy(expected); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {} + }); + + duplex._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(err); + }); + + const expected = new Error('kaboom'); + + duplex.on('finish', common.mustNotCall('no finish event')); + duplex.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + duplex.destroy(expected); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const expected = new Error('kaboom'); + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {}, + destroy: common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(); + }) + }); + duplex.resume(); + + duplex.on('end', common.mustNotCall('no end event')); + duplex.on('finish', common.mustNotCall('no finish event')); + + // Error is swallowed by the custom _destroy + duplex.on('error', common.mustNotCall('no error event')); + duplex.on('close', common.mustCall()); + + duplex.destroy(expected); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {} + }); + + duplex._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(); + }); + + duplex.destroy(); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {} + }); + duplex.resume(); + + duplex._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + process.nextTick(() => { + this.push(null); + this.end(); + cb(); + }); + }); + + const fail = common.mustNotCall('no finish or end event'); + + duplex.on('finish', fail); + duplex.on('end', fail); + + duplex.destroy(); + + duplex.removeListener('end', fail); + duplex.removeListener('finish', fail); + duplex.on('end', common.mustNotCall()); + duplex.on('finish', common.mustNotCall()); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {} + }); + + const expected = new Error('kaboom'); + + duplex._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(expected); + }); + + duplex.on('finish', common.mustNotCall('no finish event')); + duplex.on('end', common.mustNotCall('no end event')); + duplex.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + duplex.destroy(); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {}, + allowHalfOpen: true + }); + duplex.resume(); + + duplex.on('finish', common.mustNotCall()); + duplex.on('end', common.mustNotCall()); + + duplex.destroy(); + assert.strictEqual(duplex.destroyed, true); +} + +{ + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {}, + }); + + duplex.destroyed = true; + assert.strictEqual(duplex.destroyed, true); + + // The internal destroy() mechanism should not be triggered + duplex.on('finish', common.mustNotCall()); + duplex.on('end', common.mustNotCall()); + duplex.destroy(); +} + +{ + function MyDuplex() { + assert.strictEqual(this.destroyed, false); + this.destroyed = false; + Duplex.call(this); + } + + Object.setPrototypeOf(MyDuplex.prototype, Duplex.prototype); + Object.setPrototypeOf(MyDuplex, Duplex); + + new MyDuplex(); +} + +{ + const duplex = new Duplex({ + writable: false, + autoDestroy: true, + write(chunk, enc, cb) { cb(); }, + read() {}, + }); + duplex.push(null); + duplex.resume(); + duplex.on('close', common.mustCall()); +} + +{ + const duplex = new Duplex({ + readable: false, + autoDestroy: true, + write(chunk, enc, cb) { cb(); }, + read() {}, + }); + duplex.end(); + duplex.on('close', common.mustCall()); +} + +{ + const duplex = new Duplex({ + allowHalfOpen: false, + autoDestroy: true, + write(chunk, enc, cb) { cb(); }, + read() {}, + }); + duplex.push(null); + duplex.resume(); + const orgEnd = duplex.end; + duplex.end = common.mustNotCall(); + duplex.on('end', () => { + // Ensure end() is called in next tick to allow + // any pending writes to be invoked first. + process.nextTick(() => { + duplex.end = common.mustCall(orgEnd); + }); + }); + duplex.on('close', common.mustCall()); +} +{ + // Check abort signal + const controller = new AbortController(); + const { signal } = controller; + const duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {}, + signal, + }); + let count = 0; + duplex.on('error', common.mustCall((e) => { + assert.strictEqual(count++, 0); // Ensure not called twice + assert.strictEqual(e.name, 'AbortError'); + })); + duplex.on('close', common.mustCall()); + controller.abort(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-duplex-end.js b/cli/tests/node_compat/test/parallel/test-stream-duplex-end.js new file mode 100644 index 00000000000000..d78ca03e9cef6a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-duplex-end.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const Duplex = require('stream').Duplex; + +{ + const stream = new Duplex({ + read() {} + }); + assert.strictEqual(stream.allowHalfOpen, true); + stream.on('finish', common.mustNotCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} + +{ + const stream = new Duplex({ + read() {}, + allowHalfOpen: false + }); + assert.strictEqual(stream.allowHalfOpen, false); + stream.on('finish', common.mustCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} + +{ + const stream = new Duplex({ + read() {}, + allowHalfOpen: false + }); + assert.strictEqual(stream.allowHalfOpen, false); + stream._writableState.ended = true; + stream.on('finish', common.mustNotCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-duplex-from.js b/cli/tests/node_compat/test/parallel/test-stream-duplex-from.js new file mode 100644 index 00000000000000..2ade73bf14f1e8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-duplex-from.js @@ -0,0 +1,287 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Duplex, Readable, Writable, pipeline } = require('stream'); +const { Blob } = require('buffer'); + +{ + const d = Duplex.from({ + readable: new Readable({ + read() { + this.push('asd'); + this.push(null); + } + }) + }); + assert.strictEqual(d.readable, true); + assert.strictEqual(d.writable, false); + d.once('readable', common.mustCall(function() { + assert.strictEqual(d.read().toString(), 'asd'); + })); + d.once('end', common.mustCall(function() { + assert.strictEqual(d.readable, false); + })); +} + +{ + const d = Duplex.from(new Readable({ + read() { + this.push('asd'); + this.push(null); + } + })); + assert.strictEqual(d.readable, true); + assert.strictEqual(d.writable, false); + d.once('readable', common.mustCall(function() { + assert.strictEqual(d.read().toString(), 'asd'); + })); + d.once('end', common.mustCall(function() { + assert.strictEqual(d.readable, false); + })); +} + +{ + let ret = ''; + const d = Duplex.from(new Writable({ + write(chunk, encoding, callback) { + ret += chunk; + callback(); + } + })); + assert.strictEqual(d.readable, false); + assert.strictEqual(d.writable, true); + d.end('asd'); + d.on('finish', common.mustCall(function() { + assert.strictEqual(d.writable, false); + assert.strictEqual(ret, 'asd'); + })); +} + +{ + let ret = ''; + const d = Duplex.from({ + writable: new Writable({ + write(chunk, encoding, callback) { + ret += chunk; + callback(); + } + }) + }); + assert.strictEqual(d.readable, false); + assert.strictEqual(d.writable, true); + d.end('asd'); + d.on('finish', common.mustCall(function() { + assert.strictEqual(d.writable, false); + assert.strictEqual(ret, 'asd'); + })); +} + +{ + let ret = ''; + const d = Duplex.from({ + readable: new Readable({ + read() { + this.push('asd'); + this.push(null); + } + }), + writable: new Writable({ + write(chunk, encoding, callback) { + ret += chunk; + callback(); + } + }) + }); + assert.strictEqual(d.readable, true); + assert.strictEqual(d.writable, true); + d.once('readable', common.mustCall(function() { + assert.strictEqual(d.read().toString(), 'asd'); + })); + d.once('end', common.mustCall(function() { + assert.strictEqual(d.readable, false); + })); + d.end('asd'); + d.once('finish', common.mustCall(function() { + assert.strictEqual(d.writable, false); + assert.strictEqual(ret, 'asd'); + })); +} + +{ + const d = Duplex.from(Promise.resolve('asd')); + assert.strictEqual(d.readable, true); + assert.strictEqual(d.writable, false); + d.once('readable', common.mustCall(function() { + assert.strictEqual(d.read().toString(), 'asd'); + })); + d.once('end', common.mustCall(function() { + assert.strictEqual(d.readable, false); + })); +} + +{ + // https://github.com/nodejs/node/issues/40497 + pipeline( + ['abc\ndef\nghi'], + Duplex.from(async function * (source) { + let rest = ''; + for await (const chunk of source) { + const lines = (rest + chunk.toString()).split('\n'); + rest = lines.pop(); + for (const line of lines) { + yield line; + } + } + yield rest; + }), + async function * (source) { // eslint-disable-line require-yield + let ret = ''; + for await (const x of source) { + ret += x; + } + assert.strictEqual(ret, 'abcdefghi'); + }, + common.mustCall(() => {}), + ); +} + +// Ensure that isDuplexNodeStream was called +{ + const duplex = new Duplex(); + assert.strictEqual(Duplex.from(duplex), duplex); +} + +// Ensure that Duplex.from works for blobs +{ + const blob = new Blob(['blob']); + const expectedByteLength = blob.size; + const duplex = Duplex.from(blob); + duplex.on('data', common.mustCall((arrayBuffer) => { + assert.strictEqual(arrayBuffer.byteLength, expectedByteLength); + })); +} + +// Ensure that given a promise rejection it emits an error +{ + const myErrorMessage = 'myCustomError'; + Duplex.from(Promise.reject(myErrorMessage)) + .on('error', common.mustCall((error) => { + assert.strictEqual(error, myErrorMessage); + })); +} + +// Ensure that given a promise rejection on an async function it emits an error +{ + const myErrorMessage = 'myCustomError'; + async function asyncFn() { + return Promise.reject(myErrorMessage); + } + + Duplex.from(asyncFn) + .on('error', common.mustCall((error) => { + assert.strictEqual(error, myErrorMessage); + })); +} + +// Ensure that Duplex.from throws an Invalid return value when function is void +{ + assert.throws(() => Duplex.from(() => {}), { + code: 'ERR_INVALID_RETURN_VALUE', + }); +} + +// Ensure data if a sub object has a readable stream it's duplexified +{ + const msg = Buffer.from('hello'); + const duplex = Duplex.from({ + readable: Readable({ + read() { + this.push(msg); + this.push(null); + } + }) + }).on('data', common.mustCall((data) => { + assert.strictEqual(data, msg); + })); + + assert.strictEqual(duplex.writable, false); +} + +// Ensure data if a sub object has a writable stream it's duplexified +{ + const msg = Buffer.from('hello'); + const duplex = Duplex.from({ + writable: Writable({ + write: common.mustCall((data) => { + assert.strictEqual(data, msg); + }) + }) + }); + + duplex.write(msg); + assert.strictEqual(duplex.readable, false); +} + +// Ensure data if a sub object has a writable and readable stream it's duplexified +{ + const msg = Buffer.from('hello'); + + const duplex = Duplex.from({ + readable: Readable({ + read() { + this.push(msg); + this.push(null); + } + }), + writable: Writable({ + write: common.mustCall((data) => { + assert.strictEqual(data, msg); + }) + }) + }); + + duplex.pipe(duplex) + .on('data', common.mustCall((data) => { + assert.strictEqual(data, msg); + assert.strictEqual(duplex.readable, true); + assert.strictEqual(duplex.writable, true); + })) + .on('end', common.mustCall()); +} + +// Ensure that given readable stream that throws an error it calls destroy +{ + const myErrorMessage = 'error!'; + const duplex = Duplex.from(Readable({ + read() { + throw new Error(myErrorMessage); + } + })); + duplex.on('error', common.mustCall((msg) => { + assert.strictEqual(msg.message, myErrorMessage); + })); +} + +// Ensure that given writable stream that throws an error it calls destroy +{ + const myErrorMessage = 'error!'; + const duplex = Duplex.from(Writable({ + write(chunk, enc, cb) { + cb(myErrorMessage); + } + })); + + duplex.on('error', common.mustCall((msg) => { + assert.strictEqual(msg, myErrorMessage); + })); + + duplex.write('test'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-duplex-props.js b/cli/tests/node_compat/test/parallel/test-stream-duplex-props.js new file mode 100644 index 00000000000000..16487c0c2f212e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-duplex-props.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Duplex } = require('stream'); + +{ + const d = new Duplex({ + objectMode: true, + highWaterMark: 100 + }); + + assert.strictEqual(d.writableObjectMode, true); + assert.strictEqual(d.writableHighWaterMark, 100); + assert.strictEqual(d.readableObjectMode, true); + assert.strictEqual(d.readableHighWaterMark, 100); +} + +{ + const d = new Duplex({ + readableObjectMode: false, + readableHighWaterMark: 10, + writableObjectMode: true, + writableHighWaterMark: 100 + }); + + assert.strictEqual(d.writableObjectMode, true); + assert.strictEqual(d.writableHighWaterMark, 100); + assert.strictEqual(d.readableObjectMode, false); + assert.strictEqual(d.readableHighWaterMark, 10); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-duplex-readable-end.js b/cli/tests/node_compat/test/parallel/test-stream-duplex-readable-end.js new file mode 100644 index 00000000000000..84f42014977097 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-duplex-readable-end.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// https://github.com/nodejs/node/issues/35926 +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let loops = 5; + +const src = new stream.Readable({ + read() { + if (loops--) + this.push(Buffer.alloc(20000)); + } +}); + +const dst = new stream.Transform({ + transform(chunk, output, fn) { + this.push(null); + fn(); + } +}); + +src.pipe(dst); + +dst.on('data', () => { }); +dst.on('end', common.mustCall(() => { + assert.strictEqual(loops, 3); + assert.ok(src.isPaused()); +})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-duplex-writable-finished.js b/cli/tests/node_compat/test/parallel/test-stream-duplex-writable-finished.js new file mode 100644 index 00000000000000..99f7cc85516684 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-duplex-writable-finished.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Duplex } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Duplex.prototype + assert(Object.hasOwn(Duplex.prototype, 'writableFinished')); +} + +// event +{ + const duplex = new Duplex(); + + duplex._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(duplex.writableFinished, false); + cb(); + }; + + duplex.on('finish', common.mustCall(() => { + assert.strictEqual(duplex.writableFinished, true); + })); + + duplex.end('testing finished state', common.mustCall(() => { + assert.strictEqual(duplex.writableFinished, true); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-duplex.js b/cli/tests/node_compat/test/parallel/test-stream-duplex.js new file mode 100644 index 00000000000000..a03d1f8f2c0deb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-duplex.js @@ -0,0 +1,140 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const Duplex = require('stream').Duplex; +const { ReadableStream, WritableStream } = require('stream/web'); + +const stream = new Duplex({ objectMode: true }); + +assert(Duplex() instanceof Duplex); +assert(stream._readableState.objectMode); +assert(stream._writableState.objectMode); +assert(stream.allowHalfOpen); +assert.strictEqual(stream.listenerCount('end'), 0); + +let written; +let read; + +stream._write = (obj, _, cb) => { + written = obj; + cb(); +}; + +stream._read = () => {}; + +stream.on('data', (obj) => { + read = obj; +}); + +stream.push({ val: 1 }); +stream.end({ val: 2 }); + +process.on('exit', () => { + assert.strictEqual(read.val, 1); + assert.strictEqual(written.val, 2); +}); + +// Duplex.fromWeb +{ + const dataToRead = Buffer.from('hello'); + const dataToWrite = Buffer.from('world'); + + const readable = new ReadableStream({ + start(controller) { + controller.enqueue(dataToRead); + }, + }); + + const writable = new WritableStream({ + write: common.mustCall((chunk) => { + assert.strictEqual(chunk, dataToWrite); + }) + }); + + const pair = { readable, writable }; + const duplex = Duplex.fromWeb(pair); + + duplex.write(dataToWrite); + duplex.once('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, dataToRead); + })); +} + +// Duplex.fromWeb - using utf8 and objectMode +{ + const dataToRead = 'hello'; + const dataToWrite = 'world'; + + const readable = new ReadableStream({ + start(controller) { + controller.enqueue(dataToRead); + }, + }); + + const writable = new WritableStream({ + write: common.mustCall((chunk) => { + assert.strictEqual(chunk, dataToWrite); + }) + }); + + const pair = { + readable, + writable + }; + const duplex = Duplex.fromWeb(pair, { encoding: 'utf8', objectMode: true }); + + duplex.write(dataToWrite); + duplex.once('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, dataToRead); + })); +} +// Duplex.toWeb +{ + const dataToRead = Buffer.from('hello'); + const dataToWrite = Buffer.from('world'); + + const duplex = Duplex({ + read() { + this.push(dataToRead); + this.push(null); + }, + write: common.mustCall((chunk) => { + assert.strictEqual(chunk, dataToWrite); + }) + }); + + const { writable, readable } = Duplex.toWeb(duplex); + writable.getWriter().write(dataToWrite); + + readable.getReader().read().then(common.mustCall((result) => { + assert.deepStrictEqual(Buffer.from(result.value), dataToRead); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-end-paused.js b/cli/tests/node_compat/test/parallel/test-stream-end-paused.js new file mode 100644 index 00000000000000..5cd1947b7bf726 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-end-paused.js @@ -0,0 +1,57 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Make sure we don't miss the end event for paused 0-length streams + +const Readable = require('stream').Readable; +const stream = new Readable(); +let calledRead = false; +stream._read = function() { + assert(!calledRead); + calledRead = true; + this.push(null); +}; + +stream.on('data', function() { + throw new Error('should not ever get data'); +}); +stream.pause(); + +setTimeout(common.mustCall(function() { + stream.on('end', common.mustCall()); + stream.resume(); +}), 1); + +process.on('exit', function() { + assert(calledRead); + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-error-once.js b/cli/tests/node_compat/test/parallel/test-stream-error-once.js new file mode 100644 index 00000000000000..e6998c3f2eec8d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-error-once.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Writable, Readable } = require('stream'); + +{ + const writable = new Writable(); + writable.on('error', common.mustCall()); + writable.end(); + writable.write('h'); + writable.write('h'); +} + +{ + const readable = new Readable(); + readable.on('error', common.mustCall()); + readable.push(null); + readable.push('h'); + readable.push('h'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-events-prepend.js b/cli/tests/node_compat/test/parallel/test-stream-events-prepend.js new file mode 100644 index 00000000000000..8360a63f652d3c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-events-prepend.js @@ -0,0 +1,33 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +class Writable extends stream.Writable { + constructor() { + super(); + this.prependListener = undefined; + } + + _write(chunk, end, cb) { + cb(); + } +} + +class Readable extends stream.Readable { + _read() { + this.push(null); + } +} + +const w = new Writable(); +w.on('pipe', common.mustCall()); + +const r = new Readable(); +r.pipe(w); diff --git a/cli/tests/node_compat/test/parallel/test-stream-inheritance.js b/cli/tests/node_compat/test/parallel/test-stream-inheritance.js new file mode 100644 index 00000000000000..b8f740d155699e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-inheritance.js @@ -0,0 +1,70 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const { Readable, Writable, Duplex, Transform } = require('stream'); + +const readable = new Readable({ read() {} }); +const writable = new Writable({ write() {} }); +const duplex = new Duplex({ read() {}, write() {} }); +const transform = new Transform({ transform() {} }); + +assert.ok(readable instanceof Readable); +assert.ok(!(writable instanceof Readable)); +assert.ok(duplex instanceof Readable); +assert.ok(transform instanceof Readable); + +assert.ok(!(readable instanceof Writable)); +assert.ok(writable instanceof Writable); +assert.ok(duplex instanceof Writable); +assert.ok(transform instanceof Writable); + +assert.ok(!(readable instanceof Duplex)); +assert.ok(!(writable instanceof Duplex)); +assert.ok(duplex instanceof Duplex); +assert.ok(transform instanceof Duplex); + +assert.ok(!(readable instanceof Transform)); +assert.ok(!(writable instanceof Transform)); +assert.ok(!(duplex instanceof Transform)); +assert.ok(transform instanceof Transform); + +assert.ok(!(null instanceof Writable)); +assert.ok(!(undefined instanceof Writable)); + +// Simple inheritance check for `Writable` works fine in a subclass constructor. +function CustomWritable() { + assert.ok( + this instanceof CustomWritable, + `${this} does not inherit from CustomWritable` + ); + assert.ok( + this instanceof Writable, + `${this} does not inherit from Writable` + ); +} + +Object.setPrototypeOf(CustomWritable, Writable); +Object.setPrototypeOf(CustomWritable.prototype, Writable.prototype); + +new CustomWritable(); + +assert.throws( + CustomWritable, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + message: 'undefined does not inherit from CustomWritable' + } +); + +class OtherCustomWritable extends Writable {} + +assert(!(new OtherCustomWritable() instanceof CustomWritable)); +assert(!(new CustomWritable() instanceof OtherCustomWritable)); diff --git a/cli/tests/node_compat/test/parallel/test-stream-ispaused.js b/cli/tests/node_compat/test/parallel/test-stream-ispaused.js new file mode 100644 index 00000000000000..2f0a5939135a11 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-ispaused.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const readable = new stream.Readable(); + +// _read is a noop, here. +readable._read = Function(); + +// Default state of a stream is not "paused" +assert.ok(!readable.isPaused()); + +// Make the stream start flowing... +readable.on('data', Function()); + +// still not paused. +assert.ok(!readable.isPaused()); + +readable.pause(); +assert.ok(readable.isPaused()); +readable.resume(); +assert.ok(!readable.isPaused()); diff --git a/cli/tests/node_compat/test/parallel/test-stream-objectmode-undefined.js b/cli/tests/node_compat/test/parallel/test-stream-objectmode-undefined.js new file mode 100644 index 00000000000000..c1ce83e757a684 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-objectmode-undefined.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable, Transform } = require('stream'); + +{ + const stream = new Readable({ + objectMode: true, + read: common.mustCall(() => { + stream.push(undefined); + stream.push(null); + }) + }); + + stream.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, undefined); + })); +} + +{ + const stream = new Writable({ + objectMode: true, + write: common.mustCall((chunk) => { + assert.strictEqual(chunk, undefined); + }) + }); + + stream.write(undefined); +} + +{ + const stream = new Transform({ + objectMode: true, + transform: common.mustCall((chunk) => { + stream.push(chunk); + }) + }); + + stream.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, undefined); + })); + + stream.write(undefined); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-once-readable-pipe.js b/cli/tests/node_compat/test/parallel/test-stream-once-readable-pipe.js new file mode 100644 index 00000000000000..c5722ebca4f485 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-once-readable-pipe.js @@ -0,0 +1,68 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +// This test ensures that if have 'readable' listener +// on Readable instance it will not disrupt the pipe. + +{ + let receivedData = ''; + const w = new Writable({ + write: (chunk, env, callback) => { + receivedData += chunk; + callback(); + }, + }); + + const data = ['foo', 'bar', 'baz']; + const r = new Readable({ + read: () => {}, + }); + + r.once('readable', common.mustCall()); + + r.pipe(w); + r.push(data[0]); + r.push(data[1]); + r.push(data[2]); + r.push(null); + + w.on('finish', common.mustCall(() => { + assert.strictEqual(receivedData, data.join('')); + })); +} + +{ + let receivedData = ''; + const w = new Writable({ + write: (chunk, env, callback) => { + receivedData += chunk; + callback(); + }, + }); + + const data = ['foo', 'bar', 'baz']; + const r = new Readable({ + read: () => {}, + }); + + r.pipe(w); + r.push(data[0]); + r.push(data[1]); + r.push(data[2]); + r.push(null); + r.once('readable', common.mustCall()); + + w.on('finish', common.mustCall(() => { + assert.strictEqual(receivedData, data.join('')); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-after-end.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-after-end.js new file mode 100644 index 00000000000000..bccc52e3e92537 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-after-end.js @@ -0,0 +1,76 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +class TestReadable extends Readable { + constructor(opt) { + super(opt); + this._ended = false; + } + + _read() { + if (this._ended) + this.emit('error', new Error('_read called twice')); + this._ended = true; + this.push(null); + } +} + +class TestWritable extends Writable { + constructor(opt) { + super(opt); + this._written = []; + } + + _write(chunk, encoding, cb) { + this._written.push(chunk); + cb(); + } +} + +// This one should not emit 'end' until we read() from it later. +const ender = new TestReadable(); + +// What happens when you pipe() a Readable that's already ended? +const piper = new TestReadable(); +// pushes EOF null, and length=0, so this will trigger 'end' +piper.read(); + +setTimeout(common.mustCall(function() { + ender.on('end', common.mustCall()); + const c = ender.read(); + assert.strictEqual(c, null); + + const w = new TestWritable(); + w.on('finish', common.mustCall()); + piper.pipe(w); +}), 1); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-manual-resume.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-manual-resume.js new file mode 100644 index 00000000000000..759172a443514d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-manual-resume.js @@ -0,0 +1,82 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +// A consumer stream with a very low highWaterMark, which starts in a state +// where it buffers the chunk it receives rather than indicating that they +// have been consumed. +const writable = new stream.Writable({ + highWaterMark: 5 +}); + +let isCurrentlyBufferingWrites = true; +const queue = []; + +writable._write = (chunk, encoding, cb) => { + if (isCurrentlyBufferingWrites) + queue.push({ chunk, cb }); + else + cb(); +}; + +const readable = new stream.Readable({ + read() {} +}); + +readable.pipe(writable); + +readable.once('pause', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + writable, + 'Expected awaitDrainWriters to be a Writable but instead got ' + + `${readable._readableState.awaitDrainWriters}` + ); + // First pause, resume manually. The next write() to writable will still + // return false, because chunks are still being buffered, so it will increase + // the awaitDrain counter again. + + process.nextTick(common.mustCall(() => { + readable.resume(); + })); + + readable.once('pause', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + writable, + '.resume() should not reset the awaitDrainWriters, but instead got ' + + `${readable._readableState.awaitDrainWriters}` + ); + // Second pause, handle all chunks from now on. Once all callbacks that + // are currently queued up are handled, the awaitDrain drain counter should + // fall back to 0 and all chunks that are pending on the readable side + // should be flushed. + isCurrentlyBufferingWrites = false; + for (const queued of queue) + queued.cb(); + })); +})); + +readable.push(Buffer.alloc(100)); // Fill the writable HWM, first 'pause'. +readable.push(Buffer.alloc(100)); // Second 'pause'. +readable.push(Buffer.alloc(100)); // Should get through to the writable. +readable.push(null); + +writable.on('finish', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + null, + `awaitDrainWriters should be reset to null + after all chunks are written but instead got + ${readable._readableState.awaitDrainWriters}` + ); + // Everything okay, all chunks were written. +})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-push-while-write.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-push-while-write.js new file mode 100644 index 00000000000000..b5a4eb247643fa --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain-push-while-write.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const writable = new stream.Writable({ + write: common.mustCall(function(chunk, encoding, cb) { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + null, + ); + + if (chunk.length === 32 * 1024) { // first chunk + readable.push(Buffer.alloc(34 * 1024)); // above hwm + // We should check if awaitDrain counter is increased in the next + // tick, because awaitDrain is incremented after this method finished + process.nextTick(() => { + assert.strictEqual(readable._readableState.awaitDrainWriters, writable); + }); + } + + process.nextTick(cb); + }, 3) +}); + +// A readable stream which produces two buffers. +const bufs = [Buffer.alloc(32 * 1024), Buffer.alloc(33 * 1024)]; // above hwm +const readable = new stream.Readable({ + read: function() { + while (bufs.length > 0) { + this.push(bufs.shift()); + } + } +}); + +readable.pipe(writable); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain.js new file mode 100644 index 00000000000000..351bd6f2907a16 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-await-drain.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +// This is very similar to test-stream-pipe-cleanup-pause.js. + +const reader = new stream.Readable(); +const writer1 = new stream.Writable(); +const writer2 = new stream.Writable(); +const writer3 = new stream.Writable(); + +// 560000 is chosen here because it is larger than the (default) highWaterMark +// and will cause `.write()` to return false +// See: https://github.com/nodejs/node/issues/5820 +const buffer = Buffer.allocUnsafe(560000); + +reader._read = () => {}; + +writer1._write = common.mustCall(function(chunk, encoding, cb) { + this.emit('chunk-received'); + process.nextTick(cb); +}, 1); + +writer1.once('chunk-received', () => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 0, + 'awaitDrain initial value should be 0, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + setImmediate(() => { + // This one should *not* get through to writer1 because writer2 is not + // "done" processing. + reader.push(buffer); + }); +}); + +// A "slow" consumer: +writer2._write = common.mustCall((chunk, encoding, cb) => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 1, + 'awaitDrain should be 1 after first push, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + // Not calling cb here to "simulate" slow stream. + // This should be called exactly once, since the first .write() call + // will return false. +}, 1); + +writer3._write = common.mustCall((chunk, encoding, cb) => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 2, + 'awaitDrain should be 2 after second push, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + // Not calling cb here to "simulate" slow stream. + // This should be called exactly once, since the first .write() call + // will return false. +}, 1); + +reader.pipe(writer1); +reader.pipe(writer2); +reader.pipe(writer3); +reader.push(buffer); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup-pause.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup-pause.js new file mode 100644 index 00000000000000..92a28eb96d5d15 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup-pause.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +const reader = new stream.Readable(); +const writer1 = new stream.Writable(); +const writer2 = new stream.Writable(); + +// 560000 is chosen here because it is larger than the (default) highWaterMark +// and will cause `.write()` to return false +// See: https://github.com/nodejs/node/issues/2323 +const buffer = Buffer.allocUnsafe(560000); + +reader._read = () => {}; + +writer1._write = common.mustCall(function(chunk, encoding, cb) { + this.emit('chunk-received'); + cb(); +}, 1); +writer1.once('chunk-received', function() { + reader.unpipe(writer1); + reader.pipe(writer2); + reader.push(buffer); + setImmediate(function() { + reader.push(buffer); + setImmediate(function() { + reader.push(buffer); + }); + }); +}); + +writer2._write = common.mustCall(function(chunk, encoding, cb) { + cb(); +}, 3); + +reader.pipe(writer1); +reader.push(buffer); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup.js new file mode 100644 index 00000000000000..a26914e41e6712 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-cleanup.js @@ -0,0 +1,132 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This test asserts that Stream.prototype.pipe does not leave listeners +// hanging on the source or dest. +require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function Writable() { + this.writable = true; + this.endCalls = 0; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); +Writable.prototype.end = function() { + this.endCalls++; +}; + +Writable.prototype.destroy = function() { + this.endCalls++; +}; + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +function Duplex() { + this.readable = true; + Writable.call(this); +} +Object.setPrototypeOf(Duplex.prototype, Writable.prototype); +Object.setPrototypeOf(Duplex, Writable); + +let i = 0; +const limit = 100; + +let w = new Writable(); + +let r; + +for (i = 0; i < limit; i++) { + r = new Readable(); + r.pipe(w); + r.emit('end'); +} +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(w.endCalls, limit); + +w.endCalls = 0; + +for (i = 0; i < limit; i++) { + r = new Readable(); + r.pipe(w); + r.emit('close'); +} +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(w.endCalls, limit); + +w.endCalls = 0; + +r = new Readable(); + +for (i = 0; i < limit; i++) { + w = new Writable(); + r.pipe(w); + w.emit('close'); +} +assert.strictEqual(w.listeners('close').length, 0); + +r = new Readable(); +w = new Writable(); +const d = new Duplex(); +r.pipe(d); // pipeline A +d.pipe(w); // pipeline B +assert.strictEqual(r.listeners('end').length, 2); // A.onend, A.cleanup +assert.strictEqual(r.listeners('close').length, 2); // A.onclose, A.cleanup +assert.strictEqual(d.listeners('end').length, 2); // B.onend, B.cleanup +// A.cleanup, B.onclose, B.cleanup +assert.strictEqual(d.listeners('close').length, 3); +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 1); // B.cleanup + +r.emit('end'); +assert.strictEqual(d.endCalls, 1); +assert.strictEqual(w.endCalls, 0); +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(d.listeners('end').length, 2); // B.onend, B.cleanup +assert.strictEqual(d.listeners('close').length, 2); // B.onclose, B.cleanup +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 1); // B.cleanup + +d.emit('end'); +assert.strictEqual(d.endCalls, 1); +assert.strictEqual(w.endCalls, 1); +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(d.listeners('end').length, 0); +assert.strictEqual(d.listeners('close').length, 0); +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 0); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-error-handling.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-error-handling.js new file mode 100644 index 00000000000000..6b5371479980e4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-error-handling.js @@ -0,0 +1,131 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Stream, PassThrough } = require('stream'); + +{ + const source = new Stream(); + const dest = new Stream(); + + source.pipe(dest); + + let gotErr = null; + source.on('error', function(err) { + gotErr = err; + }); + + const err = new Error('This stream turned into bacon.'); + source.emit('error', err); + assert.strictEqual(gotErr, err); +} + +{ + const source = new Stream(); + const dest = new Stream(); + + source.pipe(dest); + + const err = new Error('This stream turned into bacon.'); + + let gotErr = null; + try { + source.emit('error', err); + } catch (e) { + gotErr = e; + } + + assert.strictEqual(gotErr, err); +} + +{ + const R = Stream.Readable; + const W = Stream.Writable; + + const r = new R({ autoDestroy: false }); + const w = new W({ autoDestroy: false }); + let removed = false; + + r._read = common.mustCall(function() { + setTimeout(common.mustCall(function() { + assert(removed); + assert.throws(function() { + w.emit('error', new Error('fail')); + }, /^Error: fail$/); + }), 1); + }); + + w.on('error', myOnError); + r.pipe(w); + w.removeListener('error', myOnError); + removed = true; + + function myOnError() { + throw new Error('this should not happen'); + } +} + +{ + const R = Stream.Readable; + const W = Stream.Writable; + + const r = new R(); + const w = new W(); + let removed = false; + + r._read = common.mustCall(function() { + setTimeout(common.mustCall(function() { + assert(removed); + w.emit('error', new Error('fail')); + }), 1); + }); + + w.on('error', common.mustCall()); + w._write = () => {}; + + r.pipe(w); + // Removing some OTHER random listener should not do anything + w.removeListener('error', () => {}); + removed = true; +} + +{ + const _err = new Error('this should be handled'); + const destination = new PassThrough(); + destination.once('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + + const stream = new Stream(); + stream + .pipe(destination); + + destination.destroy(_err); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-event.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-event.js new file mode 100644 index 00000000000000..70dbdf867aa637 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-event.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function Writable() { + this.writable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +let passed = false; + +const w = new Writable(); +w.on('pipe', function(src) { + passed = true; +}); + +const r = new Readable(); +r.pipe(w); + +assert.ok(passed); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-flow-after-unpipe.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-flow-after-unpipe.js new file mode 100644 index 00000000000000..a065f506ec4d3a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-flow-after-unpipe.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Readable, Writable } = require('stream'); + +// Tests that calling .unpipe() un-blocks a stream that is paused because +// it is waiting on the writable side to finish a write(). + +const rs = new Readable({ + highWaterMark: 1, + // That this gets called at least 20 times is the real test here. + read: common.mustCallAtLeast(() => rs.push('foo'), 20) +}); + +const ws = new Writable({ + highWaterMark: 1, + write: common.mustCall(() => { + // Ignore the callback, this write() simply never finishes. + setImmediate(() => rs.unpipe(ws)); + }) +}); + +let chunks = 0; +rs.on('data', common.mustCallAtLeast(() => { + chunks++; + if (chunks >= 20) + rs.pause(); // Finish this test. +})); + +rs.pipe(ws); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-flow.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-flow.js new file mode 100644 index 00000000000000..b6c4db0675a07e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-flow.js @@ -0,0 +1,97 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable, PassThrough } = require('stream'); + +{ + let ticks = 17; + + const rs = new Readable({ + objectMode: true, + read: () => { + if (ticks-- > 0) + return process.nextTick(() => rs.push({})); + rs.push({}); + rs.push(null); + } + }); + + const ws = new Writable({ + highWaterMark: 0, + objectMode: true, + write: (data, end, cb) => setImmediate(cb) + }); + + rs.on('end', common.mustCall()); + ws.on('finish', common.mustCall()); + rs.pipe(ws); +} + +{ + let missing = 8; + + const rs = new Readable({ + objectMode: true, + read: () => { + if (missing--) rs.push({}); + else rs.push(null); + } + }); + + const pt = rs + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })) + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })); + + pt.on('end', () => { + wrapper.push(null); + }); + + const wrapper = new Readable({ + objectMode: true, + read: () => { + process.nextTick(() => { + let data = pt.read(); + if (data === null) { + pt.once('readable', () => { + data = pt.read(); + if (data !== null) wrapper.push(data); + }); + } else { + wrapper.push(data); + } + }); + } + }); + + wrapper.resume(); + wrapper.on('end', common.mustCall()); +} + +{ + // Only register drain if there is backpressure. + const rs = new Readable({ read() {} }); + + const pt = rs + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })); + assert.strictEqual(pt.listenerCount('drain'), 0); + pt.on('finish', () => { + assert.strictEqual(pt.listenerCount('drain'), 0); + }); + + rs.push('asd'); + assert.strictEqual(pt.listenerCount('drain'), 0); + + process.nextTick(() => { + rs.push('asd'); + assert.strictEqual(pt.listenerCount('drain'), 0); + rs.push(null); + assert.strictEqual(pt.listenerCount('drain'), 0); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-manual-resume.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-manual-resume.js new file mode 100644 index 00000000000000..ca120e60e66245 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-manual-resume.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +function test(throwCodeInbetween) { + // Check that a pipe does not stall if .read() is called unexpectedly + // (i.e. the stream is not resumed by the pipe). + + const n = 1000; + let counter = n; + const rs = stream.Readable({ + objectMode: true, + read: common.mustCallAtLeast(() => { + if (--counter >= 0) + rs.push({ counter }); + else + rs.push(null); + }, n) + }); + + const ws = stream.Writable({ + objectMode: true, + write: common.mustCall((data, enc, cb) => { + setImmediate(cb); + }, n) + }); + + setImmediate(() => throwCodeInbetween(rs, ws)); + + rs.pipe(ws); +} + +test((rs) => rs.read()); +test((rs) => rs.resume()); +test(() => 0); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-multiple-pipes.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-multiple-pipes.js new file mode 100644 index 00000000000000..892eb0a8570fa5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-multiple-pipes.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const readable = new stream.Readable({ + read: () => {} +}); + +const writables = []; + +for (let i = 0; i < 5; i++) { + const target = new stream.Writable({ + write: common.mustCall((chunk, encoding, callback) => { + target.output.push(chunk); + callback(); + }, 1) + }); + target.output = []; + + target.on('pipe', common.mustCall()); + readable.pipe(target); + + + writables.push(target); +} + +const input = Buffer.from([1, 2, 3, 4, 5]); + +readable.push(input); + +// The pipe() calls will postpone emission of the 'resume' event using nextTick, +// so no data will be available to the writable streams until then. +process.nextTick(common.mustCall(() => { + for (const target of writables) { + assert.deepStrictEqual(target.output, [input]); + + target.on('unpipe', common.mustCall()); + readable.unpipe(target); + } + + readable.push('something else'); // This does not get through. + readable.push(null); + readable.resume(); // Make sure the 'end' event gets emitted. +})); + +readable.on('end', common.mustCall(() => { + for (const target of writables) { + assert.deepStrictEqual(target.output, [input]); + } +})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-needDrain.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-needDrain.js new file mode 100644 index 00000000000000..3fdb4a8cdbdac3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-needDrain.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +// Pipe should pause temporarily if writable needs drain. +{ + const w = new Writable({ + write(buf, encoding, callback) { + process.nextTick(callback); + }, + highWaterMark: 1 + }); + + while (w.write('asd')); + + assert.strictEqual(w.writableNeedDrain, true); + + const r = new Readable({ + read() { + this.push('asd'); + this.push(null); + } + }); + + r.on('pause', common.mustCall(2)); + r.on('end', common.mustCall()); + + r.pipe(w); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-same-destination-twice.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-same-destination-twice.js new file mode 100644 index 00000000000000..c037e00b90a368 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-same-destination-twice.js @@ -0,0 +1,85 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// Regression test for https://github.com/nodejs/node/issues/12718. +// Tests that piping a source stream twice to the same destination stream +// works, and that a subsequent unpipe() call only removes the pipe *once*. +const assert = require('assert'); +const { PassThrough, Writable } = require('stream'); + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(`${chunk}`, 'foobar'); + cb(); + }) + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.unpipe(dest); + + assert.strictEqual(passThrough._events.data.length, 1); + assert.strictEqual(passThrough._readableState.pipes.length, 1); + assert.deepStrictEqual(passThrough._readableState.pipes, [dest]); + + passThrough.write('foobar'); + passThrough.pipe(dest); +} + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(`${chunk}`, 'foobar'); + cb(); + }, 2) + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.write('foobar'); +} + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustNotCall() + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.unpipe(dest); + passThrough.unpipe(dest); + + assert.strictEqual(passThrough._events.data, undefined); + assert.strictEqual(passThrough._readableState.pipes.length, 0); + + passThrough.write('foobar'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-unpipe-streams.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-unpipe-streams.js new file mode 100644 index 00000000000000..3c3f19bd43aef9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-unpipe-streams.js @@ -0,0 +1,103 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable, Writable } = require('stream'); + +const source = Readable({ read: () => {} }); +const dest1 = Writable({ write: () => {} }); +const dest2 = Writable({ write: () => {} }); + +source.pipe(dest1); +source.pipe(dest2); + +dest1.on('unpipe', common.mustCall()); +dest2.on('unpipe', common.mustCall()); + +assert.strictEqual(source._readableState.pipes[0], dest1); +assert.strictEqual(source._readableState.pipes[1], dest2); +assert.strictEqual(source._readableState.pipes.length, 2); + +// Should be able to unpipe them in the reverse order that they were piped. + +source.unpipe(dest2); + +assert.deepStrictEqual(source._readableState.pipes, [dest1]); +assert.notStrictEqual(source._readableState.pipes, dest2); + +dest2.on('unpipe', common.mustNotCall()); +source.unpipe(dest2); + +source.unpipe(dest1); + +assert.strictEqual(source._readableState.pipes.length, 0); + +{ + // Test `cleanup()` if we unpipe all streams. + const source = Readable({ read: () => {} }); + const dest1 = Writable({ write: () => {} }); + const dest2 = Writable({ write: () => {} }); + + let destCount = 0; + const srcCheckEventNames = ['end', 'data']; + const destCheckEventNames = ['close', 'finish', 'drain', 'error', 'unpipe']; + + const checkSrcCleanup = common.mustCall(() => { + assert.strictEqual(source._readableState.pipes.length, 0); + assert.strictEqual(source._readableState.flowing, false); + + srcCheckEventNames.forEach((eventName) => { + assert.strictEqual( + source.listenerCount(eventName), 0, + `source's '${eventName}' event listeners not removed` + ); + }); + }); + + function checkDestCleanup(dest) { + const currentDestId = ++destCount; + source.pipe(dest); + + const unpipeChecker = common.mustCall(() => { + assert.deepStrictEqual( + dest.listeners('unpipe'), [unpipeChecker], + `destination{${currentDestId}} should have a 'unpipe' event ` + + 'listener which is `unpipeChecker`' + ); + dest.removeListener('unpipe', unpipeChecker); + destCheckEventNames.forEach((eventName) => { + assert.strictEqual( + dest.listenerCount(eventName), 0, + `destination{${currentDestId}}'s '${eventName}' event ` + + 'listeners not removed' + ); + }); + + if (--destCount === 0) + checkSrcCleanup(); + }); + + dest.on('unpipe', unpipeChecker); + } + + checkDestCleanup(dest1); + checkDestCleanup(dest2); + source.unpipe(); +} + +{ + const src = Readable({ read: () => {} }); + const dst = Writable({ write: () => {} }); + src.pipe(dst); + src.on('resume', common.mustCall(() => { + src.on('pause', common.mustCall()); + src.unpipe(dst); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipe-without-listenerCount.js b/cli/tests/node_compat/test/parallel/test-stream-pipe-without-listenerCount.js new file mode 100644 index 00000000000000..b27d966d8522f3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipe-without-listenerCount.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +const r = new stream.Stream(); +r.listenerCount = undefined; + +const w = new stream.Stream(); +w.listenerCount = undefined; + +w.on('pipe', function() { + r.emit('error', new Error('Readable Error')); + w.emit('error', new Error('Writable Error')); +}); +r.on('error', common.mustCall()); +w.on('error', common.mustCall()); +r.pipe(w); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipeline-async-iterator.js b/cli/tests/node_compat/test/parallel/test-stream-pipeline-async-iterator.js new file mode 100644 index 00000000000000..d2498d48c04dba --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipeline-async-iterator.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Readable, PassThrough, pipeline } = require('stream'); +const assert = require('assert'); + +const _err = new Error('kaboom'); + +async function run() { + const source = new Readable({ + read() { + } + }); + source.push('hello'); + source.push('world'); + + setImmediate(() => { source.destroy(_err); }); + + const iterator = pipeline( + source, + new PassThrough(), + () => {}); + + iterator.setEncoding('utf8'); + + for await (const k of iterator) { + assert.strictEqual(k, 'helloworld'); + } +} + +run().catch(common.mustCall((err) => assert.strictEqual(err, _err))); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipeline-queued-end-in-destroy.js b/cli/tests/node_compat/test/parallel/test-stream-pipeline-queued-end-in-destroy.js new file mode 100644 index 00000000000000..b6a921de1f0728 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipeline-queued-end-in-destroy.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Duplex, pipeline } = require('stream'); + +// Test that the callback for pipeline() is called even when the ._destroy() +// method of the stream places an .end() request to itself that does not +// get processed before the destruction of the stream (i.e. the 'close' event). +// Refs: https://github.com/nodejs/node/issues/24456 + +const readable = new Readable({ + read: common.mustCall(() => {}) +}); + +const duplex = new Duplex({ + write(chunk, enc, cb) { + // Simulate messages queueing up. + }, + read() {}, + destroy(err, cb) { + // Call end() from inside the destroy() method, like HTTP/2 streams + // do at the time of writing. + this.end(); + cb(err); + } +}); + +duplex.on('finished', common.mustNotCall()); + +pipeline(readable, duplex, common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_PREMATURE_CLOSE'); +})); + +// Write one chunk of data, and destroy the stream later. +// That should trigger the pipeline destruction. +readable.push('foo'); +setImmediate(() => { + readable.destroy(); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-pipeline-with-empty-string.js b/cli/tests/node_compat/test/parallel/test-stream-pipeline-with-empty-string.js new file mode 100644 index 00000000000000..06dcccae8e9e34 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-pipeline-with-empty-string.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { + pipeline, + PassThrough +} = require('stream'); + + +async function runTest() { + await pipeline( + '', + new PassThrough({ objectMode: true }), + common.mustCall(() => { }) + ); +} + +runTest().then(common.mustCall(() => {})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-push-strings.js b/cli/tests/node_compat/test/parallel/test-stream-push-strings.js new file mode 100644 index 00000000000000..89ace90811e3af --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-push-strings.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const Readable = require('stream').Readable; + +class MyStream extends Readable { + constructor(options) { + super(options); + this._chunks = 3; + } + + _read(n) { + switch (this._chunks--) { + case 0: + return this.push(null); + case 1: + return setTimeout(() => { + this.push('last chunk'); + }, 100); + case 2: + return this.push('second to last chunk'); + case 3: + return process.nextTick(() => { + this.push('first chunk'); + }); + default: + throw new Error('?'); + } + } +} + +const ms = new MyStream(); +const results = []; +ms.on('readable', function() { + let chunk; + while (null !== (chunk = ms.read())) + results.push(String(chunk)); +}); + +const expect = [ 'first chunksecond to last chunk', 'last chunk' ]; +process.on('exit', function() { + assert.strictEqual(ms._chunks, -1); + assert.deepStrictEqual(results, expect); + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-aborted.js b/cli/tests/node_compat/test/parallel/test-stream-readable-aborted.js new file mode 100644 index 00000000000000..8209ee6739828e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-aborted.js @@ -0,0 +1,73 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Duplex } = require('stream'); + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push(null); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push('asd'); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push('asd'); + readable.push(null); + assert.strictEqual(readable.readableAborted, false); + readable.on('end', common.mustCall(() => { + assert.strictEqual(readable.readableAborted, false); + readable.destroy(); + assert.strictEqual(readable.readableAborted, false); + queueMicrotask(() => { + assert.strictEqual(readable.readableAborted, false); + }); + })); + readable.resume(); +} + +{ + const duplex = new Duplex({ + readable: false, + write() {} + }); + duplex.destroy(); + assert.strictEqual(duplex.readableAborted, false); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-add-chunk-during-data.js b/cli/tests/node_compat/test/parallel/test-stream-readable-add-chunk-during-data.js new file mode 100644 index 00000000000000..e16312b0c5267f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-add-chunk-during-data.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +// Verify that .push() and .unshift() can be called from 'data' listeners. + +for (const method of ['push', 'unshift']) { + const r = new Readable({ read() {} }); + r.once('data', common.mustCall((chunk) => { + assert.strictEqual(r.readableLength, 0); + r[method](chunk); + assert.strictEqual(r.readableLength, chunk.length); + + r.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(), 'Hello, world'); + })); + })); + + r.push('Hello, world'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-constructor-set-methods.js b/cli/tests/node_compat/test/parallel/test-stream-readable-constructor-set-methods.js new file mode 100644 index 00000000000000..1f6f14ce3cf9cb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-constructor-set-methods.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const Readable = require('stream').Readable; + +const _read = common.mustCall(function _read(n) { + this.push(null); +}); + +const r = new Readable({ read: _read }); +r.resume(); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-data.js b/cli/tests/node_compat/test/parallel/test-stream-readable-data.js new file mode 100644 index 00000000000000..34928c9671a8b6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-data.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); + +const readable = new Readable({ + read() {} +}); + +function read() {} + +readable.setEncoding('utf8'); +readable.on('readable', read); +readable.removeListener('readable', read); + +process.nextTick(function() { + readable.on('data', common.mustCall()); + readable.push('hello'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-destroy.js b/cli/tests/node_compat/test/parallel/test-stream-readable-destroy.js new file mode 100644 index 00000000000000..642c19d600ab6f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-destroy.js @@ -0,0 +1,412 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Readable, addAbortSignal } = require('stream'); +const assert = require('assert'); + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read.on('close', common.mustCall()); + + read.destroy(); + assert.strictEqual(read.errored, null); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + read.on('close', common.mustCall()); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + read.destroy(expected); + assert.strictEqual(read.errored, expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(err); + }); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + read.on('close', common.mustCall()); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + read.destroy(expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {}, + destroy: common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(); + }) + }); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + + // Error is swallowed by the custom _destroy + read.on('error', common.mustNotCall('no error event')); + read.on('close', common.mustCall()); + + read.destroy(expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(); + }); + + read.destroy(); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + process.nextTick(() => { + this.push(null); + cb(); + }); + }); + + const fail = common.mustNotCall('no end event'); + + read.on('end', fail); + read.on('close', common.mustCall()); + + read.destroy(); + + read.removeListener('end', fail); + read.on('end', common.mustNotCall()); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + const expected = new Error('kaboom'); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(expected); + }); + + let ticked = false; + read.on('end', common.mustNotCall('no end event')); + read.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(read._readableState.errorEmitted, true); + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(err, expected); + })); + + read.destroy(); + assert.strictEqual(read._readableState.errorEmitted, false); + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(read.destroyed, true); + ticked = true; +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read.destroyed = true; + assert.strictEqual(read.destroyed, true); + + // The internal destroy() mechanism should not be triggered + read.on('end', common.mustNotCall()); + read.destroy(); +} + +{ + function MyReadable() { + assert.strictEqual(this.destroyed, false); + this.destroyed = false; + Readable.call(this); + } + + Object.setPrototypeOf(MyReadable.prototype, Readable.prototype); + Object.setPrototypeOf(MyReadable, Readable); + + new MyReadable(); +} + +{ + // Destroy and destroy callback + const read = new Readable({ + read() {} + }); + read.resume(); + + const expected = new Error('kaboom'); + + let ticked = false; + read.on('close', common.mustCall(() => { + assert.strictEqual(read._readableState.errorEmitted, true); + assert.strictEqual(ticked, true); + })); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + assert.strictEqual(read._readableState.errored, null); + assert.strictEqual(read._readableState.errorEmitted, false); + + read.destroy(expected, common.mustCall(function(err) { + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(err, expected); + })); + assert.strictEqual(read._readableState.errorEmitted, false); + assert.strictEqual(read._readableState.errored, expected); + ticked = true; +} + +{ + const readable = new Readable({ + destroy: common.mustCall(function(err, cb) { + process.nextTick(cb, new Error('kaboom 1')); + }), + read() {} + }); + + let ticked = false; + readable.on('close', common.mustCall(() => { + assert.strictEqual(ticked, true); + assert.strictEqual(readable._readableState.errorEmitted, true); + })); + readable.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.message, 'kaboom 1'); + assert.strictEqual(readable._readableState.errorEmitted, true); + })); + + readable.destroy(); + assert.strictEqual(readable.destroyed, true); + assert.strictEqual(readable._readableState.errored, null); + assert.strictEqual(readable._readableState.errorEmitted, false); + + // Test case where `readable.destroy()` is called again with an error before + // the `_destroy()` callback is called. + readable.destroy(new Error('kaboom 2')); + assert.strictEqual(readable._readableState.errorEmitted, false); + assert.strictEqual(readable._readableState.errored, null); + + ticked = true; +} + +{ + const read = new Readable({ + read() {} + }); + + read.destroy(); + read.push('hi'); + read.on('data', common.mustNotCall()); +} + +{ + const read = new Readable({ + read: common.mustNotCall(function() {}) + }); + read.destroy(); + assert.strictEqual(read.destroyed, true); + read.read(); +} + +{ + const read = new Readable({ + autoDestroy: false, + read() { + this.push(null); + this.push('asd'); + } + }); + + read.on('error', common.mustCall(() => { + assert(read._readableState.errored); + })); + read.resume(); +} + +{ + const controller = new AbortController(); + const read = addAbortSignal(controller.signal, new Readable({ + read() { + this.push('asd'); + }, + })); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + controller.abort(); + read.on('data', common.mustNotCall()); +} + +{ + const controller = new AbortController(); + const read = new Readable({ + signal: controller.signal, + read() { + this.push('asd'); + }, + }); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + controller.abort(); + read.on('data', common.mustNotCall()); +} + +{ + const controller = new AbortController(); + const read = addAbortSignal(controller.signal, new Readable({ + objectMode: true, + read() { + return false; + } + })); + read.push('asd'); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + assert.rejects((async () => { + // eslint-disable-next-line no-unused-vars, no-empty + for await (const chunk of read) { } + })(), /AbortError/); + setTimeout(() => controller.abort(), 0); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('error', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(new Error('asd')); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.unshift('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.unshift('asd'); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.resume(); + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.push('asd'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-didRead.js b/cli/tests/node_compat/test/parallel/test-stream-readable-didRead.js new file mode 100644 index 00000000000000..0c0d7b0c3bba60 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-didRead.js @@ -0,0 +1,118 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { isDisturbed, isErrored, Readable } = require('stream'); + +function noop() {} + +function check(readable, data, fn) { + assert.strictEqual(readable.readableDidRead, false); + assert.strictEqual(isDisturbed(readable), false); + assert.strictEqual(isErrored(readable), false); + if (data === -1) { + readable.on('error', common.mustCall(() => { + assert.strictEqual(isErrored(readable), true); + })); + readable.on('data', common.mustNotCall()); + readable.on('end', common.mustNotCall()); + } else { + readable.on('error', common.mustNotCall()); + if (data === -2) { + readable.on('end', common.mustNotCall()); + } else { + readable.on('end', common.mustCall()); + } + if (data > 0) { + readable.on('data', common.mustCallAtLeast(data)); + } else { + readable.on('data', common.mustNotCall()); + } + } + readable.on('close', common.mustCall()); + fn(); + setImmediate(() => { + assert.strictEqual(readable.readableDidRead, data > 0); + if (data > 0) { + assert.strictEqual(isDisturbed(readable), true); + } + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, 0, () => { + readable.read(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, 0, () => { + readable.resume(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, -2, () => { + readable.destroy(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + + check(readable, -1, () => { + readable.destroy(new Error()); + }); +} + +{ + const readable = new Readable({ + read() { + this.push('data'); + this.push(null); + } + }); + + check(readable, 1, () => { + readable.on('data', noop); + }); +} + +{ + const readable = new Readable({ + read() { + this.push('data'); + this.push(null); + } + }); + + check(readable, 1, () => { + readable.on('data', noop); + readable.off('data', noop); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-emit-readable-short-stream.js b/cli/tests/node_compat/test/parallel/test-stream-readable-emit-readable-short-stream.js new file mode 100644 index 00000000000000..543199d2cf1866 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-emit-readable-short-stream.js @@ -0,0 +1,153 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + read: common.mustCall(function() { + this.push('content'); + this.push(null); + }) + }); + + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + r.pipe(t); + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.end('content'); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.write('content'); + t.end(); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); +} + +{ + const t = new stream.Readable({ + read() { + } + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); + + t.push('content'); + t.push(null); +} + +{ + const t = new stream.Readable({ + read() { + } + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); + + process.nextTick(() => { + t.push('content'); + t.push(null); + }); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); + + t.write('content'); + t.end(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-emittedReadable.js b/cli/tests/node_compat/test/parallel/test-stream-readable-emittedReadable.js new file mode 100644 index 00000000000000..7bab366eea37b4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-emittedReadable.js @@ -0,0 +1,80 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +const readable = new Readable({ + read: () => {} +}); + +// Initialized to false. +assert.strictEqual(readable._readableState.emittedReadable, false); + +const expected = [Buffer.from('foobar'), Buffer.from('quo'), null]; +readable.on('readable', common.mustCall(() => { + // emittedReadable should be true when the readable event is emitted + assert.strictEqual(readable._readableState.emittedReadable, true); + assert.deepStrictEqual(readable.read(), expected.shift()); + // emittedReadable is reset to false during read() + assert.strictEqual(readable._readableState.emittedReadable, false); +}, 3)); + +// When the first readable listener is just attached, +// emittedReadable should be false +assert.strictEqual(readable._readableState.emittedReadable, false); + +// These trigger a single 'readable', as things are batched up +process.nextTick(common.mustCall(() => { + readable.push('foo'); +})); +process.nextTick(common.mustCall(() => { + readable.push('bar'); +})); + +// These triggers two readable events +setImmediate(common.mustCall(() => { + readable.push('quo'); + process.nextTick(common.mustCall(() => { + readable.push(null); + })); +})); + +const noRead = new Readable({ + read: () => {} +}); + +noRead.on('readable', common.mustCall(() => { + // emittedReadable should be true when the readable event is emitted + assert.strictEqual(noRead._readableState.emittedReadable, true); + noRead.read(0); + // emittedReadable is not reset during read(0) + assert.strictEqual(noRead._readableState.emittedReadable, true); +})); + +noRead.push('foo'); +noRead.push(null); + +const flowing = new Readable({ + read: () => {} +}); + +flowing.on('data', common.mustCall(() => { + // When in flowing mode, emittedReadable is always false. + assert.strictEqual(flowing._readableState.emittedReadable, false); + flowing.read(); + assert.strictEqual(flowing._readableState.emittedReadable, false); +}, 3)); + +flowing.push('foooo'); +flowing.push('bar'); +flowing.push('quo'); +process.nextTick(common.mustCall(() => { + flowing.push(null); +})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-end-destroyed.js b/cli/tests/node_compat/test/parallel/test-stream-readable-end-destroyed.js new file mode 100644 index 00000000000000..4f2bfd1f2eaadd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-end-destroyed.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +{ + // Don't emit 'end' after 'close'. + + const r = new Readable(); + + r.on('end', common.mustNotCall()); + r.resume(); + r.destroy(); + r.on('close', common.mustCall(() => { + r.push(null); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-ended.js b/cli/tests/node_compat/test/parallel/test-stream-readable-ended.js new file mode 100644 index 00000000000000..5687cb4cf5b131 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-ended.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Readable.prototype + assert(Object.hasOwn(Readable.prototype, 'readableEnded')); +} + +// event +{ + const readable = new Readable(); + + readable._read = () => { + // The state ended should start in false. + assert.strictEqual(readable.readableEnded, false); + readable.push('asd'); + assert.strictEqual(readable.readableEnded, false); + readable.push(null); + assert.strictEqual(readable.readableEnded, false); + }; + + readable.on('end', common.mustCall(() => { + assert.strictEqual(readable.readableEnded, true); + })); + + readable.on('data', common.mustCall(() => { + assert.strictEqual(readable.readableEnded, false); + })); +} + +// Verifies no `error` triggered on multiple .push(null) invocations +{ + const readable = new Readable(); + + readable.on('readable', () => { readable.read(); }); + readable.on('error', common.mustNotCall()); + readable.on('end', common.mustCall()); + + readable.push('a'); + readable.push(null); + readable.push(null); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-error-end.js b/cli/tests/node_compat/test/parallel/test-stream-readable-error-end.js new file mode 100644 index 00000000000000..fbc6cfae8125b2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-error-end.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +{ + const r = new Readable({ read() {} }); + + r.on('end', common.mustNotCall()); + r.on('data', common.mustCall()); + r.on('error', common.mustCall()); + r.push('asd'); + r.push(null); + r.destroy(new Error('kaboom')); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-event.js b/cli/tests/node_compat/test/parallel/test-stream-readable-event.js new file mode 100644 index 00000000000000..a5964ab2c24f17 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-event.js @@ -0,0 +1,135 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const Readable = require('stream').Readable; + +{ + // First test, not reading when the readable is added. + // make sure that on('readable', ...) triggers a readable event. + const r = new Readable({ + highWaterMark: 3 + }); + + r._read = common.mustNotCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('blerg')); + + setTimeout(function() { + // We're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Second test, make sure that readable is re-emitted if there's + // already a length, while it IS reading. + + const r = new Readable({ + highWaterMark: 3 + }); + + r._read = common.mustCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('bl')); + + setTimeout(function() { + // Assert we're testing what we think we are + assert(r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Third test, not reading when the stream has not passed + // the highWaterMark but *has* reached EOF. + const r = new Readable({ + highWaterMark: 30 + }); + + r._read = common.mustNotCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('blerg')); + r.push(null); + + setTimeout(function() { + // Assert we're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Pushing an empty string in non-objectMode should + // trigger next `read()`. + const underlyingData = ['', 'x', 'y', '', 'z']; + const expected = underlyingData.filter((data) => data); + const result = []; + + const r = new Readable({ + encoding: 'utf8', + }); + r._read = function() { + process.nextTick(() => { + if (!underlyingData.length) { + this.push(null); + } else { + this.push(underlyingData.shift()); + } + }); + }; + + r.on('readable', () => { + const data = r.read(); + if (data !== null) result.push(data); + }); + + r.on('end', common.mustCall(() => { + assert.deepStrictEqual(result, expected); + })); +} + +{ + // #20923 + const r = new Readable(); + r._read = function() { + // Actually doing thing here + }; + r.on('data', function() {}); + + r.removeAllListeners(); + + assert.strictEqual(r.eventNames().length, 0); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-flow-recursion.js b/cli/tests/node_compat/test/parallel/test-stream-readable-flow-recursion.js new file mode 100644 index 00000000000000..d37a22af711758 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-flow-recursion.js @@ -0,0 +1,84 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This test verifies that passing a huge number to read(size) +// will push up the highWaterMark, and cause the stream to read +// more data continuously, but without triggering a nextTick +// warning or RangeError. + +const Readable = require('stream').Readable; + +// Throw an error if we trigger a nextTick warning. +process.throwDeprecation = true; + +const stream = new Readable({ highWaterMark: 2 }); +let reads = 0; +let total = 5000; +stream._read = function(size) { + reads++; + size = Math.min(size, total); + total -= size; + if (size === 0) + stream.push(null); + else + stream.push(Buffer.allocUnsafe(size)); +}; + +let depth = 0; + +function flow(stream, size, callback) { + depth += 1; + const chunk = stream.read(size); + + if (!chunk) + stream.once('readable', flow.bind(null, stream, size, callback)); + else + callback(chunk); + + depth -= 1; + console.log(`flow(${depth}): exit`); +} + +flow(stream, 5000, function() { + console.log(`complete (${depth})`); +}); + +process.on('exit', function(code) { + assert.strictEqual(reads, 2); + // We pushed up the high water mark + assert.strictEqual(stream.readableHighWaterMark, 8192); + // Length is 0 right now, because we pulled it all out. + assert.strictEqual(stream.readableLength, 0); + assert(!code); + assert.strictEqual(depth, 0); + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-async.js b/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-async.js new file mode 100644 index 00000000000000..6ee2b625a57bc8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-async.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +// This test ensures that Readable stream will continue to call _read +// for streams with highWaterMark === 0 once the stream returns data +// by calling push() asynchronously. + +const { Readable } = require('stream'); + +let count = 5; + +const r = new Readable({ + // Called 6 times: First 5 return data, last one signals end of stream. + read: common.mustCall(() => { + process.nextTick(common.mustCall(() => { + if (count--) + r.push('a'); + else + r.push(null); + })); + }, 6), + highWaterMark: 0, +}); + +r.on('end', common.mustCall()); +r.on('data', common.mustCall(5)); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-no-flow-data.js b/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-no-flow-data.js new file mode 100644 index 00000000000000..38b69d2d90de94 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0-no-flow-data.js @@ -0,0 +1,111 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +// Ensure that subscribing the 'data' event will not make the stream flow. +// The 'data' event will require calling read() by hand. +// +// The test is written for the (somewhat rare) highWaterMark: 0 streams to +// specifically catch any regressions that might occur with these streams. + +const assert = require('assert'); +const { Readable } = require('stream'); + +const streamData = [ 'a', null ]; + +// Track the calls so we can assert their order later. +const calls = []; +const r = new Readable({ + read: common.mustCall(() => { + calls.push('_read:' + streamData[0]); + process.nextTick(() => { + calls.push('push:' + streamData[0]); + r.push(streamData.shift()); + }); + }, streamData.length), + highWaterMark: 0, + + // Object mode is used here just for testing convenience. It really + // shouldn't affect the order of events. Just the data and its format. + objectMode: true, +}); + +assert.strictEqual(r.readableFlowing, null); +r.on('readable', common.mustCall(() => { + calls.push('readable'); +}, 2)); +assert.strictEqual(r.readableFlowing, false); +r.on('data', common.mustCall((data) => { + calls.push('data:' + data); +}, 1)); +r.on('end', common.mustCall(() => { + calls.push('end'); +})); +assert.strictEqual(r.readableFlowing, false); + +// The stream emits the events asynchronously but that's not guaranteed to +// happen on the next tick (especially since the _read implementation above +// uses process.nextTick). +// +// We use setImmediate here to give the stream enough time to emit all the +// events it's about to emit. +setImmediate(() => { + + // Only the _read, push, readable calls have happened. No data must be + // emitted yet. + assert.deepStrictEqual(calls, ['_read:a', 'push:a', 'readable']); + + // Calling 'r.read()' should trigger the data event. + assert.strictEqual(r.read(), 'a'); + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a']); + + // The next 'read()' will return null because hwm: 0 does not buffer any + // data and the _read implementation above does the push() asynchronously. + // + // Note: This 'null' signals "no data available". It isn't the end-of-stream + // null value as the stream doesn't know yet that it is about to reach the + // end. + // + // Using setImmediate again to give the stream enough time to emit all the + // events it wants to emit. + assert.strictEqual(r.read(), null); + setImmediate(() => { + + // There's a new 'readable' event after the data has been pushed. + // The 'end' event will be emitted only after a 'read()'. + // + // This is somewhat special for the case where the '_read' implementation + // calls 'push' asynchronously. If 'push' was synchronous, the 'end' event + // would be emitted here _before_ we call read(). + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable']); + + assert.strictEqual(r.read(), null); + + // While it isn't really specified whether the 'end' event should happen + // synchronously with read() or not, we'll assert the current behavior + // ('end' event happening on the next tick after read()) so any changes + // to it are noted and acknowledged in the future. + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable']); + process.nextTick(() => { + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable', 'end']); + }); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0.js b/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0.js new file mode 100644 index 00000000000000..17a9c05e568f9d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-hwm-0.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +// This test ensures that Readable stream will call _read() for streams +// with highWaterMark === 0 upon .read(0) instead of just trying to +// emit 'readable' event. + +const assert = require('assert'); +const { Readable } = require('stream'); + +const r = new Readable({ + // Must be called only once upon setting 'readable' listener + read: common.mustCall(), + highWaterMark: 0, +}); + +let pushedNull = false; +// This will trigger read(0) but must only be called after push(null) +// because the we haven't pushed any data +r.on('readable', common.mustCall(() => { + assert.strictEqual(r.read(), null); + assert.strictEqual(pushedNull, true); +})); +r.on('end', common.mustCall()); +process.nextTick(() => { + assert.strictEqual(r.read(), null); + pushedNull = true; + r.push(null); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-infinite-read.js b/cli/tests/node_compat/test/parallel/test-stream-readable-infinite-read.js new file mode 100644 index 00000000000000..5b5b8275eb61cf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-infinite-read.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const buf = Buffer.alloc(8192); + +const readable = new Readable({ + read: common.mustCall(function() { + this.push(buf); + }, 31) +}); + +let i = 0; + +readable.on('readable', common.mustCall(function() { + if (i++ === 10) { + // We will just terminate now. + process.removeAllListeners('readable'); + return; + } + + const data = readable.read(); + // TODO(mcollina): there is something odd in the highWaterMark logic + // investigate. + if (i === 1) { + assert.strictEqual(data.length, 8192 * 2); + } else { + assert.strictEqual(data.length, 8192 * 3); + } +}, 11)); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-invalid-chunk.js b/cli/tests/node_compat/test/parallel/test-stream-readable-invalid-chunk.js new file mode 100644 index 00000000000000..ffbe64a8af537c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-invalid-chunk.js @@ -0,0 +1,41 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const stream = require('stream'); + +function testPushArg(val) { + const readable = new stream.Readable({ + read: () => {} + }); + readable.on('error', common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + })); + readable.push(val); +} + +testPushArg([]); +testPushArg({}); +testPushArg(0); + +function testUnshiftArg(val) { + const readable = new stream.Readable({ + read: () => {} + }); + readable.on('error', common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + })); + readable.unshift(val); +} + +testUnshiftArg([]); +testUnshiftArg({}); +testUnshiftArg(0); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-needReadable.js b/cli/tests/node_compat/test/parallel/test-stream-readable-needReadable.js new file mode 100644 index 00000000000000..49051aed321ae0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-needReadable.js @@ -0,0 +1,106 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +const readable = new Readable({ + read: () => {} +}); + +// Initialized to false. +assert.strictEqual(readable._readableState.needReadable, false); + +readable.on('readable', common.mustCall(() => { + // When the readable event fires, needReadable is reset. + assert.strictEqual(readable._readableState.needReadable, false); + readable.read(); +})); + +// If a readable listener is attached, then a readable event is needed. +assert.strictEqual(readable._readableState.needReadable, true); + +readable.push('foo'); +readable.push(null); + +readable.on('end', common.mustCall(() => { + // No need to emit readable anymore when the stream ends. + assert.strictEqual(readable._readableState.needReadable, false); +})); + +const asyncReadable = new Readable({ + read: () => {} +}); + +asyncReadable.on('readable', common.mustCall(() => { + if (asyncReadable.read() !== null) { + // After each read(), the buffer is empty. + // If the stream doesn't end now, + // then we need to notify the reader on future changes. + assert.strictEqual(asyncReadable._readableState.needReadable, true); + } +}, 2)); + +process.nextTick(common.mustCall(() => { + asyncReadable.push('foooo'); +})); +process.nextTick(common.mustCall(() => { + asyncReadable.push('bar'); +})); +setImmediate(common.mustCall(() => { + asyncReadable.push(null); + assert.strictEqual(asyncReadable._readableState.needReadable, false); +})); + +const flowing = new Readable({ + read: () => {} +}); + +// Notice this must be above the on('data') call. +flowing.push('foooo'); +flowing.push('bar'); +flowing.push('quo'); +process.nextTick(common.mustCall(() => { + flowing.push(null); +})); + +// When the buffer already has enough data, and the stream is +// in flowing mode, there is no need for the readable event. +flowing.on('data', common.mustCall(function(data) { + assert.strictEqual(flowing._readableState.needReadable, false); +}, 3)); + +const slowProducer = new Readable({ + read: () => {} +}); + +slowProducer.on('readable', common.mustCall(() => { + const chunk = slowProducer.read(8); + const state = slowProducer._readableState; + if (chunk === null) { + // The buffer doesn't have enough data, and the stream is not need, + // we need to notify the reader when data arrives. + assert.strictEqual(state.needReadable, true); + } else { + assert.strictEqual(state.needReadable, false); + } +}, 4)); + +process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push(null); + })); + })); + })); +})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-next-no-null.js b/cli/tests/node_compat/test/parallel/test-stream-readable-next-no-null.js new file mode 100644 index 00000000000000..4b48d3daaa1e07 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-next-no-null.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const { mustNotCall, expectsError } = require('../common'); +const { Readable } = require('stream'); + +async function* generate() { + yield null; +} + +const stream = Readable.from(generate()); + +stream.on('error', expectsError({ + code: 'ERR_STREAM_NULL_VALUES', + name: 'TypeError', + message: 'May not write null values to stream' +})); + +stream.on('data', mustNotCall((chunk) => {})); + +stream.on('end', mustNotCall()); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-no-unneeded-readable.js b/cli/tests/node_compat/test/parallel/test-stream-readable-no-unneeded-readable.js new file mode 100644 index 00000000000000..5c1064479638b7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-no-unneeded-readable.js @@ -0,0 +1,69 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Readable, PassThrough } = require('stream'); + +function test(r) { + const wrapper = new Readable({ + read: () => { + let data = r.read(); + + if (data) { + wrapper.push(data); + return; + } + + r.once('readable', function() { + data = r.read(); + if (data) { + wrapper.push(data); + } + // else: the end event should fire + }); + }, + }); + + r.once('end', function() { + wrapper.push(null); + }); + + wrapper.resume(); + wrapper.once('end', common.mustCall()); +} + +{ + const source = new Readable({ + read: () => {} + }); + source.push('foo'); + source.push('bar'); + source.push(null); + + const pt = source.pipe(new PassThrough()); + test(pt); +} + +{ + // This is the underlying cause of the above test case. + const pushChunks = ['foo', 'bar']; + const r = new Readable({ + read: () => { + const chunk = pushChunks.shift(); + if (chunk) { + // synchronous call + r.push(chunk); + } else { + // asynchronous call + process.nextTick(() => r.push(null)); + } + }, + }); + + test(r); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-object-multi-push-async.js b/cli/tests/node_compat/test/parallel/test-stream-readable-object-multi-push-async.js new file mode 100644 index 00000000000000..543226e9811b09 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-object-multi-push-async.js @@ -0,0 +1,190 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const MAX = 42; +const BATCH = 10; + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + if (data.length === 0) { + console.log('pushing null'); + this.push(null); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + }); + }, Math.floor(MAX / BATCH) + 2) + }); + + let i = 0; + function fetchData(cb) { + if (i > MAX) { + setTimeout(cb, 10, null, []); + } else { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + } + + readable.on('readable', () => { + let data; + console.log('readable emitted'); + while ((data = readable.read()) !== null) { + console.log(data); + } + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + if (data.length === 0) { + console.log('pushing null'); + this.push(null); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + }); + }, Math.floor(MAX / BATCH) + 2) + }); + + let i = 0; + function fetchData(cb) { + if (i > MAX) { + setTimeout(cb, 10, null, []); + } else { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + } + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + + if (data[BATCH - 1] >= MAX) { + console.log('pushing null'); + this.push(null); + } + }); + }, Math.floor(MAX / BATCH) + 1) + }); + + let i = 0; + function fetchData(cb) { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustNotCall() + }); + + readable.on('data', common.mustNotCall()); + + readable.push(null); + + let nextTickPassed = false; + process.nextTick(() => { + nextTickPassed = true; + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(nextTickPassed, true); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall() + }); + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall()); + + setImmediate(() => { + readable.push('aaa'); + readable.push(null); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-pause-and-resume.js b/cli/tests/node_compat/test/parallel/test-stream-readable-pause-and-resume.js new file mode 100644 index 00000000000000..6c57b0a70a5673 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-pause-and-resume.js @@ -0,0 +1,81 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +let ticks = 18; +let expectedData = 19; + +const rs = new Readable({ + objectMode: true, + read: () => { + if (ticks-- > 0) + return process.nextTick(() => rs.push({})); + rs.push({}); + rs.push(null); + } +}); + +rs.on('end', common.mustCall()); +readAndPause(); + +function readAndPause() { + // Does a on(data) -> pause -> wait -> resume -> on(data) ... loop. + // Expects on(data) to never fire if the stream is paused. + const ondata = common.mustCall((data) => { + rs.pause(); + + expectedData--; + if (expectedData <= 0) + return; + + setImmediate(function() { + rs.removeListener('data', ondata); + readAndPause(); + rs.resume(); + }); + }, 1); // Only call ondata once + + rs.on('data', ondata); +} + +{ + const readable = new Readable({ + read() {} + }); + + function read() {} + + readable.setEncoding('utf8'); + readable.on('readable', read); + readable.removeListener('readable', read); + readable.pause(); + + process.nextTick(function() { + assert(readable.isPaused()); + }); +} + +{ + const { PassThrough } = require('stream'); + + const source3 = new PassThrough(); + const target3 = new PassThrough(); + + const chunk = Buffer.allocUnsafe(1000); + while (target3.write(chunk)); + + source3.pipe(target3); + target3.on('drain', common.mustCall(() => { + assert(!source3.isPaused()); + })); + target3.on('data', () => {}); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-readable-then-resume.js b/cli/tests/node_compat/test/parallel/test-stream-readable-readable-then-resume.js new file mode 100644 index 00000000000000..f59b635b2b96ac --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-readable-then-resume.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +// This test verifies that a stream could be resumed after +// removing the readable event in the same tick + +check(new Readable({ + objectMode: true, + highWaterMark: 1, + read() { + if (!this.first) { + this.push('hello'); + this.first = true; + return; + } + + this.push(null); + } +})); + +function check(s) { + const readableListener = common.mustNotCall(); + s.on('readable', readableListener); + s.on('end', common.mustCall()); + assert.strictEqual(s.removeListener, s.off); + s.removeListener('readable', readableListener); + s.resume(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-readable.js b/cli/tests/node_compat/test/parallel/test-stream-readable-readable.js new file mode 100644 index 00000000000000..df3223bcd109e8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-readable.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable } = require('stream'); + +{ + const r = new Readable({ + read() {} + }); + assert.strictEqual(r.readable, true); + r.destroy(); + assert.strictEqual(r.readable, false); +} + +{ + const mustNotCall = common.mustNotCall(); + const r = new Readable({ + read() {} + }); + assert.strictEqual(r.readable, true); + r.on('end', mustNotCall); + r.resume(); + r.push(null); + assert.strictEqual(r.readable, true); + r.off('end', mustNotCall); + r.on('end', common.mustCall(() => { + assert.strictEqual(r.readable, false); + })); +} + +{ + const r = new Readable({ + read: common.mustCall(() => { + process.nextTick(() => { + r.destroy(new Error()); + assert.strictEqual(r.readable, false); + }); + }) + }); + r.resume(); + r.on('error', common.mustCall(() => { + assert.strictEqual(r.readable, false); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-reading-readingMore.js b/cli/tests/node_compat/test/parallel/test-stream-readable-reading-readingMore.js new file mode 100644 index 00000000000000..d39752d79cdef7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-reading-readingMore.js @@ -0,0 +1,178 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + readable.on('data', common.mustCall((data) => { + // While in a flowing state with a 'readable' listener + // we should not be reading more + if (readable.readableFlowing) + assert.strictEqual(state.readingMore, true); + + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + const expectedReadingMore = [true, true, false]; + readable.on('readable', common.mustCall(() => { + // There is only one readingMore scheduled from on('data'), + // after which everything is governed by the .read() call + assert.strictEqual(state.readingMore, expectedReadingMore.shift()); + + // If the stream has ended, we shouldn't be reading + assert.strictEqual(state.ended, !state.reading); + + // Consume all the data + while (readable.read() !== null); + + if (expectedReadingMore.length === 0) // Reached end of stream + process.nextTick(common.mustCall(onStreamEnd, 1)); + }, 3)); + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + readable.read(6); + + // reading + assert.strictEqual(state.reading, true); + assert.strictEqual(state.readingMore, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); +} + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + readable.on('data', common.mustCall((data) => { + // While in a flowing state without a 'readable' listener + // we should be reading more + if (readable.readableFlowing) + assert.strictEqual(state.readingMore, true); + + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + // Stop emitting 'data' events + assert.strictEqual(state.flowing, true); + readable.pause(); + + // paused + assert.strictEqual(state.reading, false); + assert.strictEqual(state.flowing, false); + + readable.resume(); + assert.strictEqual(state.reading, false); + assert.strictEqual(state.flowing, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); +} + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + const onReadable = common.mustNotCall(); + + readable.on('readable', onReadable); + + readable.on('data', common.mustCall((data) => { + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + readable.removeListener('readable', onReadable); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + // We are still not flowing, we will be resuming in the next tick + assert.strictEqual(state.flowing, false); + + // Wait for nextTick, so the readableListener flag resets + process.nextTick(function() { + readable.resume(); + + // Stop emitting 'data' events + assert.strictEqual(state.flowing, true); + readable.pause(); + + // paused + assert.strictEqual(state.flowing, false); + + readable.resume(); + assert.strictEqual(state.flowing, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-resume-hwm.js b/cli/tests/node_compat/test/parallel/test-stream-readable-resume-hwm.js new file mode 100644 index 00000000000000..58a82fe427751b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-resume-hwm.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +// readable.resume() should not lead to a ._read() call being scheduled +// when we exceed the high water mark already. + +const readable = new Readable({ + read: common.mustNotCall(), + highWaterMark: 100 +}); + +// Fill up the internal buffer so that we definitely exceed the HWM: +for (let i = 0; i < 10; i++) + readable.push('a'.repeat(200)); + +// Call resume, and pause after one chunk. +// The .pause() is just so that we don’t empty the buffer fully, which would +// be a valid reason to call ._read(). +readable.resume(); +readable.once('data', common.mustCall(() => readable.pause())); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-resumeScheduled.js b/cli/tests/node_compat/test/parallel/test-stream-readable-resumeScheduled.js new file mode 100644 index 00000000000000..7c7362e97560e2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-resumeScheduled.js @@ -0,0 +1,72 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// Testing Readable Stream resumeScheduled state + +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +{ + // pipe() test case + const r = new Readable({ read() {} }); + const w = new Writable(); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + // Calling pipe() should change the state value = true. + r.pipe(w); + assert.strictEqual(r._readableState.resumeScheduled, true); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} + +{ + // 'data' listener test case + const r = new Readable({ read() {} }); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + r.push(Buffer.from([1, 2, 3])); + + // Adding 'data' listener should change the state value + r.on('data', common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); + assert.strictEqual(r._readableState.resumeScheduled, true); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} + +{ + // resume() test case + const r = new Readable({ read() {} }); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + // Calling resume() should change the state value. + r.resume(); + assert.strictEqual(r._readableState.resumeScheduled, true); + + r.on('resume', common.mustCall(() => { + // The state value should be `false` again + assert.strictEqual(r._readableState.resumeScheduled, false); + })); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-existing-buffers.js b/cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-existing-buffers.js new file mode 100644 index 00000000000000..87d51504a16926 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-existing-buffers.js @@ -0,0 +1,67 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +{ + // Call .setEncoding() while there are bytes already in the buffer. + const r = new Readable({ read() {} }); + + r.push(Buffer.from('a')); + r.push(Buffer.from('b')); + + r.setEncoding('utf8'); + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['ab']); + }); +} + +{ + // Call .setEncoding() while the buffer contains a complete, + // but chunked character. + const r = new Readable({ read() {} }); + + r.push(Buffer.from([0xf0])); + r.push(Buffer.from([0x9f])); + r.push(Buffer.from([0x8e])); + r.push(Buffer.from([0x89])); + + r.setEncoding('utf8'); + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['🎉']); + }); +} + +{ + // Call .setEncoding() while the buffer contains an incomplete character, + // and finish the character later. + const r = new Readable({ read() {} }); + + r.push(Buffer.from([0xf0])); + r.push(Buffer.from([0x9f])); + + r.setEncoding('utf8'); + + r.push(Buffer.from([0x8e])); + r.push(Buffer.from([0x89])); + + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['🎉']); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-null.js b/cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-null.js new file mode 100644 index 00000000000000..e0c9cf805e89ee --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-setEncoding-null.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + + +{ + const readable = new Readable({ encoding: 'hex' }); + assert.strictEqual(readable._readableState.encoding, 'hex'); + + readable.setEncoding(null); + + assert.strictEqual(readable._readableState.encoding, 'utf8'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-unshift.js b/cli/tests/node_compat/test/parallel/test-stream-readable-unshift.js new file mode 100644 index 00000000000000..114fd7a1ccce54 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-unshift.js @@ -0,0 +1,177 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +{ + // Check that strings are saved as Buffer + const readable = new Readable({ read() {} }); + + const string = 'abc'; + + readable.on('data', common.mustCall((chunk) => { + assert(Buffer.isBuffer(chunk)); + assert.strictEqual(chunk.toString('utf8'), string); + }, 1)); + + readable.unshift(string); + +} + +{ + // Check that data goes at the beginning + const readable = new Readable({ read() {} }); + const unshift = 'front'; + const push = 'back'; + + const expected = [unshift, push]; + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString('utf8'), expected.shift()); + }, 2)); + + + readable.push(push); + readable.unshift(unshift); +} + +{ + // Check that buffer is saved with correct encoding + const readable = new Readable({ read() {} }); + + const encoding = 'base64'; + const string = Buffer.from('abc').toString(encoding); + + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(encoding), string); + }, 1)); + + readable.unshift(string, encoding); + +} + +{ + + const streamEncoding = 'base64'; + + function checkEncoding(readable) { + + // chunk encodings + const encodings = ['utf8', 'binary', 'hex', 'base64']; + const expected = []; + + readable.on('data', common.mustCall((chunk) => { + const { encoding, string } = expected.pop(); + assert.strictEqual(chunk.toString(encoding), string); + }, encodings.length)); + + for (const encoding of encodings) { + const string = 'abc'; + + // If encoding is the same as the state.encoding the string is + // saved as is + const expect = encoding !== streamEncoding ? + Buffer.from(string, encoding).toString(streamEncoding) : string; + + expected.push({ encoding, string: expect }); + + readable.unshift(string, encoding); + } + } + + const r1 = new Readable({ read() {} }); + r1.setEncoding(streamEncoding); + checkEncoding(r1); + + const r2 = new Readable({ read() {}, encoding: streamEncoding }); + checkEncoding(r2); + +} + +{ + // Both .push & .unshift should have the same behaviour + // When setting an encoding, each chunk should be emitted with that encoding + const encoding = 'base64'; + + function checkEncoding(readable) { + const string = 'abc'; + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, Buffer.from(string).toString(encoding)); + }, 2)); + + readable.push(string); + readable.unshift(string); + } + + const r1 = new Readable({ read() {} }); + r1.setEncoding(encoding); + checkEncoding(r1); + + const r2 = new Readable({ read() {}, encoding }); + checkEncoding(r2); + +} + +{ + // Check that ObjectMode works + const readable = new Readable({ objectMode: true, read() {} }); + + const chunks = ['a', 1, {}, []]; + + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, chunks.pop()); + }, chunks.length)); + + for (const chunk of chunks) { + readable.unshift(chunk); + } +} + +{ + + // Should not throw: https://github.com/nodejs/node/issues/27192 + const highWaterMark = 50; + class ArrayReader extends Readable { + constructor(opt) { + super({ highWaterMark }); + // The error happened only when pushing above hwm + this.buffer = new Array(highWaterMark * 2).fill(0).map(String); + } + _read(size) { + while (this.buffer.length) { + const chunk = this.buffer.shift(); + if (!this.buffer.length) { + this.push(chunk); + this.push(null); + return true; + } + if (!this.push(chunk)) + return; + } + } + } + + function onRead() { + while (null !== (stream.read())) { + // Remove the 'readable' listener before unshifting + stream.removeListener('readable', onRead); + stream.unshift('a'); + stream.on('data', (chunk) => { + console.log(chunk.length); + }); + break; + } + } + + const stream = new ArrayReader(); + stream.once('readable', common.mustCall(onRead)); + stream.on('end', common.mustCall(() => {})); + +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-readable-with-unimplemented-_read.js b/cli/tests/node_compat/test/parallel/test-stream-readable-with-unimplemented-_read.js new file mode 100644 index 00000000000000..d55343536003e2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readable-with-unimplemented-_read.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +const readable = new Readable(); + +readable.read(); +readable.on('error', common.expectsError({ + code: 'ERR_METHOD_NOT_IMPLEMENTED', + name: 'Error', + message: 'The _read() method is not implemented' +})); +readable.on('close', common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-stream-readableListening-state.js b/cli/tests/node_compat/test/parallel/test-stream-readableListening-state.js new file mode 100644 index 00000000000000..524589acd06147 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-readableListening-state.js @@ -0,0 +1,41 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const r = new stream.Readable({ + read: () => {} +}); + +// readableListening state should start in `false`. +assert.strictEqual(r._readableState.readableListening, false); + +r.on('readable', common.mustCall(() => { + // Inside the readable event this state should be true. + assert.strictEqual(r._readableState.readableListening, true); +})); + +r.push(Buffer.from('Testing readableListening state')); + +const r2 = new stream.Readable({ + read: () => {} +}); + +// readableListening state should start in `false`. +assert.strictEqual(r2._readableState.readableListening, false); + +r2.on('data', common.mustCall((chunk) => { + // readableListening should be false because we don't have + // a `readable` listener + assert.strictEqual(r2._readableState.readableListening, false); +})); + +r2.push(Buffer.from('Testing readableListening state')); diff --git a/cli/tests/node_compat/test/parallel/test-stream-some-find-every.mjs b/cli/tests/node_compat/test/parallel/test-stream-some-find-every.mjs new file mode 100644 index 00000000000000..61bbce21d48ad7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-some-find-every.mjs @@ -0,0 +1,179 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +import * as common from '../common/index.mjs'; +import { setTimeout } from 'timers/promises'; +import { Readable } from 'stream'; +import assert from 'assert'; + + +function oneTo5() { + return Readable.from([1, 2, 3, 4, 5]); +} + +function oneTo5Async() { + return oneTo5().map(async (x) => { + await Promise.resolve(); + return x; + }); +} +{ + // Some, find, and every work with a synchronous stream and predicate + assert.strictEqual(await oneTo5().some((x) => x > 3), true); + assert.strictEqual(await oneTo5().every((x) => x > 3), false); + assert.strictEqual(await oneTo5().find((x) => x > 3), 4); + assert.strictEqual(await oneTo5().some((x) => x > 6), false); + assert.strictEqual(await oneTo5().every((x) => x < 6), true); + assert.strictEqual(await oneTo5().find((x) => x > 6), undefined); + assert.strictEqual(await Readable.from([]).some(() => true), false); + assert.strictEqual(await Readable.from([]).every(() => true), true); + assert.strictEqual(await Readable.from([]).find(() => true), undefined); +} + +{ + // Some, find, and every work with an asynchronous stream and synchronous predicate + assert.strictEqual(await oneTo5Async().some((x) => x > 3), true); + assert.strictEqual(await oneTo5Async().every((x) => x > 3), false); + assert.strictEqual(await oneTo5Async().find((x) => x > 3), 4); + assert.strictEqual(await oneTo5Async().some((x) => x > 6), false); + assert.strictEqual(await oneTo5Async().every((x) => x < 6), true); + assert.strictEqual(await oneTo5Async().find((x) => x > 6), undefined); +} + +{ + // Some, find, and every work on synchronous streams with an asynchronous predicate + assert.strictEqual(await oneTo5().some(async (x) => x > 3), true); + assert.strictEqual(await oneTo5().every(async (x) => x > 3), false); + assert.strictEqual(await oneTo5().find(async (x) => x > 3), 4); + assert.strictEqual(await oneTo5().some(async (x) => x > 6), false); + assert.strictEqual(await oneTo5().every(async (x) => x < 6), true); + assert.strictEqual(await oneTo5().find(async (x) => x > 6), undefined); +} + +{ + // Some, find, and every work on asynchronous streams with an asynchronous predicate + assert.strictEqual(await oneTo5Async().some(async (x) => x > 3), true); + assert.strictEqual(await oneTo5Async().every(async (x) => x > 3), false); + assert.strictEqual(await oneTo5Async().find(async (x) => x > 3), 4); + assert.strictEqual(await oneTo5Async().some(async (x) => x > 6), false); + assert.strictEqual(await oneTo5Async().every(async (x) => x < 6), true); + assert.strictEqual(await oneTo5Async().find(async (x) => x > 6), undefined); +} + +{ + async function checkDestroyed(stream) { + await setTimeout(); + assert.strictEqual(stream.destroyed, true); + } + + { + // Some, find, and every short circuit + const someStream = oneTo5(); + await someStream.some(common.mustCall((x) => x > 2, 3)); + await checkDestroyed(someStream); + + const everyStream = oneTo5(); + await everyStream.every(common.mustCall((x) => x < 3, 3)); + await checkDestroyed(everyStream); + + const findStream = oneTo5(); + await findStream.find(common.mustCall((x) => x > 1, 2)); + await checkDestroyed(findStream); + + // When short circuit isn't possible the whole stream is iterated + await oneTo5().some(common.mustCall(() => false, 5)); + await oneTo5().every(common.mustCall(() => true, 5)); + await oneTo5().find(common.mustCall(() => false, 5)); + } + + { + // Some, find, and every short circuit async stream/predicate + const someStream = oneTo5Async(); + await someStream.some(common.mustCall(async (x) => x > 2, 3)); + await checkDestroyed(someStream); + + const everyStream = oneTo5Async(); + await everyStream.every(common.mustCall(async (x) => x < 3, 3)); + await checkDestroyed(everyStream); + + const findStream = oneTo5Async(); + await findStream.find(common.mustCall(async (x) => x > 1, 2)); + await checkDestroyed(findStream); + + // When short circuit isn't possible the whole stream is iterated + await oneTo5Async().some(common.mustCall(async () => false, 5)); + await oneTo5Async().every(common.mustCall(async () => true, 5)); + await oneTo5Async().find(common.mustCall(async () => false, 5)); + } +} + +{ + // Concurrency doesn't affect which value is found. + const found = await Readable.from([1, 2]).find(async (val) => { + if (val === 1) { + await setTimeout(100); + } + return true; + }, { concurrency: 2 }); + assert.strictEqual(found, 1); +} + +{ + // Support for AbortSignal + for (const op of ['some', 'every', 'find']) { + { + const ac = new AbortController(); + assert.rejects(Readable.from([1, 2, 3])[op]( + () => new Promise(() => { }), + { signal: ac.signal } + ), { + name: 'AbortError', + }, `${op} should abort correctly with sync abort`).then(common.mustCall()); + ac.abort(); + } + { + // Support for pre-aborted AbortSignal + assert.rejects(Readable.from([1, 2, 3])[op]( + () => new Promise(() => { }), + { signal: AbortSignal.abort() } + ), { + name: 'AbortError', + }, `${op} should abort with pre-aborted abort controller`).then(common.mustCall()); + } + } +} +{ + // Error cases + for (const op of ['some', 'every', 'find']) { + assert.rejects(async () => { + await Readable.from([1])[op](1); + }, /ERR_INVALID_ARG_TYPE/, `${op} should throw for invalid function`).then(common.mustCall()); + assert.rejects(async () => { + await Readable.from([1])[op]((x) => x, { + concurrency: 'Foo' + }); + }, /ERR_OUT_OF_RANGE/, `${op} should throw for invalid concurrency`).then(common.mustCall()); + assert.rejects(async () => { + await Readable.from([1])[op]((x) => x, 1); + }, /ERR_INVALID_ARG_TYPE/, `${op} should throw for invalid concurrency`).then(common.mustCall()); + assert.rejects(async () => { + await Readable.from([1])[op]((x) => x, { + signal: true + }); + }, /ERR_INVALID_ARG_TYPE/, `${op} should throw for invalid signal`).then(common.mustCall()); + } +} +{ + for (const op of ['some', 'every', 'find']) { + const stream = oneTo5(); + Object.defineProperty(stream, 'map', { + value: common.mustNotCall(() => {}), + }); + // Check that map isn't getting called. + stream[op](() => {}); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-callback-twice.js b/cli/tests/node_compat/test/parallel/test-stream-transform-callback-twice.js new file mode 100644 index 00000000000000..395090258e2e29 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-callback-twice.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Transform } = require('stream'); +const stream = new Transform({ + transform(chunk, enc, cb) { cb(); cb(); } +}); + +stream.on('error', common.expectsError({ + name: 'Error', + message: 'Callback called multiple times', + code: 'ERR_MULTIPLE_CALLBACK' +})); + +stream.write('foo'); diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-constructor-set-methods.js b/cli/tests/node_compat/test/parallel/test-stream-transform-constructor-set-methods.js new file mode 100644 index 00000000000000..b861de6f52bbd6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-constructor-set-methods.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const { Transform } = require('stream'); + +const t = new Transform(); + +assert.throws( + () => { + t.end(Buffer.from('blerg')); + }, + { + name: 'Error', + code: 'ERR_METHOD_NOT_IMPLEMENTED', + message: 'The _transform() method is not implemented' + } +); + +const _transform = common.mustCall((chunk, _, next) => { + next(); +}); + +const _final = common.mustCall((next) => { + next(); +}); + +const _flush = common.mustCall((next) => { + next(); +}); + +const t2 = new Transform({ + transform: _transform, + flush: _flush, + final: _final +}); + +assert.strictEqual(t2._transform, _transform); +assert.strictEqual(t2._flush, _flush); +assert.strictEqual(t2._final, _final); + +t2.end(Buffer.from('blerg')); +t2.resume(); diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-destroy.js b/cli/tests/node_compat/test/parallel/test-stream-transform-destroy.js new file mode 100644 index 00000000000000..878ca71931e208 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-destroy.js @@ -0,0 +1,150 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Transform } = require('stream'); +const assert = require('assert'); + +{ + const transform = new Transform({ + transform(chunk, enc, cb) {} + }); + + transform.resume(); + + transform.on('end', common.mustNotCall()); + transform.on('close', common.mustCall()); + transform.on('finish', common.mustNotCall()); + + transform.destroy(); +} + +{ + const transform = new Transform({ + transform(chunk, enc, cb) {} + }); + transform.resume(); + + const expected = new Error('kaboom'); + + transform.on('end', common.mustNotCall()); + transform.on('finish', common.mustNotCall()); + transform.on('close', common.mustCall()); + transform.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + transform.destroy(expected); +} + +{ + const transform = new Transform({ + transform(chunk, enc, cb) {} + }); + + transform._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(err); + }, 1); + + const expected = new Error('kaboom'); + + transform.on('finish', common.mustNotCall('no finish event')); + transform.on('close', common.mustCall()); + transform.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + transform.destroy(expected); +} + +{ + const expected = new Error('kaboom'); + const transform = new Transform({ + transform(chunk, enc, cb) {}, + destroy: common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(); + }, 1) + }); + transform.resume(); + + transform.on('end', common.mustNotCall('no end event')); + transform.on('close', common.mustCall()); + transform.on('finish', common.mustNotCall('no finish event')); + + // Error is swallowed by the custom _destroy + transform.on('error', common.mustNotCall('no error event')); + + transform.destroy(expected); +} + +{ + const transform = new Transform({ + transform(chunk, enc, cb) {} + }); + + transform._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(); + }, 1); + + transform.destroy(); +} + +{ + const transform = new Transform({ + transform(chunk, enc, cb) {} + }); + transform.resume(); + + transform._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + process.nextTick(() => { + this.push(null); + this.end(); + cb(); + }); + }, 1); + + const fail = common.mustNotCall('no event'); + + transform.on('finish', fail); + transform.on('end', fail); + transform.on('close', common.mustCall()); + + transform.destroy(); + + transform.removeListener('end', fail); + transform.removeListener('finish', fail); + transform.on('end', common.mustCall()); + transform.on('finish', common.mustNotCall()); +} + +{ + const transform = new Transform({ + transform(chunk, enc, cb) {} + }); + + const expected = new Error('kaboom'); + + transform._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(expected); + }, 1); + + transform.on('close', common.mustCall()); + transform.on('finish', common.mustNotCall('no finish event')); + transform.on('end', common.mustNotCall('no end event')); + transform.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + transform.destroy(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-final-sync.js b/cli/tests/node_compat/test/parallel/test-stream-transform-final-sync.js new file mode 100644 index 00000000000000..019f34d15ce69b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-final-sync.js @@ -0,0 +1,117 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let state = 0; + + +// What you do +// +// const stream = new stream.Transform({ +// transform: function transformCallback(chunk, _, next) { +// // part 1 +// this.push(chunk); +// //part 2 +// next(); +// }, +// final: function endCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// }, +// flush: function flushCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// } +// }); +// t.on('data', dataListener); +// t.on('end', endListener); +// t.on('finish', finishListener); +// t.write(1); +// t.write(4); +// t.end(7, endMethodCallback); +// +// The order things are called +// +// 1. transformCallback part 1 +// 2. dataListener +// 3. transformCallback part 2 +// 4. transformCallback part 1 +// 5. dataListener +// 6. transformCallback part 2 +// 7. transformCallback part 1 +// 8. dataListener +// 9. transformCallback part 2 +// 10. finalCallback part 1 +// 11. finalCallback part 2 +// 12. flushCallback part 1 +// 13. finishListener +// 14. endMethodCallback +// 15. flushCallback part 2 +// 16. endListener + +const t = new stream.Transform({ + objectMode: true, + transform: common.mustCall(function(chunk, _, next) { + // transformCallback part 1 + assert.strictEqual(++state, chunk); + this.push(state); + // transformCallback part 2 + assert.strictEqual(++state, chunk + 2); + process.nextTick(next); + }, 3), + final: common.mustCall(function(done) { + state++; + // finalCallback part 1 + assert.strictEqual(state, 10); + state++; + // finalCallback part 2 + assert.strictEqual(state, 11); + done(); + }, 1), + flush: common.mustCall(function(done) { + state++; + // fluchCallback part 1 + assert.strictEqual(state, 12); + process.nextTick(function() { + state++; + // fluchCallback part 2 + assert.strictEqual(state, 13); + done(); + }); + }, 1) +}); +t.on('finish', common.mustCall(function() { + state++; + // finishListener + assert.strictEqual(state, 15); +}, 1)); +t.on('end', common.mustCall(function() { + state++; + // endEvent + assert.strictEqual(state, 16); +}, 1)); +t.on('data', common.mustCall(function(d) { + // dataListener + assert.strictEqual(++state, d + 1); +}, 3)); +t.write(1); +t.write(4); +t.end(7, common.mustCall(function() { + state++; + // endMethodCallback + assert.strictEqual(state, 14); +}, 1)); diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-final.js b/cli/tests/node_compat/test/parallel/test-stream-transform-final.js new file mode 100644 index 00000000000000..9bc9ffec792244 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-final.js @@ -0,0 +1,119 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let state = 0; + + +// What you do: +// +// const stream = new stream.Transform({ +// transform: function transformCallback(chunk, _, next) { +// // part 1 +// this.push(chunk); +// //part 2 +// next(); +// }, +// final: function endCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// }, +// flush: function flushCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// } +// }); +// t.on('data', dataListener); +// t.on('end', endListener); +// t.on('finish', finishListener); +// t.write(1); +// t.write(4); +// t.end(7, endMethodCallback); +// +// The order things are called + +// 1. transformCallback part 1 +// 2. dataListener +// 3. transformCallback part 2 +// 4. transformCallback part 1 +// 5. dataListener +// 6. transformCallback part 2 +// 7. transformCallback part 1 +// 8. dataListener +// 9. transformCallback part 2 +// 10. finalCallback part 1 +// 11. finalCallback part 2 +// 12. flushCallback part 1 +// 13. finishListener +// 14. endMethodCallback +// 15. flushCallback part 2 +// 16. endListener + +const t = new stream.Transform({ + objectMode: true, + transform: common.mustCall(function(chunk, _, next) { + // transformCallback part 1 + assert.strictEqual(++state, chunk); + this.push(state); + // transformCallback part 2 + assert.strictEqual(++state, chunk + 2); + process.nextTick(next); + }, 3), + final: common.mustCall(function(done) { + state++; + // finalCallback part 1 + assert.strictEqual(state, 10); + setTimeout(function() { + state++; + // finalCallback part 2 + assert.strictEqual(state, 11); + done(); + }, 100); + }, 1), + flush: common.mustCall(function(done) { + state++; + // flushCallback part 1 + assert.strictEqual(state, 12); + process.nextTick(function() { + state++; + // flushCallback part 2 + assert.strictEqual(state, 13); + done(); + }); + }, 1) +}); +t.on('finish', common.mustCall(function() { + state++; + // finishListener + assert.strictEqual(state, 15); +}, 1)); +t.on('end', common.mustCall(function() { + state++; + // end event + assert.strictEqual(state, 16); +}, 1)); +t.on('data', common.mustCall(function(d) { + // dataListener + assert.strictEqual(++state, d + 1); +}, 3)); +t.write(1); +t.write(4); +t.end(7, common.mustCall(function() { + state++; + // endMethodCallback + assert.strictEqual(state, 14); +}, 1)); diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-flush-data.js b/cli/tests/node_compat/test/parallel/test-stream-transform-flush-data.js new file mode 100644 index 00000000000000..4a831a5d3d3526 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-flush-data.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +const assert = require('assert'); +const Transform = require('stream').Transform; + + +const expected = 'asdf'; + + +function _transform(d, e, n) { + n(); +} + +function _flush(n) { + n(null, expected); +} + +const t = new Transform({ + transform: _transform, + flush: _flush +}); + +t.end(Buffer.from('blerg')); +t.on('data', (data) => { + assert.strictEqual(data.toString(), expected); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-objectmode-falsey-value.js b/cli/tests/node_compat/test/parallel/test-stream-transform-objectmode-falsey-value.js new file mode 100644 index 00000000000000..e692242a90e780 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-objectmode-falsey-value.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +const PassThrough = stream.PassThrough; + +const src = new PassThrough({ objectMode: true }); +const tx = new PassThrough({ objectMode: true }); +const dest = new PassThrough({ objectMode: true }); + +const expect = [ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; +const results = []; + +dest.on('data', common.mustCall(function(x) { + results.push(x); +}, expect.length)); + +src.pipe(tx).pipe(dest); + +let i = -1; +const int = setInterval(common.mustCall(function() { + if (results.length === expect.length) { + src.end(); + clearInterval(int); + assert.deepStrictEqual(results, expect); + } else { + src.write(i++); + } +}, expect.length + 1), 1); diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-split-highwatermark.js b/cli/tests/node_compat/test/parallel/test-stream-transform-split-highwatermark.js new file mode 100644 index 00000000000000..c58b863b8c83d2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-split-highwatermark.js @@ -0,0 +1,80 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +const { Transform, Readable, Writable } = require('stream'); + +const DEFAULT = 16 * 1024; + +function testTransform(expectedReadableHwm, expectedWritableHwm, options) { + const t = new Transform(options); + assert.strictEqual(t._readableState.highWaterMark, expectedReadableHwm); + assert.strictEqual(t._writableState.highWaterMark, expectedWritableHwm); +} + +// Test overriding defaultHwm +testTransform(666, DEFAULT, { readableHighWaterMark: 666 }); +testTransform(DEFAULT, 777, { writableHighWaterMark: 777 }); +testTransform(666, 777, { + readableHighWaterMark: 666, + writableHighWaterMark: 777, +}); + +// Test highWaterMark overriding +testTransform(555, 555, { + highWaterMark: 555, + readableHighWaterMark: 666, +}); +testTransform(555, 555, { + highWaterMark: 555, + writableHighWaterMark: 777, +}); +testTransform(555, 555, { + highWaterMark: 555, + readableHighWaterMark: 666, + writableHighWaterMark: 777, +}); + +// Test undefined, null +[undefined, null].forEach((v) => { + testTransform(DEFAULT, DEFAULT, { readableHighWaterMark: v }); + testTransform(DEFAULT, DEFAULT, { writableHighWaterMark: v }); + testTransform(666, DEFAULT, { highWaterMark: v, readableHighWaterMark: 666 }); + testTransform(DEFAULT, 777, { highWaterMark: v, writableHighWaterMark: 777 }); +}); + +// test NaN +{ + assert.throws(() => { + new Transform({ readableHighWaterMark: NaN }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE', + message: "The property 'options.readableHighWaterMark' is invalid. " + + 'Received NaN' + }); + + assert.throws(() => { + new Transform({ writableHighWaterMark: NaN }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE', + message: "The property 'options.writableHighWaterMark' is invalid. " + + 'Received NaN' + }); +} + +// Test non Duplex streams ignore the options +{ + const r = new Readable({ readableHighWaterMark: 666 }); + assert.strictEqual(r._readableState.highWaterMark, DEFAULT); + const w = new Writable({ writableHighWaterMark: 777 }); + assert.strictEqual(w._writableState.highWaterMark, DEFAULT); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-transform-split-objectmode.js b/cli/tests/node_compat/test/parallel/test-stream-transform-split-objectmode.js new file mode 100644 index 00000000000000..84d21a8ccd9e69 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-transform-split-objectmode.js @@ -0,0 +1,88 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const Transform = require('stream').Transform; + +const parser = new Transform({ readableObjectMode: true }); + +assert(parser._readableState.objectMode); +assert(!parser._writableState.objectMode); +assert.strictEqual(parser.readableHighWaterMark, 16); +assert.strictEqual(parser.writableHighWaterMark, 16 * 1024); +assert.strictEqual(parser.readableHighWaterMark, + parser._readableState.highWaterMark); +assert.strictEqual(parser.writableHighWaterMark, + parser._writableState.highWaterMark); + +parser._transform = function(chunk, enc, callback) { + callback(null, { val: chunk[0] }); +}; + +let parsed; + +parser.on('data', function(obj) { + parsed = obj; +}); + +parser.end(Buffer.from([42])); + +process.on('exit', function() { + assert.strictEqual(parsed.val, 42); +}); + + +const serializer = new Transform({ writableObjectMode: true }); + +assert(!serializer._readableState.objectMode); +assert(serializer._writableState.objectMode); +assert.strictEqual(serializer.readableHighWaterMark, 16 * 1024); +assert.strictEqual(serializer.writableHighWaterMark, 16); +assert.strictEqual(parser.readableHighWaterMark, + parser._readableState.highWaterMark); +assert.strictEqual(parser.writableHighWaterMark, + parser._writableState.highWaterMark); + +serializer._transform = function(obj, _, callback) { + callback(null, Buffer.from([obj.val])); +}; + +let serialized; + +serializer.on('data', function(chunk) { + serialized = chunk; +}); + +serializer.write({ val: 42 }); + +process.on('exit', function() { + assert.strictEqual(serialized[0], 42); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-uint8array.js b/cli/tests/node_compat/test/parallel/test-stream-uint8array.js new file mode 100644 index 00000000000000..71e143016e4d9b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-uint8array.js @@ -0,0 +1,108 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable, Writable } = require('stream'); + +const ABC = new Uint8Array([0x41, 0x42, 0x43]); +const DEF = new Uint8Array([0x44, 0x45, 0x46]); +const GHI = new Uint8Array([0x47, 0x48, 0x49]); + +{ + // Simple Writable test. + + let n = 0; + const writable = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert(chunk instanceof Buffer); + if (n++ === 0) { + assert.strictEqual(String(chunk), 'ABC'); + } else { + assert.strictEqual(String(chunk), 'DEF'); + } + + cb(); + }, 2) + }); + + writable.write(ABC); + writable.end(DEF); +} + +{ + // Writable test, pass in Uint8Array in object mode. + + const writable = new Writable({ + objectMode: true, + write: common.mustCall((chunk, encoding, cb) => { + assert(!(chunk instanceof Buffer)); + assert(chunk instanceof Uint8Array); + assert.strictEqual(chunk, ABC); + assert.strictEqual(encoding, 'utf8'); + cb(); + }) + }); + + writable.end(ABC); +} + +{ + // Writable test, multiple writes carried out via writev. + let callback; + + const writable = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert(chunk instanceof Buffer); + assert.strictEqual(encoding, 'buffer'); + assert.strictEqual(String(chunk), 'ABC'); + callback = cb; + }), + writev: common.mustCall((chunks, cb) => { + assert.strictEqual(chunks.length, 2); + assert.strictEqual(chunks[0].encoding, 'buffer'); + assert.strictEqual(chunks[1].encoding, 'buffer'); + assert.strictEqual(chunks[0].chunk + chunks[1].chunk, 'DEFGHI'); + }) + }); + + writable.write(ABC); + writable.write(DEF); + writable.end(GHI); + callback(); +} + +{ + // Simple Readable test. + const readable = new Readable({ + read() {} + }); + + readable.push(DEF); + readable.unshift(ABC); + + const buf = readable.read(); + assert(buf instanceof Buffer); + assert.deepStrictEqual([...buf], [...ABC, ...DEF]); +} + +{ + // Readable test, setEncoding. + const readable = new Readable({ + read() {} + }); + + readable.setEncoding('utf8'); + + readable.push(DEF); + readable.unshift(ABC); + + const out = readable.read(); + assert.strictEqual(out, 'ABCDEF'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-unpipe-event.js b/cli/tests/node_compat/test/parallel/test-stream-unpipe-event.js new file mode 100644 index 00000000000000..7a307321c38021 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-unpipe-event.js @@ -0,0 +1,92 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Writable, Readable } = require('stream'); +class NullWriteable extends Writable { + _write(chunk, encoding, callback) { + return callback(); + } +} +class QuickEndReadable extends Readable { + _read() { + this.push(null); + } +} +class NeverEndReadable extends Readable { + _read() {} +} + +{ + const dest = new NullWriteable(); + const src = new QuickEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustNotCall('unpipe should not have been emitted')); + src.pipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 1); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest); + src.unpipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new QuickEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest, { end: false }); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustNotCall('unpipe should not have been emitted')); + src.pipe(dest, { end: false }); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 1); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest, { end: false }); + src.unpipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-unshift-empty-chunk.js b/cli/tests/node_compat/test/parallel/test-stream-unshift-empty-chunk.js new file mode 100644 index 00000000000000..b57d72eced5c44 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-unshift-empty-chunk.js @@ -0,0 +1,87 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This test verifies that stream.unshift(Buffer.alloc(0)) or +// stream.unshift('') does not set state.reading=false. +const Readable = require('stream').Readable; + +const r = new Readable(); +let nChunks = 10; +const chunk = Buffer.alloc(10, 'x'); + +r._read = function(n) { + setImmediate(() => { + r.push(--nChunks === 0 ? null : chunk); + }); +}; + +let readAll = false; +const seen = []; +r.on('readable', () => { + let chunk; + while ((chunk = r.read()) !== null) { + seen.push(chunk.toString()); + // Simulate only reading a certain amount of the data, + // and then putting the rest of the chunk back into the + // stream, like a parser might do. We just fill it with + // 'y' so that it's easy to see which bits were touched, + // and which were not. + const putBack = Buffer.alloc(readAll ? 0 : 5, 'y'); + readAll = !readAll; + r.unshift(putBack); + } +}); + +const expect = + [ 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy' ]; + +r.on('end', () => { + assert.deepStrictEqual(seen, expect); + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-unshift-read-race.js b/cli/tests/node_compat/test/parallel/test-stream-unshift-read-race.js new file mode 100644 index 00000000000000..6550acf3387d0b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-unshift-read-race.js @@ -0,0 +1,135 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// This test verifies that: +// 1. unshift() does not cause colliding _read() calls. +// 2. unshift() after the 'end' event is an error, but after the EOF +// signalling null, it is ok, and just creates a new readable chunk. +// 3. push() after the EOF signaling null is an error. +// 4. _read() is not called after pushing the EOF null chunk. + +const stream = require('stream'); +const hwm = 10; +const r = stream.Readable({ highWaterMark: hwm, autoDestroy: false }); +const chunks = 10; + +const data = Buffer.allocUnsafe(chunks * hwm + Math.ceil(hwm / 2)); +for (let i = 0; i < data.length; i++) { + const c = 'asdf'.charCodeAt(i % 4); + data[i] = c; +} + +let pos = 0; +let pushedNull = false; +r._read = function(n) { + assert(!pushedNull, '_read after null push'); + + // Every third chunk is fast + push(!(chunks % 3)); + + function push(fast) { + assert(!pushedNull, 'push() after null push'); + const c = pos >= data.length ? null : data.slice(pos, pos + n); + pushedNull = c === null; + if (fast) { + pos += n; + r.push(c); + if (c === null) pushError(); + } else { + setTimeout(function() { + pos += n; + r.push(c); + if (c === null) pushError(); + }, 1); + } + } +}; + +function pushError() { + r.unshift(Buffer.allocUnsafe(1)); + w.end(); + + assert.throws(() => { + r.push(Buffer.allocUnsafe(1)); + }, { + code: 'ERR_STREAM_PUSH_AFTER_EOF', + name: 'Error', + message: 'stream.push() after EOF' + }); +} + + +const w = stream.Writable(); +const written = []; +w._write = function(chunk, encoding, cb) { + written.push(chunk.toString()); + cb(); +}; + +r.on('end', common.mustNotCall()); + +r.on('readable', function() { + let chunk; + while (null !== (chunk = r.read(10))) { + w.write(chunk); + if (chunk.length > 4) + r.unshift(Buffer.from('1234')); + } +}); + +w.on('finish', common.mustCall(function() { + // Each chunk should start with 1234, and then be asfdasdfasdf... + // The first got pulled out before the first unshift('1234'), so it's + // lacking that piece. + assert.strictEqual(written[0], 'asdfasdfas'); + let asdf = 'd'; + console.error(`0: ${written[0]}`); + for (let i = 1; i < written.length; i++) { + console.error(`${i.toString(32)}: ${written[i]}`); + assert.strictEqual(written[i].slice(0, 4), '1234'); + for (let j = 4; j < written[i].length; j++) { + const c = written[i].charAt(j); + assert.strictEqual(c, asdf); + switch (asdf) { + case 'a': asdf = 's'; break; + case 's': asdf = 'd'; break; + case 'd': asdf = 'f'; break; + case 'f': asdf = 'a'; break; + } + } + } +})); + +process.on('exit', function() { + assert.strictEqual(written.length, 18); + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-change-default-encoding.js b/cli/tests/node_compat/test/parallel/test-stream-writable-change-default-encoding.js new file mode 100644 index 00000000000000..47376988171df0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-change-default-encoding.js @@ -0,0 +1,85 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class MyWritable extends stream.Writable { + constructor(fn, options) { + super(options); + this.fn = fn; + } + + _write(chunk, encoding, callback) { + this.fn(Buffer.isBuffer(chunk), typeof chunk, encoding); + callback(); + } +} + +(function defaultCondingIsUtf8() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'utf8'); + }, { decodeStrings: false }); + m.write('foo'); + m.end(); +}()); + +(function changeDefaultEncodingToAscii() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'ascii'); + }, { decodeStrings: false }); + m.setDefaultEncoding('ascii'); + m.write('bar'); + m.end(); +}()); + +// Change default encoding to invalid value. +assert.throws(() => { + const m = new MyWritable( + (isBuffer, type, enc) => {}, + { decodeStrings: false }); + m.setDefaultEncoding({}); + m.write('bar'); + m.end(); +}, { + name: 'TypeError', + code: 'ERR_UNKNOWN_ENCODING', + message: 'Unknown encoding: {}' +}); + +(function checkVariableCaseEncoding() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'ascii'); + }, { decodeStrings: false }); + m.setDefaultEncoding('AsCii'); + m.write('bar'); + m.end(); +}()); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-clear-buffer.js b/cli/tests/node_compat/test/parallel/test-stream-writable-clear-buffer.js new file mode 100644 index 00000000000000..58f2cd8919508c --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-clear-buffer.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This test ensures that the _writeableState.bufferedRequestCount and +// the actual buffered request count are the same. + +const common = require('../common'); +const Stream = require('stream'); +const assert = require('assert'); + +class StreamWritable extends Stream.Writable { + constructor() { + super({ objectMode: true }); + } + + // Refs: https://github.com/nodejs/node/issues/6758 + // We need a timer like on the original issue thread. + // Otherwise the code will never reach our test case. + _write(chunk, encoding, cb) { + setImmediate(cb); + } +} + +const testStream = new StreamWritable(); +testStream.cork(); + +for (let i = 1; i <= 5; i++) { + testStream.write(i, common.mustCall(() => { + assert.strictEqual( + testStream._writableState.bufferedRequestCount, + testStream._writableState.getBuffer().length + ); + })); +} + +testStream.end(); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-constructor-set-methods.js b/cli/tests/node_compat/test/parallel/test-stream-writable-constructor-set-methods.js new file mode 100644 index 00000000000000..290548ebb024c5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-constructor-set-methods.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const { Writable } = require('stream'); + +const bufferBlerg = Buffer.from('blerg'); +const w = new Writable(); + +assert.throws( + () => { + w.end(bufferBlerg); + }, + { + name: 'Error', + code: 'ERR_METHOD_NOT_IMPLEMENTED', + message: 'The _write() method is not implemented' + } +); + +const _write = common.mustCall((chunk, _, next) => { + next(); +}); + +const _writev = common.mustCall((chunks, next) => { + assert.strictEqual(chunks.length, 2); + next(); +}); + +const w2 = new Writable({ write: _write, writev: _writev }); + +assert.strictEqual(w2._write, _write); +assert.strictEqual(w2._writev, _writev); + +w2.write(bufferBlerg); + +w2.cork(); +w2.write(bufferBlerg); +w2.write(bufferBlerg); + +w2.end(); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-decoded-encoding.js b/cli/tests/node_compat/test/parallel/test-stream-writable-decoded-encoding.js new file mode 100644 index 00000000000000..00e9b85eaa6469 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-decoded-encoding.js @@ -0,0 +1,65 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class MyWritable extends stream.Writable { + constructor(fn, options) { + super(options); + this.fn = fn; + } + + _write(chunk, encoding, callback) { + this.fn(Buffer.isBuffer(chunk), typeof chunk, encoding); + callback(); + } +} + +{ + const m = new MyWritable(function(isBuffer, type, enc) { + assert(isBuffer); + assert.strictEqual(type, 'object'); + assert.strictEqual(enc, 'buffer'); + }, { decodeStrings: true }); + m.write('some-text', 'utf8'); + m.end(); +} + +{ + const m = new MyWritable(function(isBuffer, type, enc) { + assert(!isBuffer); + assert.strictEqual(type, 'string'); + assert.strictEqual(enc, 'utf8'); + }, { decodeStrings: false }); + m.write('some-text', 'utf8'); + m.end(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-destroy.js b/cli/tests/node_compat/test/parallel/test-stream-writable-destroy.js new file mode 100644 index 00000000000000..904031143aea8f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-destroy.js @@ -0,0 +1,496 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Writable, addAbortSignal } = require('stream'); +const assert = require('assert'); + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write.on('finish', common.mustNotCall()); + write.on('close', common.mustCall()); + + write.destroy(); + assert.strictEqual(write.destroyed, true); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { + this.destroy(new Error('asd')); + cb(); + } + }); + + write.on('error', common.mustCall()); + write.on('finish', common.mustNotCall()); + write.end('asd'); + assert.strictEqual(write.destroyed, true); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + const expected = new Error('kaboom'); + + write.on('finish', common.mustNotCall()); + write.on('close', common.mustCall()); + write.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + write.destroy(expected); + assert.strictEqual(write.destroyed, true); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write._destroy = function(err, cb) { + assert.strictEqual(err, expected); + cb(err); + }; + + const expected = new Error('kaboom'); + + write.on('finish', common.mustNotCall('no finish event')); + write.on('close', common.mustCall()); + write.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + write.destroy(expected); + assert.strictEqual(write.destroyed, true); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); }, + destroy: common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(); + }) + }); + + const expected = new Error('kaboom'); + + write.on('finish', common.mustNotCall('no finish event')); + write.on('close', common.mustCall()); + + // Error is swallowed by the custom _destroy + write.on('error', common.mustNotCall('no error event')); + + write.destroy(expected); + assert.strictEqual(write.destroyed, true); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(); + }); + + write.destroy(); + assert.strictEqual(write.destroyed, true); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + process.nextTick(() => { + this.end(); + cb(); + }); + }); + + const fail = common.mustNotCall('no finish event'); + + write.on('finish', fail); + write.on('close', common.mustCall()); + + write.destroy(); + + assert.strictEqual(write.destroyed, true); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + const expected = new Error('kaboom'); + + write._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(expected); + }); + + write.on('close', common.mustCall()); + write.on('finish', common.mustNotCall('no finish event')); + write.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + write.destroy(); + assert.strictEqual(write.destroyed, true); +} + +{ + // double error case + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + let ticked = false; + write.on('close', common.mustCall(() => { + assert.strictEqual(ticked, true); + })); + write.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.message, 'kaboom 1'); + assert.strictEqual(write._writableState.errorEmitted, true); + })); + + const expected = new Error('kaboom 1'); + write.destroy(expected); + write.destroy(new Error('kaboom 2')); + assert.strictEqual(write._writableState.errored, expected); + assert.strictEqual(write._writableState.errorEmitted, false); + assert.strictEqual(write.destroyed, true); + ticked = true; +} + +{ + const writable = new Writable({ + destroy: common.mustCall(function(err, cb) { + process.nextTick(cb, new Error('kaboom 1')); + }), + write(chunk, enc, cb) { + cb(); + } + }); + + let ticked = false; + writable.on('close', common.mustCall(() => { + writable.on('error', common.mustNotCall()); + writable.destroy(new Error('hello')); + assert.strictEqual(ticked, true); + assert.strictEqual(writable._writableState.errorEmitted, true); + })); + writable.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.message, 'kaboom 1'); + assert.strictEqual(writable._writableState.errorEmitted, true); + })); + + writable.destroy(); + assert.strictEqual(writable.destroyed, true); + assert.strictEqual(writable._writableState.errored, null); + assert.strictEqual(writable._writableState.errorEmitted, false); + + // Test case where `writable.destroy()` is called again with an error before + // the `_destroy()` callback is called. + writable.destroy(new Error('kaboom 2')); + assert.strictEqual(writable._writableState.errorEmitted, false); + assert.strictEqual(writable._writableState.errored, null); + + ticked = true; +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write.destroyed = true; + assert.strictEqual(write.destroyed, true); + + // The internal destroy() mechanism should not be triggered + write.on('close', common.mustNotCall()); + write.destroy(); +} + +{ + function MyWritable() { + assert.strictEqual(this.destroyed, false); + this.destroyed = false; + Writable.call(this); + } + + Object.setPrototypeOf(MyWritable.prototype, Writable.prototype); + Object.setPrototypeOf(MyWritable, Writable); + + new MyWritable(); +} + +{ + // Destroy and destroy callback + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write.destroy(); + + const expected = new Error('kaboom'); + + write.destroy(expected, common.mustCall((err) => { + assert.strictEqual(err, undefined); + })); +} + +{ + // Checks that `._undestroy()` restores the state so that `final` will be + // called again. + const write = new Writable({ + write: common.mustNotCall(), + final: common.mustCall((cb) => cb(), 2), + autoDestroy: true + }); + + write.end(); + write.once('close', common.mustCall(() => { + write._undestroy(); + write.end(); + })); +} + +{ + const write = new Writable(); + + write.destroy(); + write.on('error', common.mustNotCall()); + write.write('asd', common.expectsError({ + name: 'Error', + code: 'ERR_STREAM_DESTROYED', + message: 'Cannot call write after a stream was destroyed' + })); +} + +{ + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write.on('error', common.mustNotCall()); + + write.cork(); + write.write('asd', common.mustCall()); + write.uncork(); + + write.cork(); + write.write('asd', common.expectsError({ + name: 'Error', + code: 'ERR_STREAM_DESTROYED', + message: 'Cannot call write after a stream was destroyed' + })); + write.destroy(); + write.write('asd', common.expectsError({ + name: 'Error', + code: 'ERR_STREAM_DESTROYED', + message: 'Cannot call write after a stream was destroyed' + })); + write.uncork(); +} + +{ + // Call end(cb) after error & destroy + + const write = new Writable({ + write(chunk, enc, cb) { cb(new Error('asd')); } + }); + write.on('error', common.mustCall(() => { + write.destroy(); + let ticked = false; + write.end(common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + })); + ticked = true; + })); + write.write('asd'); +} + +{ + // Call end(cb) after finish & destroy + + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + write.on('finish', common.mustCall(() => { + write.destroy(); + let ticked = false; + write.end(common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED'); + })); + ticked = true; + })); + write.end(); +} + +{ + // Call end(cb) after error & destroy and don't trigger + // unhandled exception. + + const write = new Writable({ + write(chunk, enc, cb) { process.nextTick(cb); } + }); + const _err = new Error('asd'); + write.once('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'asd'); + })); + write.end('asd', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + write.destroy(_err); +} + +{ + // Call buffered write callback with error + + const _err = new Error('asd'); + const write = new Writable({ + write(chunk, enc, cb) { + process.nextTick(cb, _err); + }, + autoDestroy: false + }); + write.cork(); + write.write('asd', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + write.write('asd', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + write.on('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + write.uncork(); +} + +{ + // Ensure callback order. + + let state = 0; + const write = new Writable({ + write(chunk, enc, cb) { + // `setImmediate()` is used on purpose to ensure the callback is called + // after `process.nextTick()` callbacks. + setImmediate(cb); + } + }); + write.write('asd', common.mustCall(() => { + assert.strictEqual(state++, 0); + })); + write.write('asd', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + assert.strictEqual(state++, 1); + })); + write.destroy(); +} + +{ + const write = new Writable({ + autoDestroy: false, + write(chunk, enc, cb) { + cb(); + cb(); + } + }); + + write.on('error', common.mustCall(() => { + assert(write._writableState.errored); + })); + write.write('asd'); +} + +{ + const ac = new AbortController(); + const write = addAbortSignal(ac.signal, new Writable({ + write(chunk, enc, cb) { cb(); } + })); + + write.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(write.destroyed, true); + })); + write.write('asd'); + ac.abort(); +} + +{ + const ac = new AbortController(); + const write = new Writable({ + signal: ac.signal, + write(chunk, enc, cb) { cb(); } + }); + + write.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(write.destroyed, true); + })); + write.write('asd'); + ac.abort(); +} + +{ + const signal = AbortSignal.abort(); + + const write = new Writable({ + signal, + write(chunk, enc, cb) { cb(); } + }); + + write.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(write.destroyed, true); + })); +} + +{ + // Destroy twice + const write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write.end(common.mustCall()); + write.destroy(); + write.destroy(); +} + +{ + // https://github.com/nodejs/node/issues/39356 + const s = new Writable({ + final() {} + }); + const _err = new Error('oh no'); + // Remove `callback` and it works + s.end(common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + s.on('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + s.destroy(_err); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-end-cb-error.js b/cli/tests/node_compat/test/parallel/test-stream-writable-end-cb-error.js new file mode 100644 index 00000000000000..b144d0ddfe4b21 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-end-cb-error.js @@ -0,0 +1,85 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +{ + // Invoke end callback on failure. + const writable = new stream.Writable(); + + const _err = new Error('kaboom'); + writable._write = (chunk, encoding, cb) => { + process.nextTick(cb, _err); + }; + + writable.on('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + writable.write('asd'); + writable.end(common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + writable.end(common.mustCall((err) => { + assert.strictEqual(err, _err); + })); +} + +{ + // Don't invoke end callback twice + const writable = new stream.Writable(); + + writable._write = (chunk, encoding, cb) => { + process.nextTick(cb); + }; + + let called = false; + writable.end('asd', common.mustCall((err) => { + called = true; + assert.strictEqual(err, undefined); + })); + + writable.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'kaboom'); + })); + writable.on('finish', common.mustCall(() => { + assert.strictEqual(called, true); + writable.emit('error', new Error('kaboom')); + })); +} + +{ + const w = new stream.Writable({ + write(chunk, encoding, callback) { + setImmediate(callback); + }, + finish(callback) { + setImmediate(callback); + } + }); + w.end('testing ended state', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_WRITE_AFTER_END'); + })); + assert.strictEqual(w.destroyed, false); + assert.strictEqual(w.writableEnded, true); + w.end(common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_WRITE_AFTER_END'); + })); + assert.strictEqual(w.destroyed, false); + assert.strictEqual(w.writableEnded, true); + w.end('end', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_WRITE_AFTER_END'); + })); + assert.strictEqual(w.destroyed, true); + w.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_WRITE_AFTER_END'); + })); + w.on('finish', common.mustNotCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-end-multiple.js b/cli/tests/node_compat/test/parallel/test-stream-writable-end-multiple.js new file mode 100644 index 00000000000000..22dfc5140870ce --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-end-multiple.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); +writable._write = (chunk, encoding, cb) => { + setTimeout(() => cb(), 10); +}; + +writable.end('testing ended state', common.mustCall()); +writable.end(common.mustCall()); +writable.on('finish', common.mustCall(() => { + let ticked = false; + writable.end(common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED'); + })); + ticked = true; +})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-ended-state.js b/cli/tests/node_compat/test/parallel/test-stream-writable-ended-state.js new file mode 100644 index 00000000000000..4d3719dcd3c3cf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-ended-state.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._write = (chunk, encoding, cb) => { + assert.strictEqual(writable._writableState.ended, false); + assert.strictEqual(writable._writableState.writable, undefined); + assert.strictEqual(writable.writableEnded, false); + cb(); +}; + +assert.strictEqual(writable._writableState.ended, false); +assert.strictEqual(writable._writableState.writable, undefined); +assert.strictEqual(writable.writable, true); +assert.strictEqual(writable.writableEnded, false); + +writable.end('testing ended state', common.mustCall(() => { + assert.strictEqual(writable._writableState.ended, true); + assert.strictEqual(writable._writableState.writable, undefined); + assert.strictEqual(writable.writable, false); + assert.strictEqual(writable.writableEnded, true); +})); + +assert.strictEqual(writable._writableState.ended, true); +assert.strictEqual(writable._writableState.writable, undefined); +assert.strictEqual(writable.writable, false); +assert.strictEqual(writable.writableEnded, true); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-finish-destroyed.js b/cli/tests/node_compat/test/parallel/test-stream-writable-finish-destroyed.js new file mode 100644 index 00000000000000..0f32291713ec48 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-finish-destroyed.js @@ -0,0 +1,50 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.end('asd'); + w.destroy(); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + w.end(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.write('asd'); + w.destroy(); +} + +{ + const w = new Writable({ + write() { + } + }); + w.on('finish', common.mustNotCall()); + w.end(); + w.destroy(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-finished-state.js b/cli/tests/node_compat/test/parallel/test-stream-writable-finished-state.js new file mode 100644 index 00000000000000..09f388bce0be17 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-finished-state.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(writable._writableState.finished, false); + cb(); +}; + +writable.on('finish', common.mustCall(() => { + assert.strictEqual(writable._writableState.finished, true); +})); + +writable.end('testing finished state', common.mustCall(() => { + assert.strictEqual(writable._writableState.finished, true); +})); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-finished.js b/cli/tests/node_compat/test/parallel/test-stream-writable-finished.js new file mode 100644 index 00000000000000..5df7f47700a64d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-finished.js @@ -0,0 +1,106 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Writable.prototype + assert(Object.hasOwn(Writable.prototype, 'writableFinished')); +} + +// event +{ + const writable = new Writable(); + + writable._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(writable.writableFinished, false); + cb(); + }; + + writable.on('finish', common.mustCall(() => { + assert.strictEqual(writable.writableFinished, true); + })); + + writable.end('testing finished state', common.mustCall(() => { + assert.strictEqual(writable.writableFinished, true); + })); +} + +{ + // Emit finish asynchronously. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + } + }); + + w.end(); + w.on('finish', common.mustCall()); +} + +{ + // Emit prefinish synchronously. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + } + }); + + let sync = true; + w.on('prefinish', common.mustCall(() => { + assert.strictEqual(sync, true); + })); + w.end(); + sync = false; +} + +{ + // Emit prefinish synchronously w/ final. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + }, + final(cb) { + cb(); + } + }); + + let sync = true; + w.on('prefinish', common.mustCall(() => { + assert.strictEqual(sync, true); + })); + w.end(); + sync = false; +} + + +{ + // Call _final synchronously. + + let sync = true; + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + }, + final: common.mustCall((cb) => { + assert.strictEqual(sync, true); + cb(); + }) + }); + + w.end(); + sync = false; +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-invalid-chunk.js b/cli/tests/node_compat/test/parallel/test-stream-writable-invalid-chunk.js new file mode 100644 index 00000000000000..acdc47bf12566e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-invalid-chunk.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function testWriteType(val, objectMode, code) { + const writable = new stream.Writable({ + objectMode, + write: () => {} + }); + writable.on('error', common.mustNotCall()); + if (code) { + assert.throws(() => { + writable.write(val); + }, { code }); + } else { + writable.write(val); + } +} + +testWriteType([], false, 'ERR_INVALID_ARG_TYPE'); +testWriteType({}, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(0, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(true, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(0.0, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(undefined, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(null, false, 'ERR_STREAM_NULL_VALUES'); + +testWriteType([], true); +testWriteType({}, true); +testWriteType(0, true); +testWriteType(true, true); +testWriteType(0.0, true); +testWriteType(undefined, true); +testWriteType(null, true, 'ERR_STREAM_NULL_VALUES'); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-needdrain-state.js b/cli/tests/node_compat/test/parallel/test-stream-writable-needdrain-state.js new file mode 100644 index 00000000000000..ca43cef234c8fa --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-needdrain-state.js @@ -0,0 +1,32 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const transform = new stream.Transform({ + transform: _transform, + highWaterMark: 1 +}); + +function _transform(chunk, encoding, cb) { + process.nextTick(() => { + assert.strictEqual(transform._writableState.needDrain, true); + cb(); + }); +} + +assert.strictEqual(transform._writableState.needDrain, false); + +transform.write('asdasd', common.mustCall(() => { + assert.strictEqual(transform._writableState.needDrain, false); +})); + +assert.strictEqual(transform._writableState.needDrain, true); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-null.js b/cli/tests/node_compat/test/parallel/test-stream-writable-null.js new file mode 100644 index 00000000000000..c8f96067401577 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-null.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class MyWritable extends stream.Writable { + constructor(options) { + super({ autoDestroy: false, ...options }); + } + _write(chunk, encoding, callback) { + assert.notStrictEqual(chunk, null); + callback(); + } +} + +{ + const m = new MyWritable({ objectMode: true }); + m.on('error', common.mustNotCall()); + assert.throws(() => { + m.write(null); + }, { + code: 'ERR_STREAM_NULL_VALUES' + }); +} + +{ + const m = new MyWritable(); + m.on('error', common.mustNotCall()); + assert.throws(() => { + m.write(false); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); +} + +{ // Should not throw. + const m = new MyWritable({ objectMode: true }); + m.write(false, assert.ifError); +} + +{ // Should not throw. + const m = new MyWritable({ objectMode: true }).on('error', (e) => { + assert.ifError(e || new Error('should not get here')); + }); + m.write(false, assert.ifError); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-properties.js b/cli/tests/node_compat/test/parallel/test-stream-writable-properties.js new file mode 100644 index 00000000000000..99c0dc0f4ef006 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-properties.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +{ + const w = new Writable(); + assert.strictEqual(w.writableCorked, 0); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); + w.cork(); + assert.strictEqual(w.writableCorked, 1); + w.cork(); + assert.strictEqual(w.writableCorked, 2); + w.uncork(); + assert.strictEqual(w.writableCorked, 1); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-writable.js b/cli/tests/node_compat/test/parallel/test-stream-writable-writable.js new file mode 100644 index 00000000000000..85f5de25d10303 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-writable.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write() {} + }); + assert.strictEqual(w.writable, true); + w.destroy(); + assert.strictEqual(w.writable, false); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + callback(new Error()); + }) + }); + assert.strictEqual(w.writable, true); + w.write('asd'); + assert.strictEqual(w.writable, false); + w.on('error', common.mustCall()); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + process.nextTick(() => { + callback(new Error()); + assert.strictEqual(w.writable, false); + }); + }) + }); + w.write('asd'); + w.on('error', common.mustCall()); +} + +{ + const w = new Writable({ + write: common.mustNotCall() + }); + assert.strictEqual(w.writable, true); + w.end(); + assert.strictEqual(w.writable, false); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-error.js b/cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-error.js new file mode 100644 index 00000000000000..675236b0b4404b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-error.js @@ -0,0 +1,65 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// Ensure callback is always invoked before +// error is emitted. Regardless if error was +// sync or async. + +{ + let callbackCalled = false; + // Sync Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(new Error()); + }) + }); + writable.on('error', common.mustCall(() => { + assert.strictEqual(callbackCalled, true); + })); + writable.write('hi', common.mustCall(() => { + callbackCalled = true; + })); +} + +{ + let callbackCalled = false; + // Async Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + process.nextTick(cb, new Error()); + }) + }); + writable.on('error', common.mustCall(() => { + assert.strictEqual(callbackCalled, true); + })); + writable.write('hi', common.mustCall(() => { + callbackCalled = true; + })); +} + +{ + // Sync Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(new Error()); + }) + }); + + writable.on('error', common.mustCall()); + + let cnt = 0; + // Ensure we don't live lock on sync error + while (writable.write('a')) + cnt++; + + assert.strictEqual(cnt, 0); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-twice.js b/cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-twice.js new file mode 100644 index 00000000000000..fd59f43a8ecfc1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-write-cb-twice.js @@ -0,0 +1,59 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); + +{ + // Sync + Sync + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(); + cb(); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} + +{ + // Sync + Async + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(); + process.nextTick(() => { + cb(); + }); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} + +{ + // Async + Async + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + process.nextTick(cb); + process.nextTick(() => { + cb(); + }); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-write-error.js b/cli/tests/node_compat/test/parallel/test-stream-writable-write-error.js new file mode 100644 index 00000000000000..b43b4bf1f370bc --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-write-error.js @@ -0,0 +1,82 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +function expectError(w, args, code, sync) { + if (sync) { + if (code) { + assert.throws(() => w.write(...args), { code }); + } else { + w.write(...args); + } + } else { + let errorCalled = false; + let ticked = false; + w.write(...args, common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(errorCalled, false); + assert.strictEqual(err.code, code); + })); + ticked = true; + w.on('error', common.mustCall((err) => { + errorCalled = true; + assert.strictEqual(err.code, code); + })); + } +} + +function test(autoDestroy) { + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + w.end(); + expectError(w, ['asd'], 'ERR_STREAM_WRITE_AFTER_END'); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + w.destroy(); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + expectError(w, [null], 'ERR_STREAM_NULL_VALUES', true); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + expectError(w, [{}], 'ERR_INVALID_ARG_TYPE', true); + } + + { + const w = new Writable({ + decodeStrings: false, + autoDestroy, + _write() {} + }); + expectError(w, ['asd', 'noencoding'], 'ERR_UNKNOWN_ENCODING', true); + } +} + +test(false); +test(true); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writable-write-writev-finish.js b/cli/tests/node_compat/test/parallel/test-stream-writable-write-writev-finish.js new file mode 100644 index 00000000000000..c12877fd49a1c1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writable-write-writev-finish.js @@ -0,0 +1,159 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +// Ensure consistency between the finish event when using cork() +// and writev and when not using them + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + cb(new Error('write test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'write test error'); + })); + + writable.end('test'); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + setImmediate(cb, new Error('write test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'write test error'); + })); + + writable.end('test'); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + cb(new Error('write test error')); + }; + + writable._writev = (chunks, cb) => { + cb(new Error('writev test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'writev test error'); + })); + + writable.cork(); + writable.write('test'); + + setImmediate(function() { + writable.end('test'); + }); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + setImmediate(cb, new Error('write test error')); + }; + + writable._writev = (chunks, cb) => { + setImmediate(cb, new Error('writev test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'writev test error'); + })); + + writable.cork(); + writable.write('test'); + + setImmediate(function() { + writable.end('test'); + }); +} + +// Regression test for +// https://github.com/nodejs/node/issues/13812 + +{ + const rs = new stream.Readable(); + rs.push('ok'); + rs.push(null); + rs._read = () => {}; + + const ws = new stream.Writable(); + + ws.on('finish', common.mustNotCall()); + ws.on('error', common.mustCall()); + + ws._write = (chunk, encoding, done) => { + setImmediate(done, new Error()); + }; + rs.pipe(ws); +} + +{ + const rs = new stream.Readable(); + rs.push('ok'); + rs.push(null); + rs._read = () => {}; + + const ws = new stream.Writable(); + + ws.on('finish', common.mustNotCall()); + ws.on('error', common.mustCall()); + + ws._write = (chunk, encoding, done) => { + done(new Error()); + }; + rs.pipe(ws); +} + +{ + const w = new stream.Writable(); + w._write = (chunk, encoding, cb) => { + process.nextTick(cb); + }; + w.on('error', common.mustCall()); + w.on('finish', common.mustNotCall()); + w.on('prefinish', () => { + w.write("shouldn't write in prefinish listener"); + }); + w.end(); +} + +{ + const w = new stream.Writable(); + w._write = (chunk, encoding, cb) => { + process.nextTick(cb); + }; + w.on('error', common.mustCall()); + w.on('finish', () => { + w.write("shouldn't write in finish listener"); + }); + w.end(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-writableState-ending.js b/cli/tests/node_compat/test/parallel/test-stream-writableState-ending.js new file mode 100644 index 00000000000000..1c45d09ecdaa3d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writableState-ending.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +function testStates(ending, finished, ended) { + assert.strictEqual(writable._writableState.ending, ending); + assert.strictEqual(writable._writableState.finished, finished); + assert.strictEqual(writable._writableState.ended, ended); +} + +writable._write = (chunk, encoding, cb) => { + // Ending, finished, ended start in false. + testStates(false, false, false); + cb(); +}; + +writable.on('finish', () => { + // Ending, finished, ended = true. + testStates(true, true, true); +}); + +const result = writable.end('testing function end()', () => { + // Ending, finished, ended = true. + testStates(true, true, true); +}); + +// End returns the writable instance +assert.strictEqual(result, writable); + +// Ending, ended = true. +// finished = false. +testStates(true, false, true); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js b/cli/tests/node_compat/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js new file mode 100644 index 00000000000000..b4775a1f19b3f9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js @@ -0,0 +1,64 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._writev = common.mustCall((chunks, cb) => { + assert.strictEqual(chunks.length, 2); + cb(); +}, 1); + +writable._write = common.mustCall((chunk, encoding, cb) => { + cb(); +}, 1); + +// first cork +writable.cork(); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 0); + +// cork again +writable.cork(); +assert.strictEqual(writable._writableState.corked, 2); + +// The first chunk is buffered +writable.write('first chunk'); +assert.strictEqual(writable._writableState.bufferedRequestCount, 1); + +// First uncork does nothing +writable.uncork(); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 1); + +process.nextTick(uncork); + +// The second chunk is buffered, because we uncork at the end of tick +writable.write('second chunk'); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 2); + +function uncork() { + // Second uncork flushes the buffer + writable.uncork(); + assert.strictEqual(writable._writableState.corked, 0); + assert.strictEqual(writable._writableState.bufferedRequestCount, 0); + + // Verify that end() uncorks correctly + writable.cork(); + writable.write('third chunk'); + writable.end(); + + // End causes an uncork() as well + assert.strictEqual(writable._writableState.corked, 0); + assert.strictEqual(writable._writableState.bufferedRequestCount, 0); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-write-destroy.js b/cli/tests/node_compat/test/parallel/test-stream-write-destroy.js new file mode 100644 index 00000000000000..3df93095bd8bf1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-write-destroy.js @@ -0,0 +1,69 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const { Writable } = require('stream'); + +// Test interaction between calling .destroy() on a writable and pending +// writes. + +for (const withPendingData of [ false, true ]) { + for (const useEnd of [ false, true ]) { + const callbacks = []; + + const w = new Writable({ + write(data, enc, cb) { + callbacks.push(cb); + }, + // Effectively disable the HWM to observe 'drain' events more easily. + highWaterMark: 1 + }); + + let chunksWritten = 0; + let drains = 0; + w.on('drain', () => drains++); + + function onWrite(err) { + if (err) { + assert.strictEqual(w.destroyed, true); + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + } else { + chunksWritten++; + } + } + + w.write('abc', onWrite); + assert.strictEqual(chunksWritten, 0); + assert.strictEqual(drains, 0); + callbacks.shift()(); + assert.strictEqual(chunksWritten, 1); + assert.strictEqual(drains, 1); + + if (withPendingData) { + // Test 2 cases: There either is or is not data still in the write queue. + // (The second write will never actually get executed either way.) + w.write('def', onWrite); + } + if (useEnd) { + // Again, test 2 cases: Either we indicate that we want to end the + // writable or not. + w.end('ghi', onWrite); + } else { + w.write('ghi', onWrite); + } + + assert.strictEqual(chunksWritten, 1); + w.destroy(); + assert.strictEqual(chunksWritten, 1); + callbacks.shift()(); + assert.strictEqual(chunksWritten, useEnd && !withPendingData ? 1 : 2); + assert.strictEqual(callbacks.length, 0); + assert.strictEqual(drains, 1); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-stream-write-drain.js b/cli/tests/node_compat/test/parallel/test-stream-write-drain.js new file mode 100644 index 00000000000000..a04bf99345e9b9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-write-drain.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); + +// Don't emit 'drain' if ended + +const w = new Writable({ + write(data, enc, cb) { + process.nextTick(cb); + }, + highWaterMark: 1 +}); + +w.on('drain', common.mustNotCall()); +w.write('asd'); +w.end(); diff --git a/cli/tests/node_compat/test/parallel/test-stream-write-final.js b/cli/tests/node_compat/test/parallel/test-stream-write-final.js new file mode 100644 index 00000000000000..1cdd64f46eaa0b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-write-final.js @@ -0,0 +1,31 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let shutdown = false; + +const w = new stream.Writable({ + final: common.mustCall(function(cb) { + assert.strictEqual(this, w); + setTimeout(function() { + shutdown = true; + cb(); + }, 100); + }), + write: function(chunk, e, cb) { + process.nextTick(cb); + } +}); +w.on('finish', common.mustCall(function() { + assert(shutdown); +})); +w.write(Buffer.allocUnsafe(1)); +w.end(Buffer.allocUnsafe(0)); diff --git a/cli/tests/node_compat/test/parallel/test-stream-writev.js b/cli/tests/node_compat/test/parallel/test-stream-writev.js new file mode 100644 index 00000000000000..f7fa7b9a012dc9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream-writev.js @@ -0,0 +1,137 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +const queue = []; +for (let decode = 0; decode < 2; decode++) { + for (let uncork = 0; uncork < 2; uncork++) { + for (let multi = 0; multi < 2; multi++) { + queue.push([!!decode, !!uncork, !!multi]); + } + } +} + +run(); + +function run() { + const t = queue.pop(); + if (t) + test(t[0], t[1], t[2], run); + else + console.log('ok'); +} + +function test(decode, uncork, multi, next) { + console.log(`# decode=${decode} uncork=${uncork} multi=${multi}`); + let counter = 0; + let expectCount = 0; + function cnt(msg) { + expectCount++; + const expect = expectCount; + return function(er) { + assert.ifError(er); + counter++; + assert.strictEqual(counter, expect); + }; + } + + const w = new stream.Writable({ decodeStrings: decode }); + w._write = common.mustNotCall('Should not call _write'); + + const expectChunks = decode ? [ + { encoding: 'buffer', + chunk: [104, 101, 108, 108, 111, 44, 32] }, + { encoding: 'buffer', + chunk: [119, 111, 114, 108, 100] }, + { encoding: 'buffer', + chunk: [33] }, + { encoding: 'buffer', + chunk: [10, 97, 110, 100, 32, 116, 104, 101, 110, 46, 46, 46] }, + { encoding: 'buffer', + chunk: [250, 206, 190, 167, 222, 173, 190, 239, 222, 202, 251, 173] }, + ] : [ + { encoding: 'ascii', chunk: 'hello, ' }, + { encoding: 'utf8', chunk: 'world' }, + { encoding: 'buffer', chunk: [33] }, + { encoding: 'latin1', chunk: '\nand then...' }, + { encoding: 'hex', chunk: 'facebea7deadbeefdecafbad' }, + ]; + + let actualChunks; + w._writev = function(chunks, cb) { + actualChunks = chunks.map(function(chunk) { + return { + encoding: chunk.encoding, + chunk: Buffer.isBuffer(chunk.chunk) ? + Array.prototype.slice.call(chunk.chunk) : chunk.chunk + }; + }); + cb(); + }; + + w.cork(); + w.write('hello, ', 'ascii', cnt('hello')); + w.write('world', 'utf8', cnt('world')); + + if (multi) + w.cork(); + + w.write(Buffer.from('!'), 'buffer', cnt('!')); + w.write('\nand then...', 'latin1', cnt('and then')); + + if (multi) + w.uncork(); + + w.write('facebea7deadbeefdecafbad', 'hex', cnt('hex')); + + if (uncork) + w.uncork(); + + w.end(cnt('end')); + + w.on('finish', function() { + // Make sure finish comes after all the write cb + cnt('finish')(); + assert.deepStrictEqual(actualChunks, expectChunks); + next(); + }); +} + +{ + const w = new stream.Writable({ + writev: common.mustCall(function(chunks, cb) { + cb(); + }) + }); + w.write('asd', common.mustCall()); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-base64-single-char-read-end.js b/cli/tests/node_compat/test/parallel/test-stream2-base64-single-char-read-end.js new file mode 100644 index 00000000000000..37f7129132d947 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-base64-single-char-read-end.js @@ -0,0 +1,63 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +const src = new R({ encoding: 'base64' }); +const dst = new W(); +let hasRead = false; +const accum = []; + +src._read = function(n) { + if (!hasRead) { + hasRead = true; + process.nextTick(function() { + src.push(Buffer.from('1')); + src.push(null); + }); + } +}; + +dst._write = function(chunk, enc, cb) { + accum.push(chunk); + cb(); +}; + +src.on('end', function() { + assert.strictEqual(String(Buffer.concat(accum)), 'MQ=='); + clearTimeout(timeout); +}); + +src.pipe(dst); + +const timeout = setTimeout(function() { + assert.fail('timed out waiting for _write'); +}, 100); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-basic.js b/cli/tests/node_compat/test/parallel/test-stream2-basic.js new file mode 100644 index 00000000000000..538ee89f6ffc40 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-basic.js @@ -0,0 +1,452 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +const EE = require('events').EventEmitter; + +class TestReader extends R { + constructor(n) { + super(); + this._buffer = Buffer.alloc(n || 100, 'x'); + this._pos = 0; + this._bufs = 10; + } + + _read(n) { + const max = this._buffer.length - this._pos; + n = Math.max(n, 0); + const toRead = Math.min(n, max); + if (toRead === 0) { + // Simulate the read buffer filling up with some more bytes some time + // in the future. + setTimeout(() => { + this._pos = 0; + this._bufs -= 1; + if (this._bufs <= 0) { + // read them all! + if (!this.ended) + this.push(null); + } else { + // now we have more. + // kinda cheating by calling _read, but whatever, + // it's just fake anyway. + this._read(n); + } + }, 10); + return; + } + + const ret = this._buffer.slice(this._pos, this._pos + toRead); + this._pos += toRead; + this.push(ret); + } +} + +class TestWriter extends EE { + constructor() { + super(); + this.received = []; + this.flush = false; + } + + write(c) { + this.received.push(c.toString()); + this.emit('write', c); + return true; + } + + end(c) { + if (c) this.write(c); + this.emit('end', this.received); + } +} + +{ + // Test basic functionality + const r = new TestReader(20); + + const reads = []; + const expect = [ 'x', + 'xx', + 'xxx', + 'xxxx', + 'xxxxx', + 'xxxxxxxxx', + 'xxxxxxxxxx', + 'xxxxxxxxxxxx', + 'xxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx' ]; + + r.on('end', common.mustCall(function() { + assert.deepStrictEqual(reads, expect); + })); + + let readSize = 1; + function flow() { + let res; + while (null !== (res = r.read(readSize++))) { + reads.push(res.toString()); + } + r.once('readable', flow); + } + + flow(); +} + +{ + // Verify pipe + const r = new TestReader(5); + + const expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + + const w = new TestWriter(); + + w.on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + + r.pipe(w); +} + + +[1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function(SPLIT) { + // Verify unpipe + const r = new TestReader(5); + + // Unpipe after 3 writes, then write to another stream instead. + let expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + const w = [ new TestWriter(), new TestWriter() ]; + + let writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + assert.deepStrictEqual(r._readableState.pipes, []); + w[0].end(); + r.pipe(w[1]); + assert.deepStrictEqual(r._readableState.pipes, [w[1]]); + } + }); + + let ended = 0; + + w[0].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 1); + assert.deepStrictEqual(results, expect[0]); + })); + + w[1].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 2); + assert.deepStrictEqual(results, expect[1]); + })); + + r.pipe(w[0]); +}); + + +{ + // Verify both writers get the same data when piping to destinations + const r = new TestReader(5); + const w = [ new TestWriter(), new TestWriter() ]; + + const expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + + w[0].on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + w[1].on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + + r.pipe(w[0]); + r.pipe(w[1]); +} + + +[1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function(SPLIT) { + // Verify multi-unpipe + const r = new TestReader(5); + + // Unpipe after 3 writes, then write to another stream instead. + let expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + const w = [ new TestWriter(), new TestWriter(), new TestWriter() ]; + + let writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + w[0].end(); + r.pipe(w[1]); + } + }); + + let ended = 0; + + w[0].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 1); + assert.deepStrictEqual(results, expect[0]); + })); + + w[1].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 2); + assert.deepStrictEqual(results, expect[1]); + })); + + r.pipe(w[0]); + r.pipe(w[2]); +}); + +{ + // Verify that back pressure is respected + const r = new R({ objectMode: true }); + r._read = common.mustNotCall(); + let counter = 0; + r.push(['one']); + r.push(['two']); + r.push(['three']); + r.push(['four']); + r.push(null); + + const w1 = new R(); + w1.write = function(chunk) { + assert.strictEqual(chunk[0], 'one'); + w1.emit('close'); + process.nextTick(function() { + r.pipe(w2); + r.pipe(w3); + }); + }; + w1.end = common.mustNotCall(); + + r.pipe(w1); + + const expected = ['two', 'two', 'three', 'three', 'four', 'four']; + + const w2 = new R(); + w2.write = function(chunk) { + assert.strictEqual(chunk[0], expected.shift()); + assert.strictEqual(counter, 0); + + counter++; + + if (chunk[0] === 'four') { + return true; + } + + setTimeout(function() { + counter--; + w2.emit('drain'); + }, 10); + + return false; + }; + w2.end = common.mustCall(); + + const w3 = new R(); + w3.write = function(chunk) { + assert.strictEqual(chunk[0], expected.shift()); + assert.strictEqual(counter, 1); + + counter++; + + if (chunk[0] === 'four') { + return true; + } + + setTimeout(function() { + counter--; + w3.emit('drain'); + }, 50); + + return false; + }; + w3.end = common.mustCall(function() { + assert.strictEqual(counter, 2); + assert.strictEqual(expected.length, 0); + }); +} + +{ + // Verify read(0) behavior for ended streams + const r = new R(); + let written = false; + let ended = false; + r._read = common.mustNotCall(); + + r.push(Buffer.from('foo')); + r.push(null); + + const v = r.read(0); + + assert.strictEqual(v, null); + + const w = new R(); + w.write = function(buffer) { + written = true; + assert.strictEqual(ended, false); + assert.strictEqual(buffer.toString(), 'foo'); + }; + + w.end = common.mustCall(function() { + ended = true; + assert.strictEqual(written, true); + }); + + r.pipe(w); +} + +{ + // Verify synchronous _read ending + const r = new R(); + let called = false; + r._read = function(n) { + r.push(null); + }; + + r.once('end', function() { + // Verify that this is called before the next tick + called = true; + }); + + r.read(); + + process.nextTick(function() { + assert.strictEqual(called, true); + }); +} + +{ + // Verify that adding readable listeners trigger data flow + const r = new R({ highWaterMark: 5 }); + let onReadable = false; + let readCalled = 0; + + r._read = function(n) { + if (readCalled++ === 2) + r.push(null); + else + r.push(Buffer.from('asdf')); + }; + + r.on('readable', function() { + onReadable = true; + r.read(); + }); + + r.on('end', common.mustCall(function() { + assert.strictEqual(readCalled, 3); + assert.ok(onReadable); + })); +} + +{ + // Verify that streams are chainable + const r = new R(); + r._read = common.mustCall(); + const r2 = r.setEncoding('utf8').pause().resume().pause(); + assert.strictEqual(r, r2); +} + +{ + // Verify readableEncoding property + assert(Object.hasOwn(R.prototype, 'readableEncoding')); + + const r = new R({ encoding: 'utf8' }); + assert.strictEqual(r.readableEncoding, 'utf8'); +} + +{ + // Verify readableObjectMode property + assert(Object.hasOwn(R.prototype, 'readableObjectMode')); + + const r = new R({ objectMode: true }); + assert.strictEqual(r.readableObjectMode, true); +} + +{ + // Verify writableObjectMode property + assert(Object.hasOwn(W.prototype, 'writableObjectMode')); + + const w = new W({ objectMode: true }); + assert.strictEqual(w.writableObjectMode, true); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-compatibility.js b/cli/tests/node_compat/test/parallel/test-stream2-compatibility.js new file mode 100644 index 00000000000000..3128cf2887e376 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-compatibility.js @@ -0,0 +1,77 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +let ondataCalled = 0; + +class TestReader extends R { + constructor() { + super(); + this._buffer = Buffer.alloc(100, 'x'); + + this.on('data', () => { + ondataCalled++; + }); + } + + _read(n) { + this.push(this._buffer); + this._buffer = Buffer.alloc(0); + } +} + +const reader = new TestReader(); +setImmediate(function() { + assert.strictEqual(ondataCalled, 1); + console.log('ok'); + reader.push(null); +}); + +class TestWriter extends W { + constructor() { + super(); + this.write('foo'); + this.end(); + } + + _write(chunk, enc, cb) { + cb(); + } +} + +const writer = new TestWriter(); + +process.on('exit', function() { + assert.strictEqual(reader.readable, false); + assert.strictEqual(writer.writable, false); + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-decode-partial.js b/cli/tests/node_compat/test/parallel/test-stream2-decode-partial.js new file mode 100644 index 00000000000000..78791ce9c12567 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-decode-partial.js @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +let buf = ''; +const euro = Buffer.from([0xE2, 0x82, 0xAC]); +const cent = Buffer.from([0xC2, 0xA2]); +const source = Buffer.concat([euro, cent]); + +const readable = Readable({ encoding: 'utf8' }); +readable.push(source.slice(0, 2)); +readable.push(source.slice(2, 4)); +readable.push(source.slice(4, 6)); +readable.push(null); + +readable.on('data', function(data) { + buf += data; +}); + +process.on('exit', function() { + assert.strictEqual(buf, '€¢'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-finish-pipe.js b/cli/tests/node_compat/test/parallel/test-stream2-finish-pipe.js new file mode 100644 index 00000000000000..6ff68882a34661 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-finish-pipe.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const stream = require('stream'); + +const r = new stream.Readable(); +r._read = function(size) { + r.push(Buffer.allocUnsafe(size)); +}; + +const w = new stream.Writable(); +w._write = function(data, encoding, cb) { + process.nextTick(cb, null); +}; + +r.pipe(w); + +// end() must be called in nextTick or a WRITE_AFTER_END error occurs. +process.nextTick(() => { + // This might sound unrealistic, but it happens in net.js. When + // socket.allowHalfOpen === false, EOF will cause .destroySoon() call which + // ends the writable side of net.Socket. + w.end(); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-large-read-stall.js b/cli/tests/node_compat/test/parallel/test-stream2-large-read-stall.js new file mode 100644 index 00000000000000..b1a173a3cddd0f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-large-read-stall.js @@ -0,0 +1,81 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// If everything aligns so that you do a read(n) of exactly the +// remaining buffer, then make sure that 'end' still emits. + +const READSIZE = 100; +const PUSHSIZE = 20; +const PUSHCOUNT = 1000; +const HWM = 50; + +const Readable = require('stream').Readable; +const r = new Readable({ + highWaterMark: HWM +}); +const rs = r._readableState; + +r._read = push; + +r.on('readable', function() { + console.error('>> readable'); + let ret; + do { + console.error(` > read(${READSIZE})`); + ret = r.read(READSIZE); + console.error(` < ${ret && ret.length} (${rs.length} remain)`); + } while (ret && ret.length === READSIZE); + + console.error('<< after read()', + ret && ret.length, + rs.needReadable, + rs.length); +}); + +r.on('end', common.mustCall(function() { + assert.strictEqual(pushes, PUSHCOUNT + 1); +})); + +let pushes = 0; +function push() { + if (pushes > PUSHCOUNT) + return; + + if (pushes++ === PUSHCOUNT) { + console.error(' push(EOF)'); + return r.push(null); + } + + console.error(` push #${pushes}`); + if (r.push(Buffer.allocUnsafe(PUSHSIZE))) + setTimeout(push, 1); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-objects.js b/cli/tests/node_compat/test/parallel/test-stream2-objects.js new file mode 100644 index 00000000000000..a2cb775eb18521 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-objects.js @@ -0,0 +1,304 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const { Readable, Writable } = require('stream'); +const assert = require('assert'); + +function toArray(callback) { + const stream = new Writable({ objectMode: true }); + const list = []; + stream.write = function(chunk) { + list.push(chunk); + }; + + stream.end = common.mustCall(function() { + callback(list); + }); + + return stream; +} + +function fromArray(list) { + const r = new Readable({ objectMode: true }); + r._read = common.mustNotCall(); + list.forEach(function(chunk) { + r.push(chunk); + }); + r.push(null); + + return r; +} + +{ + // Verify that objects can be read from the stream + const r = fromArray([{ one: '1' }, { two: '2' }]); + + const v1 = r.read(); + const v2 = r.read(); + const v3 = r.read(); + + assert.deepStrictEqual(v1, { one: '1' }); + assert.deepStrictEqual(v2, { two: '2' }); + assert.strictEqual(v3, null); +} + +{ + // Verify that objects can be piped into the stream + const r = fromArray([{ one: '1' }, { two: '2' }]); + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that read(n) is ignored + const r = fromArray([{ one: '1' }, { two: '2' }]); + const value = r.read(2); + + assert.deepStrictEqual(value, { one: '1' }); +} + +{ + // Verify that objects can be synchronously read + const r = new Readable({ objectMode: true }); + const list = [{ one: '1' }, { two: '2' }]; + r._read = function(n) { + const item = list.shift(); + r.push(item || null); + }; + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that objects can be asynchronously read + const r = new Readable({ objectMode: true }); + const list = [{ one: '1' }, { two: '2' }]; + r._read = function(n) { + const item = list.shift(); + process.nextTick(function() { + r.push(item || null); + }); + }; + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that strings can be read as objects + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + const list = ['one', 'two', 'three']; + list.forEach(function(str) { + r.push(str); + }); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, list); + }))); +} + +{ + // Verify read(0) behavior for object streams + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + + r.push('foobar'); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, ['foobar']); + }))); +} + +{ + // Verify the behavior of pushing falsey values + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + + r.push(false); + r.push(0); + r.push(''); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, [false, 0, '']); + }))); +} + +{ + // Verify high watermark _read() behavior + const r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + let calls = 0; + const list = ['1', '2', '3', '4', '5', '6', '7', '8']; + + r._read = function(n) { + calls++; + }; + + list.forEach(function(c) { + r.push(c); + }); + + const v = r.read(); + + assert.strictEqual(calls, 0); + assert.strictEqual(v, '1'); + + const v2 = r.read(); + assert.strictEqual(v2, '2'); + + const v3 = r.read(); + assert.strictEqual(v3, '3'); + + assert.strictEqual(calls, 1); +} + +{ + // Verify high watermark push behavior + const r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + r._read = common.mustNotCall(); + for (let i = 0; i < 6; i++) { + const bool = r.push(i); + assert.strictEqual(bool, i !== 5); + } +} + +{ + // Verify that objects can be written to stream + const w = new Writable({ objectMode: true }); + + w._write = function(chunk, encoding, cb) { + assert.deepStrictEqual(chunk, { foo: 'bar' }); + cb(); + }; + + w.on('finish', common.mustCall()); + w.write({ foo: 'bar' }); + w.end(); +} + +{ + // Verify that multiple objects can be written to stream + const w = new Writable({ objectMode: true }); + const list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + cb(); + }; + + w.on('finish', common.mustCall(function() { + assert.deepStrictEqual(list, [0, 1, 2, 3, 4]); + })); + + w.write(0); + w.write(1); + w.write(2); + w.write(3); + w.write(4); + w.end(); +} + +{ + // Verify that strings can be written as objects + const w = new Writable({ + objectMode: true + }); + const list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + process.nextTick(cb); + }; + + w.on('finish', common.mustCall(function() { + assert.deepStrictEqual(list, ['0', '1', '2', '3', '4']); + })); + + w.write('0'); + w.write('1'); + w.write('2'); + w.write('3'); + w.write('4'); + w.end(); +} + +{ + // Verify that stream buffers finish until callback is called + const w = new Writable({ + objectMode: true + }); + let called = false; + + w._write = function(chunk, encoding, cb) { + assert.strictEqual(chunk, 'foo'); + + process.nextTick(function() { + called = true; + cb(); + }); + }; + + w.on('finish', common.mustCall(function() { + assert.strictEqual(called, true); + })); + + w.write('foo'); + w.end(); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-pipe-error-handling.js b/cli/tests/node_compat/test/parallel/test-stream2-pipe-error-handling.js new file mode 100644 index 00000000000000..ef3bb0575e5edc --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-pipe-error-handling.js @@ -0,0 +1,113 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +{ + let count = 1000; + + const source = new stream.Readable(); + source._read = function(n) { + n = Math.min(count, n); + count -= n; + source.push(Buffer.allocUnsafe(n)); + }; + + let unpipedDest; + source.unpipe = function(dest) { + unpipedDest = dest; + stream.Readable.prototype.unpipe.call(this, dest); + }; + + const dest = new stream.Writable(); + dest._write = function(chunk, encoding, cb) { + cb(); + }; + + source.pipe(dest); + + let gotErr = null; + dest.on('error', function(err) { + gotErr = err; + }); + + let unpipedSource; + dest.on('unpipe', function(src) { + unpipedSource = src; + }); + + const err = new Error('This stream turned into bacon.'); + dest.emit('error', err); + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +} + +{ + let count = 1000; + + const source = new stream.Readable(); + source._read = function(n) { + n = Math.min(count, n); + count -= n; + source.push(Buffer.allocUnsafe(n)); + }; + + let unpipedDest; + source.unpipe = function(dest) { + unpipedDest = dest; + stream.Readable.prototype.unpipe.call(this, dest); + }; + + const dest = new stream.Writable({ autoDestroy: false }); + dest._write = function(chunk, encoding, cb) { + cb(); + }; + + source.pipe(dest); + + let unpipedSource; + dest.on('unpipe', function(src) { + unpipedSource = src; + }); + + const err = new Error('This stream turned into bacon.'); + + let gotErr = null; + try { + dest.emit('error', err); + } catch (e) { + gotErr = e; + } + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-pipe-error-once-listener.js b/cli/tests/node_compat/test/parallel/test-stream2-pipe-error-once-listener.js new file mode 100644 index 00000000000000..160381b086bc30 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-pipe-error-once-listener.js @@ -0,0 +1,60 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const stream = require('stream'); + +class Read extends stream.Readable { + _read(size) { + this.push('x'); + this.push(null); + } +} + +class Write extends stream.Writable { + _write(buffer, encoding, cb) { + this.emit('error', new Error('boom')); + this.emit('alldone'); + } +} + +const read = new Read(); +const write = new Write(); + +write.once('error', () => {}); +write.once('alldone', function(err) { + console.log('ok'); +}); + +process.on('exit', function(c) { + console.error('error thrown even with listener'); +}); + +read.pipe(write); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-push.js b/cli/tests/node_compat/test/parallel/test-stream2-push.js new file mode 100644 index 00000000000000..1ccfda2ca0e3d3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-push.js @@ -0,0 +1,143 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +const EE = require('events').EventEmitter; + + +// A mock thing a bit like the net.Socket/tcp_wrap.handle interaction + +const stream = new Readable({ + highWaterMark: 16, + encoding: 'utf8' +}); + +const source = new EE(); + +stream._read = function() { + console.error('stream._read'); + readStart(); +}; + +let ended = false; +stream.on('end', function() { + ended = true; +}); + +source.on('data', function(chunk) { + const ret = stream.push(chunk); + console.error('data', stream.readableLength); + if (!ret) + readStop(); +}); + +source.on('end', function() { + stream.push(null); +}); + +let reading = false; + +function readStart() { + console.error('readStart'); + reading = true; +} + +function readStop() { + console.error('readStop'); + reading = false; + process.nextTick(function() { + const r = stream.read(); + if (r !== null) + writer.write(r); + }); +} + +const writer = new Writable({ + decodeStrings: false +}); + +const written = []; + +const expectWritten = + [ 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg' ]; + +writer._write = function(chunk, encoding, cb) { + console.error(`WRITE ${chunk}`); + written.push(chunk); + process.nextTick(cb); +}; + +writer.on('finish', finish); + + +// Now emit some chunks. + +const chunk = 'asdfg'; + +let set = 0; +readStart(); +data(); +function data() { + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(!reading); + if (set++ < 5) + setTimeout(data, 10); + else + end(); +} + +function finish() { + console.error('finish'); + assert.deepStrictEqual(written, expectWritten); + console.log('ok'); +} + +function end() { + source.emit('end'); + assert(!reading); + writer.end(stream.read()); + setImmediate(function() { + assert(ended); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-read-sync-stack.js b/cli/tests/node_compat/test/parallel/test-stream2-read-sync-stack.js new file mode 100644 index 00000000000000..2630e1c29aabec --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-read-sync-stack.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const Readable = require('stream').Readable; + +// This tests synchronous read callbacks and verifies that even if they nest +// heavily the process handles it without an error + +const r = new Readable(); +const N = 256 * 1024; + +let reads = 0; +r._read = function(n) { + const chunk = reads++ === N ? null : Buffer.allocUnsafe(1); + r.push(chunk); +}; + +r.on('readable', function onReadable() { + if (!(r.readableLength % 256)) + console.error('readable', r.readableLength); + r.read(N * 2); +}); + +r.on('end', common.mustCall()); + +r.read(0); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-empty-buffer-no-eof.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-empty-buffer-no-eof.js new file mode 100644 index 00000000000000..15fad698083909 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-empty-buffer-no-eof.js @@ -0,0 +1,124 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const Readable = require('stream').Readable; + +test1(); +test2(); + +function test1() { + const r = new Readable(); + + // Should not end when we get a Buffer.alloc(0) or '' as the _read + // result that just means that there is *temporarily* no data, but to + // go ahead and try again later. + // + // note that this is very unusual. it only works for crypto streams + // because the other side of the stream will call read(0) to cycle + // data through openssl. that's why setImmediate() is used to call + // r.read(0) again later, otherwise there is no more work being done + // and the process just exits. + + const buf = Buffer.alloc(5, 'x'); + let reads = 5; + r._read = function(n) { + switch (reads--) { + case 5: + return setImmediate(() => { + return r.push(buf); + }); + case 4: + setImmediate(() => { + return r.push(Buffer.alloc(0)); + }); + return setImmediate(r.read.bind(r, 0)); + case 3: + setImmediate(r.read.bind(r, 0)); + return process.nextTick(() => { + return r.push(Buffer.alloc(0)); + }); + case 2: + setImmediate(r.read.bind(r, 0)); + return r.push(Buffer.alloc(0)); // Not-EOF! + case 1: + return r.push(buf); + case 0: + return r.push(null); // EOF + default: + throw new Error('unreachable'); + } + }; + + const results = []; + function flow() { + let chunk; + while (null !== (chunk = r.read())) + results.push(String(chunk)); + } + r.on('readable', flow); + r.on('end', () => { + results.push('EOF'); + }); + flow(); + + process.on('exit', () => { + assert.deepStrictEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]); + console.log('ok'); + }); +} + +function test2() { + const r = new Readable({ encoding: 'base64' }); + let reads = 5; + r._read = function(n) { + if (!reads--) + return r.push(null); // EOF + return r.push(Buffer.from('x')); + }; + + const results = []; + function flow() { + let chunk; + while (null !== (chunk = r.read())) + results.push(String(chunk)); + } + r.on('readable', flow); + r.on('end', () => { + results.push('EOF'); + }); + flow(); + + process.on('exit', () => { + assert.deepStrictEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]); + console.log('ok'); + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-from-list.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-from-list.js new file mode 100644 index 00000000000000..72c3b15aab71ba --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-from-list.js @@ -0,0 +1,108 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const fromList = require('stream').Readable._fromList; +const BufferList = require('internal/streams/buffer_list'); +const util = require('util'); + +function bufferListFromArray(arr) { + const bl = new BufferList(); + for (let i = 0; i < arr.length; ++i) + bl.push(arr[i]); + return bl; +} + +{ + // Verify behavior with buffers + let list = [ Buffer.from('foog'), + Buffer.from('bark'), + Buffer.from('bazy'), + Buffer.from('kuel') ]; + list = bufferListFromArray(list); + + assert.strictEqual( + util.inspect([ list ], { compact: false }), + `[ + BufferList { + head: [Object], + tail: [Object], + length: 4 + } +]`); + + // Read more than the first element. + let ret = fromList(6, { buffer: list, length: 16 }); + assert.strictEqual(ret.toString(), 'foogba'); + + // Read exactly the first element. + ret = fromList(2, { buffer: list, length: 10 }); + assert.strictEqual(ret.toString(), 'rk'); + + // Read less than the first element. + ret = fromList(2, { buffer: list, length: 8 }); + assert.strictEqual(ret.toString(), 'ba'); + + // Read more than we have. + ret = fromList(100, { buffer: list, length: 6 }); + assert.strictEqual(ret.toString(), 'zykuel'); + + // all consumed. + assert.deepStrictEqual(list, new BufferList()); +} + +{ + // Verify behavior with strings + let list = [ 'foog', + 'bark', + 'bazy', + 'kuel' ]; + list = bufferListFromArray(list); + + // Read more than the first element. + let ret = fromList(6, { buffer: list, length: 16, decoder: true }); + assert.strictEqual(ret, 'foogba'); + + // Read exactly the first element. + ret = fromList(2, { buffer: list, length: 10, decoder: true }); + assert.strictEqual(ret, 'rk'); + + // Read less than the first element. + ret = fromList(2, { buffer: list, length: 8, decoder: true }); + assert.strictEqual(ret, 'ba'); + + // Read more than we have. + ret = fromList(100, { buffer: list, length: 6, decoder: true }); + assert.strictEqual(ret, 'zykuel'); + + // all consumed. + assert.deepStrictEqual(list, new BufferList()); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-legacy-drain.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-legacy-drain.js new file mode 100644 index 00000000000000..e05af718d7b993 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-legacy-drain.js @@ -0,0 +1,62 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const Stream = require('stream'); +const Readable = Stream.Readable; + +const r = new Readable(); +const N = 256; +let reads = 0; +r._read = function(n) { + return r.push(++reads === N ? null : Buffer.allocUnsafe(1)); +}; + +r.on('end', common.mustCall()); + +const w = new Stream(); +w.writable = true; +let buffered = 0; +w.write = function(c) { + buffered += c.length; + process.nextTick(drain); + return false; +}; + +function drain() { + assert(buffered <= 3); + buffered = 0; + w.emit('drain'); +} + +w.end = common.mustCall(); + +r.pipe(w); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-non-empty-end.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-non-empty-end.js new file mode 100644 index 00000000000000..3b6a1e4d1758b9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-non-empty-end.js @@ -0,0 +1,79 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +let len = 0; +const chunks = new Array(10); +for (let i = 1; i <= 10; i++) { + chunks[i - 1] = Buffer.allocUnsafe(i); + len += i; +} + +const test = new Readable(); +let n = 0; +test._read = function(size) { + const chunk = chunks[n++]; + setTimeout(function() { + test.push(chunk === undefined ? null : chunk); + }, 1); +}; + +test.on('end', thrower); +function thrower() { + throw new Error('this should not happen!'); +} + +let bytesread = 0; +test.on('readable', function() { + const b = len - bytesread - 1; + const res = test.read(b); + if (res) { + bytesread += res.length; + console.error(`br=${bytesread} len=${len}`); + setTimeout(next, 1); + } + test.read(0); +}); +test.read(0); + +function next() { + // Now let's make 'end' happen + test.removeListener('end', thrower); + test.on('end', common.mustCall()); + + // One to get the last byte + let r = test.read(); + assert(r); + assert.strictEqual(r.length, 1); + r = test.read(); + assert.strictEqual(r, null); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-destroy.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-destroy.js new file mode 100644 index 00000000000000..1282cbffabdecf --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-destroy.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +const oldStream = new EE(); +oldStream.pause = () => {}; +oldStream.resume = () => {}; + +{ + new Readable({ + autoDestroy: false, + destroy: common.mustCall() + }) + .wrap(oldStream); + oldStream.emit('destroy'); +} + +{ + new Readable({ + autoDestroy: false, + destroy: common.mustCall() + }) + .wrap(oldStream); + oldStream.emit('close'); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-empty.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-empty.js new file mode 100644 index 00000000000000..235c999a02ed90 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-empty.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +const oldStream = new EE(); +oldStream.pause = () => {}; +oldStream.resume = () => {}; + +const newStream = new Readable().wrap(oldStream); + +newStream + .on('readable', () => {}) + .on('end', common.mustCall()); + +oldStream.emit('end'); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-error.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-error.js new file mode 100644 index 00000000000000..2de9cba5c9a0f1 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap-error.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +class LegacyStream extends EE { + pause() {} + resume() {} +} + +{ + const err = new Error(); + const oldStream = new LegacyStream(); + const r = new Readable({ autoDestroy: true }) + .wrap(oldStream) + .on('error', common.mustCall(() => { + assert.strictEqual(r._readableState.errorEmitted, true); + assert.strictEqual(r._readableState.errored, err); + assert.strictEqual(r.destroyed, true); + })); + oldStream.emit('error', err); +} + +{ + const err = new Error(); + const oldStream = new LegacyStream(); + const r = new Readable({ autoDestroy: false }) + .wrap(oldStream) + .on('error', common.mustCall(() => { + assert.strictEqual(r._readableState.errorEmitted, true); + assert.strictEqual(r._readableState.errored, err); + assert.strictEqual(r.destroyed, false); + })); + oldStream.emit('error', err); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap.js b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap.js new file mode 100644 index 00000000000000..bc131fc8b54eb4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-readable-wrap.js @@ -0,0 +1,107 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); +const EE = require('events').EventEmitter; + +function runTest(highWaterMark, objectMode, produce) { + + const old = new EE(); + const r = new Readable({ highWaterMark, objectMode }); + assert.strictEqual(r, r.wrap(old)); + + r.on('end', common.mustCall()); + + old.pause = function() { + old.emit('pause'); + flowing = false; + }; + + old.resume = function() { + old.emit('resume'); + flow(); + }; + + // Make sure pause is only emitted once. + let pausing = false; + r.on('pause', () => { + assert.strictEqual(pausing, false); + pausing = true; + process.nextTick(() => { + pausing = false; + }); + }); + + let flowing; + let chunks = 10; + let oldEnded = false; + const expected = []; + function flow() { + flowing = true; + while (flowing && chunks-- > 0) { + const item = produce(); + expected.push(item); + old.emit('data', item); + } + if (chunks <= 0) { + oldEnded = true; + old.emit('end'); + } + } + + const w = new Writable({ highWaterMark: highWaterMark * 2, + objectMode }); + const written = []; + w._write = function(chunk, encoding, cb) { + written.push(chunk); + setTimeout(cb, 1); + }; + + w.on('finish', common.mustCall(function() { + performAsserts(); + })); + + r.pipe(w); + + flow(); + + function performAsserts() { + assert(oldEnded); + assert.deepStrictEqual(written, expected); + } +} + +runTest(100, false, function() { return Buffer.allocUnsafe(100); }); +runTest(10, false, function() { return Buffer.from('xxxxxxxxxx'); }); +runTest(1, true, function() { return { foo: 'bar' }; }); + +const objectChunks = [ 5, 'a', false, 0, '', 'xyz', { x: 4 }, 7, [], 555 ]; +runTest(1, true, function() { return objectChunks.shift(); }); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-set-encoding.js b/cli/tests/node_compat/test/parallel/test-stream2-set-encoding.js new file mode 100644 index 00000000000000..ae8d04da9aafe9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-set-encoding.js @@ -0,0 +1,330 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable: R } = require('stream'); + +class TestReader extends R { + constructor(n, opts) { + super(opts); + this.pos = 0; + this.len = n || 100; + } + + _read(n) { + setTimeout(() => { + if (this.pos >= this.len) { + // Double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + n = Math.min(n, this.len - this.pos); + if (n <= 0) { + // Double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + this.pos += n; + const ret = Buffer.alloc(n, 'a'); + + return this.push(ret); + }, 1); + } +} + +{ + // Verify utf8 encoding + const tr = new TestReader(100); + tr.setEncoding('utf8'); + const out = []; + const expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + + +{ + // Verify hex encoding + const tr = new TestReader(100); + tr.setEncoding('hex'); + const out = []; + const expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify hex encoding with read(13) + const tr = new TestReader(100); + tr.setEncoding('hex'); + const out = []; + const expect = + [ '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '16161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify base64 encoding + const tr = new TestReader(100); + tr.setEncoding('base64'); + const out = []; + const expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify utf8 encoding + const tr = new TestReader(100, { encoding: 'utf8' }); + const out = []; + const expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + + +{ + // Verify hex encoding + const tr = new TestReader(100, { encoding: 'hex' }); + const out = []; + const expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify hex encoding with read(13) + const tr = new TestReader(100, { encoding: 'hex' }); + const out = []; + const expect = + [ '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '16161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify base64 encoding + const tr = new TestReader(100, { encoding: 'base64' }); + const out = []; + const expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify chaining behavior + const tr = new TestReader(100); + assert.deepStrictEqual(tr.setEncoding('utf8'), tr); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-transform.js b/cli/tests/node_compat/test/parallel/test-stream2-transform.js new file mode 100644 index 00000000000000..72823369de99ac --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-transform.js @@ -0,0 +1,477 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { PassThrough, Transform } = require('stream'); + +{ + // Verify writable side consumption + const tx = new Transform({ + highWaterMark: 10 + }); + + let transformed = 0; + tx._transform = function(chunk, encoding, cb) { + transformed += chunk.length; + tx.push(chunk); + cb(); + }; + + for (let i = 1; i <= 10; i++) { + tx.write(Buffer.allocUnsafe(i)); + } + tx.end(); + + assert.strictEqual(tx.readableLength, 10); + assert.strictEqual(transformed, 10); + assert.deepStrictEqual(tx.writableBuffer.map(function(c) { + return c.chunk.length; + }), [5, 6, 7, 8, 9, 10]); +} + +{ + // Verify passthrough behavior + const pt = new PassThrough(); + + pt.write(Buffer.from('foog')); + pt.write(Buffer.from('bark')); + pt.write(Buffer.from('bazy')); + pt.write(Buffer.from('kuel')); + pt.end(); + + assert.strictEqual(pt.read(5).toString(), 'foogb'); + assert.strictEqual(pt.read(5).toString(), 'arkba'); + assert.strictEqual(pt.read(5).toString(), 'zykue'); + assert.strictEqual(pt.read(5).toString(), 'l'); +} + +{ + // Verify object passthrough behavior + const pt = new PassThrough({ objectMode: true }); + + pt.write(1); + pt.write(true); + pt.write(false); + pt.write(0); + pt.write('foo'); + pt.write(''); + pt.write({ a: 'b' }); + pt.end(); + + assert.strictEqual(pt.read(), 1); + assert.strictEqual(pt.read(), true); + assert.strictEqual(pt.read(), false); + assert.strictEqual(pt.read(), 0); + assert.strictEqual(pt.read(), 'foo'); + assert.strictEqual(pt.read(), ''); + assert.deepStrictEqual(pt.read(), { a: 'b' }); +} + +{ + // Verify passthrough constructor behavior + const pt = PassThrough(); + + assert(pt instanceof PassThrough); +} + +{ + // Verify transform constructor behavior + const pt = Transform(); + + assert(pt instanceof Transform); +} + +{ + // Perform a simple transform + const pt = new Transform(); + pt._transform = function(c, e, cb) { + const ret = Buffer.alloc(c.length, 'x'); + pt.push(ret); + cb(); + }; + + pt.write(Buffer.from('foog')); + pt.write(Buffer.from('bark')); + pt.write(Buffer.from('bazy')); + pt.write(Buffer.from('kuel')); + pt.end(); + + assert.strictEqual(pt.read(5).toString(), 'xxxxx'); + assert.strictEqual(pt.read(5).toString(), 'xxxxx'); + assert.strictEqual(pt.read(5).toString(), 'xxxxx'); + assert.strictEqual(pt.read(5).toString(), 'x'); +} + +{ + // Verify simple object transform + const pt = new Transform({ objectMode: true }); + pt._transform = function(c, e, cb) { + pt.push(JSON.stringify(c)); + cb(); + }; + + pt.write(1); + pt.write(true); + pt.write(false); + pt.write(0); + pt.write('foo'); + pt.write(''); + pt.write({ a: 'b' }); + pt.end(); + + assert.strictEqual(pt.read(), '1'); + assert.strictEqual(pt.read(), 'true'); + assert.strictEqual(pt.read(), 'false'); + assert.strictEqual(pt.read(), '0'); + assert.strictEqual(pt.read(), '"foo"'); + assert.strictEqual(pt.read(), '""'); + assert.strictEqual(pt.read(), '{"a":"b"}'); +} + +{ + // Verify async passthrough + const pt = new Transform(); + pt._transform = function(chunk, encoding, cb) { + setTimeout(function() { + pt.push(chunk); + cb(); + }, 10); + }; + + pt.write(Buffer.from('foog')); + pt.write(Buffer.from('bark')); + pt.write(Buffer.from('bazy')); + pt.write(Buffer.from('kuel')); + pt.end(); + + pt.on('finish', common.mustCall(function() { + assert.strictEqual(pt.read(5).toString(), 'foogb'); + assert.strictEqual(pt.read(5).toString(), 'arkba'); + assert.strictEqual(pt.read(5).toString(), 'zykue'); + assert.strictEqual(pt.read(5).toString(), 'l'); + })); +} + +{ + // Verify asymmetric transform (expand) + const pt = new Transform(); + + // Emit each chunk 2 times. + pt._transform = function(chunk, encoding, cb) { + setTimeout(function() { + pt.push(chunk); + setTimeout(function() { + pt.push(chunk); + cb(); + }, 10); + }, 10); + }; + + pt.write(Buffer.from('foog')); + pt.write(Buffer.from('bark')); + pt.write(Buffer.from('bazy')); + pt.write(Buffer.from('kuel')); + pt.end(); + + pt.on('finish', common.mustCall(function() { + assert.strictEqual(pt.read(5).toString(), 'foogf'); + assert.strictEqual(pt.read(5).toString(), 'oogba'); + assert.strictEqual(pt.read(5).toString(), 'rkbar'); + assert.strictEqual(pt.read(5).toString(), 'kbazy'); + assert.strictEqual(pt.read(5).toString(), 'bazyk'); + assert.strictEqual(pt.read(5).toString(), 'uelku'); + assert.strictEqual(pt.read(5).toString(), 'el'); + })); +} + +{ + // Verify asymmetric transform (compress) + const pt = new Transform(); + + // Each output is the first char of 3 consecutive chunks, + // or whatever's left. + pt.state = ''; + + pt._transform = function(chunk, encoding, cb) { + if (!chunk) + chunk = ''; + const s = chunk.toString(); + setTimeout(() => { + this.state += s.charAt(0); + if (this.state.length === 3) { + pt.push(Buffer.from(this.state)); + this.state = ''; + } + cb(); + }, 10); + }; + + pt._flush = function(cb) { + // Just output whatever we have. + pt.push(Buffer.from(this.state)); + this.state = ''; + cb(); + }; + + pt.write(Buffer.from('aaaa')); + pt.write(Buffer.from('bbbb')); + pt.write(Buffer.from('cccc')); + pt.write(Buffer.from('dddd')); + pt.write(Buffer.from('eeee')); + pt.write(Buffer.from('aaaa')); + pt.write(Buffer.from('bbbb')); + pt.write(Buffer.from('cccc')); + pt.write(Buffer.from('dddd')); + pt.write(Buffer.from('eeee')); + pt.write(Buffer.from('aaaa')); + pt.write(Buffer.from('bbbb')); + pt.write(Buffer.from('cccc')); + pt.write(Buffer.from('dddd')); + pt.end(); + + // 'abcdeabcdeabcd' + pt.on('finish', common.mustCall(function() { + assert.strictEqual(pt.read(5).toString(), 'abcde'); + assert.strictEqual(pt.read(5).toString(), 'abcde'); + assert.strictEqual(pt.read(5).toString(), 'abcd'); + })); +} + +// This tests for a stall when data is written to a full stream +// that has empty transforms. +{ + // Verify complex transform behavior + let count = 0; + let saved = null; + const pt = new Transform({ highWaterMark: 3 }); + pt._transform = function(c, e, cb) { + if (count++ === 1) + saved = c; + else { + if (saved) { + pt.push(saved); + saved = null; + } + pt.push(c); + } + + cb(); + }; + + pt.once('readable', function() { + process.nextTick(function() { + pt.write(Buffer.from('d')); + pt.write(Buffer.from('ef'), common.mustCall(function() { + pt.end(); + })); + assert.strictEqual(pt.read().toString(), 'abcdef'); + assert.strictEqual(pt.read(), null); + }); + }); + + pt.write(Buffer.from('abc')); +} + + +{ + // Verify passthrough event emission + const pt = new PassThrough(); + let emits = 0; + pt.on('readable', function() { + emits++; + }); + + pt.write(Buffer.from('foog')); + pt.write(Buffer.from('bark')); + + assert.strictEqual(emits, 0); + assert.strictEqual(pt.read(5).toString(), 'foogb'); + assert.strictEqual(String(pt.read(5)), 'null'); + assert.strictEqual(emits, 0); + + pt.write(Buffer.from('bazy')); + pt.write(Buffer.from('kuel')); + + assert.strictEqual(emits, 0); + assert.strictEqual(pt.read(5).toString(), 'arkba'); + assert.strictEqual(pt.read(5).toString(), 'zykue'); + assert.strictEqual(pt.read(5), null); + + pt.end(); + + assert.strictEqual(emits, 1); + assert.strictEqual(pt.read(5).toString(), 'l'); + assert.strictEqual(pt.read(5), null); + assert.strictEqual(emits, 1); +} + +{ + // Verify passthrough event emission reordering + const pt = new PassThrough(); + let emits = 0; + pt.on('readable', function() { + emits++; + }); + + pt.write(Buffer.from('foog')); + pt.write(Buffer.from('bark')); + + assert.strictEqual(emits, 0); + assert.strictEqual(pt.read(5).toString(), 'foogb'); + assert.strictEqual(pt.read(5), null); + + pt.once('readable', common.mustCall(function() { + assert.strictEqual(pt.read(5).toString(), 'arkba'); + assert.strictEqual(pt.read(5), null); + + pt.once('readable', common.mustCall(function() { + assert.strictEqual(pt.read(5).toString(), 'zykue'); + assert.strictEqual(pt.read(5), null); + pt.once('readable', common.mustCall(function() { + assert.strictEqual(pt.read(5).toString(), 'l'); + assert.strictEqual(pt.read(5), null); + assert.strictEqual(emits, 3); + })); + pt.end(); + })); + pt.write(Buffer.from('kuel')); + })); + + pt.write(Buffer.from('bazy')); +} + +{ + // Verify passthrough facade + const pt = new PassThrough(); + const datas = []; + pt.on('data', function(chunk) { + datas.push(chunk.toString()); + }); + + pt.on('end', common.mustCall(function() { + assert.deepStrictEqual(datas, ['foog', 'bark', 'bazy', 'kuel']); + })); + + pt.write(Buffer.from('foog')); + setTimeout(function() { + pt.write(Buffer.from('bark')); + setTimeout(function() { + pt.write(Buffer.from('bazy')); + setTimeout(function() { + pt.write(Buffer.from('kuel')); + setTimeout(function() { + pt.end(); + }, 10); + }, 10); + }, 10); + }, 10); +} + +{ + // Verify object transform (JSON parse) + const jp = new Transform({ objectMode: true }); + jp._transform = function(data, encoding, cb) { + try { + jp.push(JSON.parse(data)); + cb(); + } catch (er) { + cb(er); + } + }; + + // Anything except null/undefined is fine. + // those are "magic" in the stream API, because they signal EOF. + const objects = [ + { foo: 'bar' }, + 100, + 'string', + { nested: { things: [ { foo: 'bar' }, 100, 'string' ] } }, + ]; + + let ended = false; + jp.on('end', function() { + ended = true; + }); + + objects.forEach(function(obj) { + jp.write(JSON.stringify(obj)); + const res = jp.read(); + assert.deepStrictEqual(res, obj); + }); + + jp.end(); + // Read one more time to get the 'end' event + jp.read(); + + process.nextTick(common.mustCall(function() { + assert.strictEqual(ended, true); + })); +} + +{ + // Verify object transform (JSON stringify) + const js = new Transform({ objectMode: true }); + js._transform = function(data, encoding, cb) { + try { + js.push(JSON.stringify(data)); + cb(); + } catch (er) { + cb(er); + } + }; + + // Anything except null/undefined is fine. + // those are "magic" in the stream API, because they signal EOF. + const objects = [ + { foo: 'bar' }, + 100, + 'string', + { nested: { things: [ { foo: 'bar' }, 100, 'string' ] } }, + ]; + + let ended = false; + js.on('end', function() { + ended = true; + }); + + objects.forEach(function(obj) { + js.write(obj); + const res = js.read(); + assert.strictEqual(res, JSON.stringify(obj)); + }); + + js.end(); + // Read one more time to get the 'end' event + js.read(); + + process.nextTick(common.mustCall(function() { + assert.strictEqual(ended, true); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream2-unpipe-drain.js b/cli/tests/node_compat/test/parallel/test-stream2-unpipe-drain.js new file mode 100644 index 00000000000000..65420120c2c66e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-unpipe-drain.js @@ -0,0 +1,79 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class TestWriter extends stream.Writable { + _write(buffer, encoding, callback) { + console.log('write called'); + // Super slow write stream (callback never called) + } +} + +const dest = new TestWriter(); + +class TestReader extends stream.Readable { + constructor() { + super(); + this.reads = 0; + } + + _read(size) { + this.reads += 1; + this.push(Buffer.alloc(size)); + } +} + +const src1 = new TestReader(); +const src2 = new TestReader(); + +src1.pipe(dest); + +src1.once('readable', () => { + process.nextTick(() => { + + src2.pipe(dest); + + src2.once('readable', () => { + process.nextTick(() => { + + src1.unpipe(dest); + }); + }); + }); +}); + + +process.on('exit', () => { + assert.strictEqual(src1.reads, 2); + assert.strictEqual(src2.reads, 2); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-unpipe-leak.js b/cli/tests/node_compat/test/parallel/test-stream2-unpipe-leak.js new file mode 100644 index 00000000000000..ddafb5a9ce5db7 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-unpipe-leak.js @@ -0,0 +1,80 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const chunk = Buffer.from('hallo'); + +class TestWriter extends stream.Writable { + _write(buffer, encoding, callback) { + callback(null); + } +} + +const dest = new TestWriter(); + +// Set this high so that we'd trigger a nextTick warning +// and/or RangeError if we do maybeReadMore wrong. +class TestReader extends stream.Readable { + constructor() { + super({ + highWaterMark: 0x10000 + }); + } + + _read(size) { + this.push(chunk); + } +} + +const src = new TestReader(); + +for (let i = 0; i < 10; i++) { + src.pipe(dest); + src.unpipe(dest); +} + +assert.strictEqual(src.listeners('end').length, 0); +assert.strictEqual(src.listeners('readable').length, 0); + +assert.strictEqual(dest.listeners('unpipe').length, 0); +assert.strictEqual(dest.listeners('drain').length, 0); +assert.strictEqual(dest.listeners('error').length, 0); +assert.strictEqual(dest.listeners('close').length, 0); +assert.strictEqual(dest.listeners('finish').length, 0); + +console.error(src._readableState); +process.on('exit', function() { + src.readableBuffer.length = 0; + console.error(src._readableState); + assert(src.readableLength >= src.readableHighWaterMark); + console.log('ok'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream2-writable.js b/cli/tests/node_compat/test/parallel/test-stream2-writable.js new file mode 100644 index 00000000000000..7ffa829698a3a6 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream2-writable.js @@ -0,0 +1,466 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const { Writable: W, Duplex: D } = require('stream'); +const assert = require('assert'); + +class TestWriter extends W { + constructor(opts) { + super(opts); + this.buffer = []; + this.written = 0; + } + + _write(chunk, encoding, cb) { + // Simulate a small unpredictable latency + setTimeout(() => { + this.buffer.push(chunk.toString()); + this.written += chunk.length; + cb(); + }, Math.floor(Math.random() * 10)); + } +} + +const chunks = new Array(50); +for (let i = 0; i < chunks.length; i++) { + chunks[i] = 'x'.repeat(i); +} + +{ + // Verify fast writing + const tw = new TestWriter({ + highWaterMark: 100 + }); + + tw.on('finish', common.mustCall(function() { + // Got chunks in the right order + assert.deepStrictEqual(tw.buffer, chunks); + })); + + chunks.forEach(function(chunk) { + // Ignore backpressure. Just buffer it all up. + tw.write(chunk); + }); + tw.end(); +} + +{ + // Verify slow writing + const tw = new TestWriter({ + highWaterMark: 100 + }); + + tw.on('finish', common.mustCall(function() { + // Got chunks in the right order + assert.deepStrictEqual(tw.buffer, chunks); + })); + + let i = 0; + (function W() { + tw.write(chunks[i++]); + if (i < chunks.length) + setTimeout(W, 10); + else + tw.end(); + })(); +} + +{ + // Verify write backpressure + const tw = new TestWriter({ + highWaterMark: 50 + }); + + let drains = 0; + + tw.on('finish', common.mustCall(function() { + // Got chunks in the right order + assert.deepStrictEqual(tw.buffer, chunks); + assert.strictEqual(drains, 17); + })); + + tw.on('drain', function() { + drains++; + }); + + let i = 0; + (function W() { + let ret; + do { + ret = tw.write(chunks[i++]); + } while (ret !== false && i < chunks.length); + + if (i < chunks.length) { + assert(tw.writableLength >= 50); + tw.once('drain', W); + } else { + tw.end(); + } + })(); +} + +{ + // Verify write buffersize + const tw = new TestWriter({ + highWaterMark: 100 + }); + + const encodings = + [ 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'latin1', + 'binary', + 'base64', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le', + undefined ]; + + tw.on('finish', function() { + // Got the expected chunks + assert.deepStrictEqual(tw.buffer, chunks); + }); + + chunks.forEach(function(chunk, i) { + const enc = encodings[i % encodings.length]; + chunk = Buffer.from(chunk); + tw.write(chunk.toString(enc), enc); + }); +} + +{ + // Verify write with no buffersize + const tw = new TestWriter({ + highWaterMark: 100, + decodeStrings: false + }); + + tw._write = function(chunk, encoding, cb) { + assert.strictEqual(typeof chunk, 'string'); + chunk = Buffer.from(chunk, encoding); + return TestWriter.prototype._write.call(this, chunk, encoding, cb); + }; + + const encodings = + [ 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'latin1', + 'binary', + 'base64', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le', + undefined ]; + + tw.on('finish', function() { + // Got the expected chunks + assert.deepStrictEqual(tw.buffer, chunks); + }); + + chunks.forEach(function(chunk, i) { + const enc = encodings[i % encodings.length]; + chunk = Buffer.from(chunk); + tw.write(chunk.toString(enc), enc); + }); +} + +{ + // Verify write callbacks + const callbacks = chunks.map(function(chunk, i) { + return [i, function() { + callbacks._called[i] = chunk; + }]; + }).reduce(function(set, x) { + set[`callback-${x[0]}`] = x[1]; + return set; + }, {}); + callbacks._called = []; + + const tw = new TestWriter({ + highWaterMark: 100 + }); + + tw.on('finish', common.mustCall(function() { + process.nextTick(common.mustCall(function() { + // Got chunks in the right order + assert.deepStrictEqual(tw.buffer, chunks); + // Called all callbacks + assert.deepStrictEqual(callbacks._called, chunks); + })); + })); + + chunks.forEach(function(chunk, i) { + tw.write(chunk, callbacks[`callback-${i}`]); + }); + tw.end(); +} + +{ + // Verify end() callback + const tw = new TestWriter(); + tw.end(common.mustCall()); +} + +const helloWorldBuffer = Buffer.from('hello world'); + +{ + // Verify end() callback with chunk + const tw = new TestWriter(); + tw.end(helloWorldBuffer, common.mustCall()); +} + +{ + // Verify end() callback with chunk and encoding + const tw = new TestWriter(); + tw.end('hello world', 'ascii', common.mustCall()); +} + +{ + // Verify end() callback after write() call + const tw = new TestWriter(); + tw.write(helloWorldBuffer); + tw.end(common.mustCall()); +} + +{ + // Verify end() callback after write() callback + const tw = new TestWriter(); + let writeCalledback = false; + tw.write(helloWorldBuffer, function() { + writeCalledback = true; + }); + tw.end(common.mustCall(function() { + assert.strictEqual(writeCalledback, true); + })); +} + +{ + // Verify encoding is ignored for buffers + const tw = new W(); + const hex = '018b5e9a8f6236ffe30e31baf80d2cf6eb'; + tw._write = common.mustCall(function(chunk) { + assert.strictEqual(chunk.toString('hex'), hex); + }); + const buf = Buffer.from(hex, 'hex'); + tw.write(buf, 'latin1'); +} + +{ + // Verify writables cannot be piped + const w = new W({ autoDestroy: false }); + w._write = common.mustNotCall(); + let gotError = false; + w.on('error', function() { + gotError = true; + }); + w.pipe(process.stdout); + assert.strictEqual(gotError, true); +} + +{ + // Verify that duplex streams cannot be piped + const d = new D(); + d._read = common.mustCall(); + d._write = common.mustNotCall(); + let gotError = false; + d.on('error', function() { + gotError = true; + }); + d.pipe(process.stdout); + assert.strictEqual(gotError, false); +} + +{ + // Verify that end(chunk) twice is an error + const w = new W(); + w._write = common.mustCall((msg) => { + assert.strictEqual(msg.toString(), 'this is the end'); + }); + let gotError = false; + w.on('error', function(er) { + gotError = true; + assert.strictEqual(er.message, 'write after end'); + }); + w.end('this is the end'); + w.end('and so is this'); + process.nextTick(common.mustCall(function() { + assert.strictEqual(gotError, true); + })); +} + +{ + // Verify stream doesn't end while writing + const w = new W(); + let wrote = false; + w._write = function(chunk, e, cb) { + assert.strictEqual(this.writing, undefined); + wrote = true; + this.writing = true; + setTimeout(() => { + this.writing = false; + cb(); + }, 1); + }; + w.on('finish', common.mustCall(function() { + assert.strictEqual(wrote, true); + assert.strictEqual(this.writing, false); + })); + w.write(Buffer.alloc(0)); + w.end(); +} + +{ + // Verify finish does not come before write() callback + const w = new W(); + let writeCb = false; + w._write = function(chunk, e, cb) { + setTimeout(function() { + writeCb = true; + cb(); + }, 10); + }; + w.on('finish', common.mustCall(function() { + assert.strictEqual(writeCb, true); + })); + w.write(Buffer.alloc(0)); + w.end(); +} + +{ + // Verify finish does not come before synchronous _write() callback + const w = new W(); + let writeCb = false; + w._write = function(chunk, e, cb) { + cb(); + }; + w.on('finish', common.mustCall(function() { + assert.strictEqual(writeCb, true); + })); + w.write(Buffer.alloc(0), function() { + writeCb = true; + }); + w.end(); +} + +{ + // Verify finish is emitted if the last chunk is empty + const w = new W(); + w._write = function(chunk, e, cb) { + process.nextTick(cb); + }; + w.on('finish', common.mustCall()); + w.write(Buffer.allocUnsafe(1)); + w.end(Buffer.alloc(0)); +} + +{ + // Verify that finish is emitted after shutdown + const w = new W(); + let shutdown = false; + + w._final = common.mustCall(function(cb) { + assert.strictEqual(this, w); + setTimeout(function() { + shutdown = true; + cb(); + }, 100); + }); + w._write = function(chunk, e, cb) { + process.nextTick(cb); + }; + w.on('finish', common.mustCall(function() { + assert.strictEqual(shutdown, true); + })); + w.write(Buffer.allocUnsafe(1)); + w.end(Buffer.allocUnsafe(0)); +} + +{ + // Verify that error is only emitted once when failing in _finish. + const w = new W(); + + w._final = common.mustCall(function(cb) { + cb(new Error('test')); + }); + w.on('error', common.mustCall((err) => { + assert.strictEqual(w._writableState.errorEmitted, true); + assert.strictEqual(err.message, 'test'); + w.on('error', common.mustNotCall()); + w.destroy(new Error()); + })); + w.end(); +} + +{ + // Verify that error is only emitted once when failing in write. + const w = new W(); + w.on('error', common.mustNotCall()); + assert.throws(() => { + w.write(null); + }, { + code: 'ERR_STREAM_NULL_VALUES' + }); +} + +{ + // Verify that error is only emitted once when failing in write after end. + const w = new W(); + w.on('error', common.mustCall((err) => { + assert.strictEqual(w._writableState.errorEmitted, true); + assert.strictEqual(err.code, 'ERR_STREAM_WRITE_AFTER_END'); + })); + w.end(); + w.write('hello'); + w.destroy(new Error()); +} + +{ + // Verify that finish is not emitted after error + const w = new W(); + + w._final = common.mustCall(function(cb) { + cb(new Error()); + }); + w._write = function(chunk, e, cb) { + process.nextTick(cb); + }; + w.on('error', common.mustCall()); + w.on('prefinish', common.mustNotCall()); + w.on('finish', common.mustNotCall()); + w.write(Buffer.allocUnsafe(1)); + w.end(Buffer.allocUnsafe(0)); +} diff --git a/cli/tests/node_compat/test/parallel/test-stream3-cork-end.js b/cli/tests/node_compat/test/parallel/test-stream3-cork-end.js new file mode 100644 index 00000000000000..3ada5dad8bf415 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream3-cork-end.js @@ -0,0 +1,98 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const Writable = stream.Writable; + +// Test the buffering behavior of Writable streams. +// +// The call to cork() triggers storing chunks which are flushed +// on calling end() and the stream subsequently ended. +// +// node version target: 0.12 + +const expectedChunks = ['please', 'buffer', 'me', 'kindly']; +const inputChunks = expectedChunks.slice(0); +let seenChunks = []; +let seenEnd = false; + +const w = new Writable(); +// Let's arrange to store the chunks. +w._write = function(chunk, encoding, cb) { + // Stream end event is not seen before the last write. + assert.ok(!seenEnd); + // Default encoding given none was specified. + assert.strictEqual(encoding, 'buffer'); + + seenChunks.push(chunk); + cb(); +}; +// Let's record the stream end event. +w.on('finish', () => { + seenEnd = true; +}); + +function writeChunks(remainingChunks, callback) { + const writeChunk = remainingChunks.shift(); + let writeState; + + if (writeChunk) { + setImmediate(() => { + writeState = w.write(writeChunk); + // We were not told to stop writing. + assert.ok(writeState); + + writeChunks(remainingChunks, callback); + }); + } else { + callback(); + } +} + +// Do an initial write. +w.write('stuff'); +// The write was immediate. +assert.strictEqual(seenChunks.length, 1); +// Reset the seen chunks. +seenChunks = []; + +// Trigger stream buffering. +w.cork(); + +// Write the bufferedChunks. +writeChunks(inputChunks, () => { + // Should not have seen anything yet. + assert.strictEqual(seenChunks.length, 0); + + // Trigger flush and ending the stream. + w.end(); + + // Stream should not ended in current tick. + assert.ok(!seenEnd); + + // Buffered bytes should be seen in current tick. + assert.strictEqual(seenChunks.length, 4); + + // Did the chunks match. + for (let i = 0, l = expectedChunks.length; i < l; i++) { + const seen = seenChunks[i]; + // There was a chunk. + assert.ok(seen); + + const expected = Buffer.from(expectedChunks[i]); + // It was what we expected. + assert.ok(seen.equals(expected)); + } + + setImmediate(() => { + // Stream should have ended in next tick. + assert.ok(seenEnd); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream3-cork-uncork.js b/cli/tests/node_compat/test/parallel/test-stream3-cork-uncork.js new file mode 100644 index 00000000000000..eb2365b0146838 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream3-cork-uncork.js @@ -0,0 +1,93 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const Writable = stream.Writable; + +// Test the buffering behavior of Writable streams. +// +// The call to cork() triggers storing chunks which are flushed +// on calling uncork() in the same tick. +// +// node version target: 0.12 + +const expectedChunks = ['please', 'buffer', 'me', 'kindly']; +const inputChunks = expectedChunks.slice(0); +let seenChunks = []; +let seenEnd = false; + +const w = new Writable(); +// Let's arrange to store the chunks. +w._write = function(chunk, encoding, cb) { + // Default encoding given none was specified. + assert.strictEqual(encoding, 'buffer'); + + seenChunks.push(chunk); + cb(); +}; +// Let's record the stream end event. +w.on('finish', () => { + seenEnd = true; +}); + +function writeChunks(remainingChunks, callback) { + const writeChunk = remainingChunks.shift(); + let writeState; + + if (writeChunk) { + setImmediate(() => { + writeState = w.write(writeChunk); + // We were not told to stop writing. + assert.ok(writeState); + + writeChunks(remainingChunks, callback); + }); + } else { + callback(); + } +} + +// Do an initial write. +w.write('stuff'); +// The write was immediate. +assert.strictEqual(seenChunks.length, 1); +// Reset the chunks seen so far. +seenChunks = []; + +// Trigger stream buffering. +w.cork(); + +// Write the bufferedChunks. +writeChunks(inputChunks, () => { + // Should not have seen anything yet. + assert.strictEqual(seenChunks.length, 0); + + // Trigger writing out the buffer. + w.uncork(); + + // Buffered bytes should be seen in current tick. + assert.strictEqual(seenChunks.length, 4); + + // Did the chunks match. + for (let i = 0, l = expectedChunks.length; i < l; i++) { + const seen = seenChunks[i]; + // There was a chunk. + assert.ok(seen); + + const expected = Buffer.from(expectedChunks[i]); + // It was what we expected. + assert.ok(seen.equals(expected)); + } + + setImmediate(() => { + // The stream should not have been ended. + assert.ok(!seenEnd); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-stream3-pause-then-read.js b/cli/tests/node_compat/test/parallel/test-stream3-pause-then-read.js new file mode 100644 index 00000000000000..583e2ff8081033 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-stream3-pause-then-read.js @@ -0,0 +1,177 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +const Readable = stream.Readable; +const Writable = stream.Writable; + +const totalChunks = 100; +const chunkSize = 99; +const expectTotalData = totalChunks * chunkSize; +let expectEndingData = expectTotalData; + +const r = new Readable({ highWaterMark: 1000 }); +let chunks = totalChunks; +r._read = function(n) { + console.log('_read called', chunks); + if (!(chunks % 2)) + setImmediate(push); + else if (!(chunks % 3)) + process.nextTick(push); + else + push(); +}; + +let totalPushed = 0; +function push() { + const chunk = chunks-- > 0 ? Buffer.alloc(chunkSize, 'x') : null; + if (chunk) { + totalPushed += chunk.length; + } + console.log('chunks', chunks); + r.push(chunk); +} + +read100(); + +// First we read 100 bytes. +function read100() { + readn(100, onData); +} + +function readn(n, then) { + console.error(`read ${n}`); + expectEndingData -= n; + (function read() { + const c = r.read(n); + console.error('c', c); + if (!c) + r.once('readable', read); + else { + assert.strictEqual(c.length, n); + assert(!r.readableFlowing); + then(); + } + })(); +} + +// Then we listen to some data events. +function onData() { + expectEndingData -= 100; + console.error('onData'); + let seen = 0; + r.on('data', function od(c) { + seen += c.length; + if (seen >= 100) { + // Seen enough + r.removeListener('data', od); + r.pause(); + if (seen > 100) { + // Oh no, seen too much! + // Put the extra back. + const diff = seen - 100; + r.unshift(c.slice(c.length - diff)); + console.error('seen too much', seen, diff); + } + + // Nothing should be lost in-between. + setImmediate(pipeLittle); + } + }); +} + +// Just pipe 200 bytes, then unshift the extra and unpipe. +function pipeLittle() { + expectEndingData -= 200; + console.error('pipe a little'); + const w = new Writable(); + let written = 0; + w.on('finish', () => { + assert.strictEqual(written, 200); + setImmediate(read1234); + }); + w._write = function(chunk, encoding, cb) { + written += chunk.length; + if (written >= 200) { + r.unpipe(w); + w.end(); + cb(); + if (written > 200) { + const diff = written - 200; + written -= diff; + r.unshift(chunk.slice(chunk.length - diff)); + } + } else { + setImmediate(cb); + } + }; + r.pipe(w); +} + +// Now read 1234 more bytes. +function read1234() { + readn(1234, resumePause); +} + +function resumePause() { + console.error('resumePause'); + // Don't read anything, just resume and re-pause a whole bunch. + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + setImmediate(pipe); +} + + +function pipe() { + console.error('pipe the rest'); + const w = new Writable(); + let written = 0; + w._write = function(chunk, encoding, cb) { + written += chunk.length; + cb(); + }; + w.on('finish', () => { + console.error('written', written, totalPushed); + assert.strictEqual(written, expectEndingData); + assert.strictEqual(totalPushed, expectTotalData); + console.log('ok'); + }); + r.pipe(w); +} diff --git a/cli/tests/node_compat/test/parallel/test-streams-highwatermark.js b/cli/tests/node_compat/test/parallel/test-streams-highwatermark.js new file mode 100644 index 00000000000000..623d7450a44baa --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-streams-highwatermark.js @@ -0,0 +1,94 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); +const { inspect } = require('util'); + +{ + // This test ensures that the stream implementation correctly handles values + // for highWaterMark which exceed the range of signed 32 bit integers and + // rejects invalid values. + + // This number exceeds the range of 32 bit integer arithmetic but should still + // be handled correctly. + const ovfl = Number.MAX_SAFE_INTEGER; + + const readable = stream.Readable({ highWaterMark: ovfl }); + assert.strictEqual(readable._readableState.highWaterMark, ovfl); + + const writable = stream.Writable({ highWaterMark: ovfl }); + assert.strictEqual(writable._writableState.highWaterMark, ovfl); + + for (const invalidHwm of [true, false, '5', {}, -5, NaN]) { + for (const type of [stream.Readable, stream.Writable]) { + assert.throws(() => { + type({ highWaterMark: invalidHwm }); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE', + message: "The property 'options.highWaterMark' is invalid. " + + `Received ${inspect(invalidHwm)}` + }); + } + } +} + +{ + // This test ensures that the push method's implementation + // correctly handles the edge case where the highWaterMark and + // the state.length are both zero + + const readable = stream.Readable({ highWaterMark: 0 }); + + for (let i = 0; i < 3; i++) { + const needMoreData = readable.push(); + assert.strictEqual(needMoreData, true); + } +} + +{ + // This test ensures that the read(n) method's implementation + // correctly handles the edge case where the highWaterMark, state.length + // and n are all zero + + const readable = stream.Readable({ highWaterMark: 0 }); + + readable._read = common.mustCall(); + readable.read(0); +} + +{ + // Parse size as decimal integer + ['1', '1.0', 1].forEach((size) => { + const readable = new stream.Readable({ + read: common.mustCall(), + highWaterMark: 0, + }); + readable.read(size); + + assert.strictEqual(readable._readableState.highWaterMark, Number(size)); + }); +} + +{ + // Test highwatermark limit + const hwm = 0x40000000 + 1; + const readable = stream.Readable({ + read() {}, + }); + + assert.throws(() => readable.read(hwm), common.expectsError({ + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "size" is out of range.' + + ' It must be <= 1GiB. Received ' + + hwm, + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-timers-api-refs.js b/cli/tests/node_compat/test/parallel/test-timers-api-refs.js new file mode 100644 index 00000000000000..091b69f9f18511 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-api-refs.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const timers = require('timers'); + +// Delete global APIs to make sure they're not relied on by the internal timers +// code +delete global.setTimeout; +delete global.clearTimeout; +delete global.setInterval; +delete global.clearInterval; +delete global.setImmediate; +delete global.clearImmediate; + +const timeoutCallback = () => { timers.clearTimeout(timeout); }; +const timeout = timers.setTimeout(common.mustCall(timeoutCallback), 1); + +const intervalCallback = () => { timers.clearInterval(interval); }; +const interval = timers.setInterval(common.mustCall(intervalCallback), 1); + +const immediateCallback = () => { timers.clearImmediate(immediate); }; +const immediate = timers.setImmediate(immediateCallback); diff --git a/cli/tests/node_compat/test/parallel/test-timers-args.js b/cli/tests/node_compat/test/parallel/test-timers-args.js new file mode 100644 index 00000000000000..d9a77bbc27d468 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-args.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); + +function range(n) { + return 'x'.repeat(n + 1).split('').map(function(_, i) { return i; }); +} + +function timeout(nargs) { + const args = range(nargs); + setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) timeout(nargs + 1); + } +} + +function interval(nargs) { + const args = range(nargs); + const timer = setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + clearInterval(timer); + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) interval(nargs + 1); + } +} + +timeout(0); +interval(0); diff --git a/cli/tests/node_compat/test/parallel/test-timers-clear-null-does-not-throw-error.js b/cli/tests/node_compat/test/parallel/test-timers-clear-null-does-not-throw-error.js new file mode 100644 index 00000000000000..ab0335d23ce161 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-clear-null-does-not-throw-error.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test makes sure clearing timers with +// 'null' or no input does not throw error +clearInterval(null); +clearInterval(); +clearTimeout(null); +clearTimeout(); +clearImmediate(null); +clearImmediate(); diff --git a/cli/tests/node_compat/test/parallel/test-timers-clear-object-does-not-throw-error.js b/cli/tests/node_compat/test/parallel/test-timers-clear-object-does-not-throw-error.js new file mode 100644 index 00000000000000..5df30724066a87 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-clear-object-does-not-throw-error.js @@ -0,0 +1,15 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test makes sure clearing timers with +// objects doesn't throw +clearImmediate({}); +clearTimeout({}); +clearInterval({}); diff --git a/cli/tests/node_compat/test/parallel/test-timers-clear-timeout-interval-equivalent.js b/cli/tests/node_compat/test/parallel/test-timers-clear-timeout-interval-equivalent.js new file mode 100644 index 00000000000000..5620ad1a007978 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-clear-timeout-interval-equivalent.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// This test makes sure that timers created with setTimeout can be disarmed by +// clearInterval and that timers created with setInterval can be disarmed by +// clearTimeout. +// +// This behavior is documented in the HTML Living Standard: +// +// * Refs: https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval + +// Disarm interval with clearTimeout. +const interval = setInterval(common.mustNotCall(), 1); +clearTimeout(interval); + +// Disarm timeout with clearInterval. +const timeout = setTimeout(common.mustNotCall(), 1); +clearInterval(timeout); diff --git a/cli/tests/node_compat/test/parallel/test-timers-clearImmediate.js b/cli/tests/node_compat/test/parallel/test-timers-clearImmediate.js new file mode 100644 index 00000000000000..c6e55cf6b8204e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-clearImmediate.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const N = 3; + +function next() { + const fn = common.mustCall(() => clearImmediate(immediate)); + const immediate = setImmediate(fn); +} + +for (let i = 0; i < N; i++) { + next(); +} diff --git a/cli/tests/node_compat/test/parallel/test-timers-interval-throw.js b/cli/tests/node_compat/test/parallel/test-timers-interval-throw.js new file mode 100644 index 00000000000000..f5db9f62d73184 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-interval-throw.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// To match browser behaviour, interval should continue +// being rescheduled even if it throws. + +let count = 2; +const interval = setInterval(() => { throw new Error('IntervalError'); }, 1); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'IntervalError'); + if (--count === 0) { + clearInterval(interval); + } +}, 2)); diff --git a/cli/tests/node_compat/test/parallel/test-timers-non-integer-delay.js b/cli/tests/node_compat/test/parallel/test-timers-non-integer-delay.js new file mode 100644 index 00000000000000..158870f1a3a6a5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-non-integer-delay.js @@ -0,0 +1,88 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// This test makes sure that non-integer timer delays do not make the process +// hang. See https://github.com/joyent/node/issues/8065 and +// https://github.com/joyent/node/issues/8068 which have been fixed by +// https://github.com/joyent/node/pull/8073. +// +// If the process hangs, this test will make the tests suite timeout, +// otherwise it will exit very quickly (after 50 timers with a short delay +// fire). +// +// We have to set at least several timers with a non-integer delay to +// reproduce the issue. Sometimes, a timer with a non-integer delay will +// expire correctly. 50 timers has always been more than enough to reproduce +// it 100%. + +const TIMEOUT_DELAY = 1.1; +let N = 50; + +const interval = setInterval(common.mustCall(() => { + if (--N === 0) { + clearInterval(interval); + } +}, N), TIMEOUT_DELAY); + +// Test non-integer delay ordering +{ + const ordering = []; + + setTimeout(common.mustCall(() => { + ordering.push(1); + }), 1); + + setTimeout(common.mustCall(() => { + ordering.push(2); + }), 1.8); + + setTimeout(common.mustCall(() => { + ordering.push(3); + }), 1.1); + + setTimeout(common.mustCall(() => { + ordering.push(4); + }), 1); + + setTimeout(common.mustCall(() => { + const expected = [1, 2, 3, 4]; + + assert.deepStrictEqual( + ordering, + expected, + `Non-integer delay ordering should be ${expected}, but got ${ordering}` + ); + + // 2 should always be last of these delays due to ordering guarantees by + // the implementation. + }), 2); +} diff --git a/cli/tests/node_compat/test/parallel/test-timers-refresh.js b/cli/tests/node_compat/test/parallel/test-timers-refresh.js new file mode 100644 index 00000000000000..d2e49268c6c3fc --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-refresh.js @@ -0,0 +1,109 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals + +'use strict'; + +const common = require('../common'); + +const { strictEqual, throws } = require('assert'); +const { setUnrefTimeout } = require('internal/timers'); + +// Schedule the unrefed cases first so that the later case keeps the event loop +// active. + +// Every case in this test relies on implicit sorting within either Node's or +// libuv's timers storage data structures. + +// unref()'d timer +{ + let called = false; + const timer = setTimeout(common.mustCall(() => { + called = true; + }), 1); + timer.unref(); + + // This relies on implicit timers handle sorting within libuv. + + setTimeout(common.mustCall(() => { + strictEqual(called, false, 'unref()\'d timer returned before check'); + }), 1); + + strictEqual(timer.refresh(), timer); +} + +// Should throw with non-functions +{ + [null, true, false, 0, 1, NaN, '', 'foo', {}, Symbol()].forEach((cb) => { + throws( + () => setUnrefTimeout(cb), + { + code: 'ERR_INVALID_ARG_TYPE', + } + ); + }); +} + +// unref pooled timer +{ + let called = false; + const timer = setUnrefTimeout(common.mustCall(() => { + called = true; + }), 1); + + setUnrefTimeout(common.mustCall(() => { + strictEqual(called, false, 'unref pooled timer returned before check'); + }), 1); + + strictEqual(timer.refresh(), timer); +} + +// regular timer +{ + let called = false; + const timer = setTimeout(common.mustCall(() => { + called = true; + }), 1); + + setTimeout(common.mustCall(() => { + strictEqual(called, false, 'pooled timer returned before check'); + }), 1); + + strictEqual(timer.refresh(), timer); +} + +// regular timer +{ + let called = false; + const timer = setTimeout(common.mustCall(() => { + if (!called) { + called = true; + process.nextTick(common.mustCall(() => { + timer.refresh(); + strictEqual(timer.hasRef(), true); + })); + } + }, 2), 1); +} + +// interval +{ + let called = 0; + const timer = setInterval(common.mustCall(() => { + called += 1; + if (called === 2) { + clearInterval(timer); + } + }, 2), 1); + + setTimeout(common.mustCall(() => { + strictEqual(called, 0, 'pooled timer returned before check'); + }), 1); + + strictEqual(timer.refresh(), timer); +} diff --git a/cli/tests/node_compat/test/parallel/test-timers-same-timeout-wrong-list-deleted.js b/cli/tests/node_compat/test/parallel/test-timers-same-timeout-wrong-list-deleted.js new file mode 100644 index 00000000000000..d9d65f1bd0b2a8 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-same-timeout-wrong-list-deleted.js @@ -0,0 +1,41 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This is a regression test for https://github.com/nodejs/node/issues/7722. +// +// When nested timers have the same timeout, calling clearTimeout on the +// older timer after it has fired causes the list the newer timer is in +// to be deleted. Since the newer timer was not cleared, it still blocks +// the event loop completing for the duration of its timeout, however, since +// no reference exists to it in its list, it cannot be canceled and its +// callback is not called when the timeout elapses. + +const common = require('../common'); + +const TIMEOUT = common.platformTimeout(100); + +const handle1 = setTimeout(common.mustCall(function() { + // Cause the old TIMEOUT list to be deleted + clearTimeout(handle1); + + // Cause a new list with the same key (TIMEOUT) to be created for this timer + const handle2 = setTimeout(common.mustNotCall(), TIMEOUT); + + setTimeout(common.mustCall(function() { + // Attempt to cancel the second timer. Fix for this bug will keep the + // newer timer from being dereferenced by keeping its list from being + // erroneously deleted. If we are able to cancel the timer successfully, + // the bug is fixed. + clearTimeout(handle2); + }), 1); + + // When this callback completes, `listOnTimeout` should now look at the + // correct list and refrain from removing the new TIMEOUT list which + // contains the reference to the newer timer. +}), TIMEOUT); diff --git a/cli/tests/node_compat/test/parallel/test-timers-timeout-with-non-integer.js b/cli/tests/node_compat/test/parallel/test-timers-timeout-with-non-integer.js new file mode 100644 index 00000000000000..e823f9f51c235e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-timeout-with-non-integer.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +/** + * This test is for https://github.com/nodejs/node/issues/24203 + */ +let count = 50; +const time = 1.00000000000001; +const exec = common.mustCall(() => { + if (--count === 0) { + return; + } + setTimeout(exec, time); +}, count); +exec(); diff --git a/cli/tests/node_compat/test/parallel/test-timers-uncaught-exception.js b/cli/tests/node_compat/test/parallel/test-timers-uncaught-exception.js new file mode 100644 index 00000000000000..11d2f58e8e165e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-uncaught-exception.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const errorMsg = 'BAM!'; + +// The first timer throws... +setTimeout(common.mustCall(function() { + throw new Error(errorMsg); +}), 1); + +// ...but the second one should still run +setTimeout(common.mustCall(), 1); + +function uncaughtException(err) { + assert.strictEqual(err.message, errorMsg); +} + +process.on('uncaughtException', common.mustCall(uncaughtException)); diff --git a/cli/tests/node_compat/test/parallel/test-timers-unref-throw-then-ref.js b/cli/tests/node_compat/test/parallel/test-timers-unref-throw-then-ref.js new file mode 100644 index 00000000000000..a050640a472f29 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-unref-throw-then-ref.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.once('uncaughtException', common.mustCall((err) => { + common.expectsError({ + message: 'Timeout Error' + })(err); +})); + +let called = false; +const t = setTimeout(() => { + assert(!called); + called = true; + t.ref(); + throw new Error('Timeout Error'); +}, 1).unref(); + +setTimeout(common.mustCall(), 1); diff --git a/cli/tests/node_compat/test/parallel/test-timers-user-call.js b/cli/tests/node_compat/test/parallel/test-timers-user-call.js new file mode 100644 index 00000000000000..eefc8e6c804e81 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-user-call.js @@ -0,0 +1,47 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Make sure `setTimeout()` and friends don't throw if the user-supplied +// function has .call() and .apply() monkey-patched to undesirable values. + +// Refs: https://github.com/nodejs/node/issues/12956 + +'use strict'; + +const common = require('../common'); + +{ + const fn = common.mustCall(10); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + setTimeout(fn, 1); + setTimeout(fn, 1, 'oneArg'); + setTimeout(fn, 1, 'two', 'args'); + setTimeout(fn, 1, 'three', '(3)', 'args'); + setTimeout(fn, 1, 'more', 'than', 'three', 'args'); + + setImmediate(fn, 1); + setImmediate(fn, 1, 'oneArg'); + setImmediate(fn, 1, 'two', 'args'); + setImmediate(fn, 1, 'three', '(3)', 'args'); + setImmediate(fn, 1, 'more', 'than', 'three', 'args'); +} + +{ + const testInterval = (...args) => { + const fn = common.mustCall(() => { clearInterval(interval); }); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + const interval = setInterval(fn, 1, ...args); + }; + + testInterval(); + testInterval('oneArg'); + testInterval('two', 'args'); + testInterval('three', '(3)', 'args'); + testInterval('more', 'than', 'three', 'args'); +} diff --git a/cli/tests/node_compat/test/parallel/test-timers-zero-timeout.js b/cli/tests/node_compat/test/parallel/test-timers-zero-timeout.js new file mode 100644 index 00000000000000..3ff385d51dd682 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-timers-zero-timeout.js @@ -0,0 +1,56 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// https://github.com/joyent/node/issues/2079 - zero timeout drops extra args +{ + setTimeout(common.mustCall(f), 0, 'foo', 'bar', 'baz'); + setTimeout(() => {}, 0); + + function f(a, b, c) { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + } +} + +{ + let ncalled = 3; + + const f = common.mustCall((a, b, c) => { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + if (--ncalled === 0) clearTimeout(iv); + }, ncalled); + + const iv = setInterval(f, 0, 'foo', 'bar', 'baz'); +} diff --git a/cli/tests/node_compat/test/parallel/test-tls-connect-simple.js b/cli/tests/node_compat/test/parallel/test-tls-connect-simple.js new file mode 100644 index 00000000000000..01a688f3f5738e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-tls-connect-simple.js @@ -0,0 +1,69 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +let serverConnected = 0; + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = tls.Server(options, common.mustCall(function(socket) { + if (++serverConnected === 2) { + server.close(common.mustCall()); + server.on('close', common.mustCall()); + } +}, 2)); + +server.listen(0, function() { + const client1options = { + port: this.address().port, + rejectUnauthorized: false + }; + const client1 = tls.connect(client1options, common.mustCall(function() { + client1.end(); + })); + + const client2options = { + port: this.address().port, + rejectUnauthorized: false + }; + const client2 = tls.connect(client2options); + client2.on('secureConnect', common.mustCall(function() { + client2.end(); + })); +}); diff --git a/cli/tests/node_compat/test/parallel/test-url-domain-ascii-unicode.js b/cli/tests/node_compat/test/parallel/test-url-domain-ascii-unicode.js new file mode 100644 index 00000000000000..fb622070ba29c4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-domain-ascii-unicode.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +if (!common.hasIntl) + common.skip('missing Intl'); + +const strictEqual = require('assert').strictEqual; +const url = require('url'); + +const domainToASCII = url.domainToASCII; +const domainToUnicode = url.domainToUnicode; + +const domainWithASCII = [ + ['ıíd', 'xn--d-iga7r'], + ['يٴ', 'xn--mhb8f'], + ['www.ϧƽəʐ.com', 'www.xn--cja62apfr6c.com'], + ['новини.com', 'xn--b1amarcd.com'], + ['名がドメイン.com', 'xn--v8jxj3d1dzdz08w.com'], + ['افغانستا.icom.museum', 'xn--mgbaal8b0b9b2b.icom.museum'], + ['الجزائر.icom.fake', 'xn--lgbbat1ad8j.icom.fake'], + ['भारत.org', 'xn--h2brj9c.org'], +]; + +domainWithASCII.forEach((pair) => { + const domain = pair[0]; + const ascii = pair[1]; + const domainConvertedToASCII = domainToASCII(domain); + strictEqual(domainConvertedToASCII, ascii); + const asciiConvertedToUnicode = domainToUnicode(ascii); + strictEqual(asciiConvertedToUnicode, domain); +}); diff --git a/cli/tests/node_compat/test/parallel/test-url-fileurltopath.js b/cli/tests/node_compat/test/parallel/test-url-fileurltopath.js new file mode 100644 index 00000000000000..ae679bb5ae7f7b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-fileurltopath.js @@ -0,0 +1,161 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); +const url = require('url'); + +function testInvalidArgs(...args) { + for (const arg of args) { + assert.throws(() => url.fileURLToPath(arg), { + code: 'ERR_INVALID_ARG_TYPE' + }); + } +} + +// Input must be string or URL +testInvalidArgs(null, undefined, 1, {}, true); + +// Input must be a file URL +assert.throws(() => url.fileURLToPath('https://a/b/c'), { + code: 'ERR_INVALID_URL_SCHEME' +}); + +{ + const withHost = new URL('file://host/a'); + + if (isWindows) { + assert.strictEqual(url.fileURLToPath(withHost), '\\\\host\\a'); + } else { + assert.throws(() => url.fileURLToPath(withHost), { + code: 'ERR_INVALID_FILE_URL_HOST' + }); + } +} + +{ + if (isWindows) { + assert.throws(() => url.fileURLToPath('file:///C:/a%2F/'), { + code: 'ERR_INVALID_FILE_URL_PATH' + }); + assert.throws(() => url.fileURLToPath('file:///C:/a%5C/'), { + code: 'ERR_INVALID_FILE_URL_PATH' + }); + assert.throws(() => url.fileURLToPath('file:///?:/'), { + code: 'ERR_INVALID_FILE_URL_PATH' + }); + } else { + assert.throws(() => url.fileURLToPath('file:///a%2F/'), { + code: 'ERR_INVALID_FILE_URL_PATH' + }); + } +} + +{ + let testCases; + if (isWindows) { + testCases = [ + // Lowercase ascii alpha + { path: 'C:\\foo', fileURL: 'file:///C:/foo' }, + // Uppercase ascii alpha + { path: 'C:\\FOO', fileURL: 'file:///C:/FOO' }, + // dir + { path: 'C:\\dir\\foo', fileURL: 'file:///C:/dir/foo' }, + // trailing separator + { path: 'C:\\dir\\', fileURL: 'file:///C:/dir/' }, + // dot + { path: 'C:\\foo.mjs', fileURL: 'file:///C:/foo.mjs' }, + // space + { path: 'C:\\foo bar', fileURL: 'file:///C:/foo%20bar' }, + // question mark + { path: 'C:\\foo?bar', fileURL: 'file:///C:/foo%3Fbar' }, + // number sign + { path: 'C:\\foo#bar', fileURL: 'file:///C:/foo%23bar' }, + // ampersand + { path: 'C:\\foo&bar', fileURL: 'file:///C:/foo&bar' }, + // equals + { path: 'C:\\foo=bar', fileURL: 'file:///C:/foo=bar' }, + // colon + { path: 'C:\\foo:bar', fileURL: 'file:///C:/foo:bar' }, + // semicolon + { path: 'C:\\foo;bar', fileURL: 'file:///C:/foo;bar' }, + // percent + { path: 'C:\\foo%bar', fileURL: 'file:///C:/foo%25bar' }, + // backslash + { path: 'C:\\foo\\bar', fileURL: 'file:///C:/foo/bar' }, + // backspace + { path: 'C:\\foo\bbar', fileURL: 'file:///C:/foo%08bar' }, + // tab + { path: 'C:\\foo\tbar', fileURL: 'file:///C:/foo%09bar' }, + // newline + { path: 'C:\\foo\nbar', fileURL: 'file:///C:/foo%0Abar' }, + // carriage return + { path: 'C:\\foo\rbar', fileURL: 'file:///C:/foo%0Dbar' }, + // latin1 + { path: 'C:\\fóóbàr', fileURL: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' }, + // Euro sign (BMP code point) + { path: 'C:\\€', fileURL: 'file:///C:/%E2%82%AC' }, + // Rocket emoji (non-BMP code point) + { path: 'C:\\🚀', fileURL: 'file:///C:/%F0%9F%9A%80' }, + // UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows) + { path: '\\\\nas\\My Docs\\File.doc', fileURL: 'file://nas/My%20Docs/File.doc' }, + ]; + } else { + testCases = [ + // Lowercase ascii alpha + { path: '/foo', fileURL: 'file:///foo' }, + // Uppercase ascii alpha + { path: '/FOO', fileURL: 'file:///FOO' }, + // dir + { path: '/dir/foo', fileURL: 'file:///dir/foo' }, + // trailing separator + { path: '/dir/', fileURL: 'file:///dir/' }, + // dot + { path: '/foo.mjs', fileURL: 'file:///foo.mjs' }, + // space + { path: '/foo bar', fileURL: 'file:///foo%20bar' }, + // question mark + { path: '/foo?bar', fileURL: 'file:///foo%3Fbar' }, + // number sign + { path: '/foo#bar', fileURL: 'file:///foo%23bar' }, + // ampersand + { path: '/foo&bar', fileURL: 'file:///foo&bar' }, + // equals + { path: '/foo=bar', fileURL: 'file:///foo=bar' }, + // colon + { path: '/foo:bar', fileURL: 'file:///foo:bar' }, + // semicolon + { path: '/foo;bar', fileURL: 'file:///foo;bar' }, + // percent + { path: '/foo%bar', fileURL: 'file:///foo%25bar' }, + // backslash + { path: '/foo\\bar', fileURL: 'file:///foo%5Cbar' }, + // backspace + { path: '/foo\bbar', fileURL: 'file:///foo%08bar' }, + // tab + { path: '/foo\tbar', fileURL: 'file:///foo%09bar' }, + // newline + { path: '/foo\nbar', fileURL: 'file:///foo%0Abar' }, + // carriage return + { path: '/foo\rbar', fileURL: 'file:///foo%0Dbar' }, + // latin1 + { path: '/fóóbàr', fileURL: 'file:///f%C3%B3%C3%B3b%C3%A0r' }, + // Euro sign (BMP code point) + { path: '/€', fileURL: 'file:///%E2%82%AC' }, + // Rocket emoji (non-BMP code point) + { path: '/🚀', fileURL: 'file:///%F0%9F%9A%80' }, + ]; + } + + for (const { path, fileURL } of testCases) { + const fromString = url.fileURLToPath(fileURL); + assert.strictEqual(fromString, path); + const fromURL = url.fileURLToPath(new URL(fileURL)); + assert.strictEqual(fromURL, path); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-url-format-invalid-input.js b/cli/tests/node_compat/test/parallel/test-url-format-invalid-input.js new file mode 100644 index 00000000000000..c42ee2c09f6a89 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-format-invalid-input.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const url = require('url'); + +const throwsObjsAndReportTypes = [ + undefined, + null, + true, + false, + 0, + function() {}, + Symbol('foo'), +]; + +for (const urlObject of throwsObjsAndReportTypes) { + assert.throws(() => { + url.format(urlObject); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "urlObject" argument must be one of type object or string.' + + common.invalidArgTypeHelper(urlObject) + }); +} +assert.strictEqual(url.format(''), ''); +assert.strictEqual(url.format({}), ''); diff --git a/cli/tests/node_compat/test/parallel/test-url-format-whatwg.js b/cli/tests/node_compat/test/parallel/test-url-format-whatwg.js new file mode 100644 index 00000000000000..ea099f494c4e95 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-format-whatwg.js @@ -0,0 +1,149 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const url = require('url'); + +const myURL = new URL('http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'); + +assert.strictEqual( + url.format(myURL), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, {}), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +{ + [true, 1, 'test', Infinity].forEach((value) => { + assert.throws( + () => url.format(myURL, value), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options" argument must be of type object.' + + common.invalidArgTypeHelper(value) + } + ); + }); +} + +// Any falsy value other than undefined will be treated as false. +// Any truthy value will be treated as true. + +assert.strictEqual( + url.format(myURL, { auth: false }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: '' }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: 0 }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { fragment: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: '' }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { fragment: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { search: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: '' }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { search: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: true }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: 1 }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: {} }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(new URL('http://user:pass@xn--0zwm56d.com:8080/path'), { unicode: true }), + 'http://user:pass@测试.com:8080/path' +); diff --git a/cli/tests/node_compat/test/parallel/test-url-format.js b/cli/tests/node_compat/test/parallel/test-url-format.js new file mode 100644 index 00000000000000..1437bdb879e6db --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-format.js @@ -0,0 +1,284 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const url = require('url'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +// Formatting tests to verify that it'll format slightly wonky content to a +// valid URL. +const formatTests = { + 'http://example.com?': { + href: 'http://example.com/?', + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + search: '?', + query: {}, + pathname: '/' + }, + 'http://example.com?foo=bar#frag': { + href: 'http://example.com/?foo=bar#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=bar', + query: 'foo=bar', + pathname: '/' + }, + 'http://example.com?foo=@bar#frag': { + href: 'http://example.com/?foo=@bar#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=@bar', + query: 'foo=@bar', + pathname: '/' + }, + 'http://example.com?foo=/bar/#frag': { + href: 'http://example.com/?foo=/bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=/bar/', + query: 'foo=/bar/', + pathname: '/' + }, + 'http://example.com?foo=?bar/#frag': { + href: 'http://example.com/?foo=?bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=?bar/', + query: 'foo=?bar/', + pathname: '/' + }, + 'http://example.com#frag=?bar/#frag': { + href: 'http://example.com/#frag=?bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag=?bar/#frag', + pathname: '/' + }, + 'http://google.com" onload="alert(42)/': { + href: 'http://google.com/%22%20onload=%22alert(42)/', + protocol: 'http:', + host: 'google.com', + pathname: '/%22%20onload=%22alert(42)/' + }, + 'http://a.com/a/b/c?s#h': { + href: 'http://a.com/a/b/c?s#h', + protocol: 'http', + host: 'a.com', + pathname: 'a/b/c', + hash: 'h', + search: 's' + }, + 'xmpp:isaacschlueter@jabber.org': { + href: 'xmpp:isaacschlueter@jabber.org', + protocol: 'xmpp:', + host: 'jabber.org', + auth: 'isaacschlueter', + hostname: 'jabber.org' + }, + 'http://atpass:foo%40bar@127.0.0.1/': { + href: 'http://atpass:foo%40bar@127.0.0.1/', + auth: 'atpass:foo@bar', + hostname: '127.0.0.1', + protocol: 'http:', + pathname: '/' + }, + 'http://atslash%2F%40:%2F%40@foo/': { + href: 'http://atslash%2F%40:%2F%40@foo/', + auth: 'atslash/@:/@', + hostname: 'foo', + protocol: 'http:', + pathname: '/' + }, + 'svn+ssh://foo/bar': { + href: 'svn+ssh://foo/bar', + hostname: 'foo', + protocol: 'svn+ssh:', + pathname: '/bar', + slashes: true + }, + 'dash-test://foo/bar': { + href: 'dash-test://foo/bar', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + slashes: true + }, + 'dash-test:foo/bar': { + href: 'dash-test:foo/bar', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar' + }, + 'dot.test://foo/bar': { + href: 'dot.test://foo/bar', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + slashes: true + }, + 'dot.test:foo/bar': { + href: 'dot.test:foo/bar', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar' + }, + // IPv6 support + 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': { + href: 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature', + protocol: 'coap:', + auth: 'u:p', + hostname: '::1', + port: '61616', + pathname: '/.well-known/r', + search: 'n=Temperature' + }, + 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': { + href: 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton', + protocol: 'coap', + host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', + pathname: '/s/stopButton' + }, + 'http://[::]/': { + href: 'http://[::]/', + protocol: 'http:', + hostname: '[::]', + pathname: '/' + }, + + // Encode context-specific delimiters in path and query, but do not touch + // other non-delimiter chars like `%`. + // + + // `#`,`?` in path + '/path/to/%%23%3F+=&.txt?foo=theA1#bar': { + href: '/path/to/%%23%3F+=&.txt?foo=theA1#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'theA1' + }, + hash: '#bar' + }, + + // `#`,`?` in path + `#` in query + '/path/to/%%23%3F+=&.txt?foo=the%231#bar': { + href: '/path/to/%%23%3F+=&.txt?foo=the%231#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'the#1' + }, + hash: '#bar' + }, + + // `#` in path end + `#` in query + '/path/to/%%23?foo=the%231#bar': { + href: '/path/to/%%23?foo=the%231#bar', + pathname: '/path/to/%#', + query: { + foo: 'the#1' + }, + hash: '#bar' + }, + + // `?` and `#` in path and search + 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/foo?100%m#r', + }, + + // `?` and `#` in search only + 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/fooA100%mBr', + }, + + // Multiple `#` in search + 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag': { + href: 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag', + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=bar#1#2#3&abc=#4##5', + query: {}, + pathname: '/' + }, + + // More than 255 characters in hostname which exceeds the limit + [`http://${'a'.repeat(255)}.com/node`]: { + href: 'http:///node', + protocol: 'http:', + slashes: true, + host: '', + hostname: '', + pathname: '/node', + path: '/node' + }, + + // Greater than or equal to 63 characters after `.` in hostname + [`http://www.${'z'.repeat(63)}example.com/node`]: { + href: `http://www.${'z'.repeat(63)}example.com/node`, + protocol: 'http:', + slashes: true, + host: `www.${'z'.repeat(63)}example.com`, + hostname: `www.${'z'.repeat(63)}example.com`, + pathname: '/node', + path: '/node' + }, + + // https://github.com/nodejs/node/issues/3361 + 'file:///home/user': { + href: 'file:///home/user', + protocol: 'file', + pathname: '/home/user', + path: '/home/user' + }, + + // surrogate in auth + 'http://%F0%9F%98%80@www.example.com/': { + href: 'http://%F0%9F%98%80@www.example.com/', + protocol: 'http:', + auth: '\uD83D\uDE00', + hostname: 'www.example.com', + pathname: '/' + } +}; +for (const u in formatTests) { + const expect = formatTests[u].href; + delete formatTests[u].href; + const actual = url.format(u); + const actualObj = url.format(formatTests[u]); + assert.strictEqual(actual, expect, + `wonky format(${u}) == ${expect}\nactual:${actual}`); + assert.strictEqual(actualObj, expect, + `wonky format(${JSON.stringify(formatTests[u])}) == ${ + expect}\nactual: ${actualObj}`); +} diff --git a/cli/tests/node_compat/test/parallel/test-url-parse-format.js b/cli/tests/node_compat/test/parallel/test-url-parse-format.js new file mode 100644 index 00000000000000..7079857bd54d73 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-parse-format.js @@ -0,0 +1,1053 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const inspect = require('util').inspect; + +const url = require('url'); + +// URLs to parse, and expected data +// { url : parsed } +const parseTests = { + '//some_path': { + href: '//some_path', + pathname: '//some_path', + path: '//some_path' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + search: '?json=%22%5C%22foo%5C%22%22', + query: 'json=%22%5C%22foo%5C%22%22', + path: '/foo.html?json=%22%5C%22foo%5C%22%22', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch?blarg', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg' + }, + + + 'http:\\\\evil-phisher\\foo.html': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + href: 'http://evil-phisher/foo.html' + }, + + 'HTTP://www.example.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'HTTP://www.example.com': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://www.ExAmPlE.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user:pw@www.ExAmPlE.com/': { + href: 'http://user:pw@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user:pw', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://USER:PW@www.ExAmPlE.com/': { + href: 'http://USER:PW@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'USER:PW', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user@www.example.com/': { + href: 'http://user@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user%3Apw@www.example.com/': { + href: 'http://user:pw@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user:pw', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://x.com/path?that\'s#all, folks': { + href: 'http://x.com/path?that%27s#all,%20folks', + protocol: 'http:', + slashes: true, + host: 'x.com', + hostname: 'x.com', + search: '?that%27s', + query: 'that%27s', + pathname: '/path', + hash: '#all,%20folks', + path: '/path?that%27s' + }, + + 'HTTP://X.COM/Y': { + href: 'http://x.com/Y', + protocol: 'http:', + slashes: true, + host: 'x.com', + hostname: 'x.com', + pathname: '/Y', + path: '/Y' + }, + + // Whitespace in the front + ' http://www.example.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + // + not an invalid host character + // per https://url.spec.whatwg.org/#host-parsing + 'http://x.y.com+a/b/c': { + href: 'http://x.y.com+a/b/c', + protocol: 'http:', + slashes: true, + host: 'x.y.com+a', + hostname: 'x.y.com+a', + pathname: '/b/c', + path: '/b/c' + }, + + // An unexpected invalid char in the hostname. + 'HtTp://x.y.cOm;a/b/c?d=e#f gi': { + href: 'http://x.y.com/;a/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'x.y.com', + hostname: 'x.y.com', + pathname: ';a/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';a/b/c?d=e' + }, + + // Make sure that we don't accidentally lcast the path parts. + 'HtTp://x.y.cOm;A/b/c?d=e#f gi': { + href: 'http://x.y.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'x.y.com', + hostname: 'x.y.com', + pathname: ';A/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';A/b/c?d=e' + }, + + 'http://x...y...#p': { + href: 'http://x...y.../#p', + protocol: 'http:', + slashes: true, + host: 'x...y...', + hostname: 'x...y...', + hash: '#p', + pathname: '/', + path: '/' + }, + + 'http://x/p/"quoted"': { + href: 'http://x/p/%22quoted%22', + protocol: 'http:', + slashes: true, + host: 'x', + hostname: 'x', + pathname: '/p/%22quoted%22', + path: '/p/%22quoted%22' + }, + + ' Is a URL!': { + href: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + pathname: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + path: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!' + }, + + 'http://www.narwhaljs.org/blog/categories?id=news': { + href: 'http://www.narwhaljs.org/blog/categories?id=news', + protocol: 'http:', + slashes: true, + host: 'www.narwhaljs.org', + hostname: 'www.narwhaljs.org', + search: '?id=news', + query: 'id=news', + pathname: '/blog/categories', + path: '/blog/categories?id=news' + }, + + 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + hostname: 'mt0.google.com', + pathname: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + path: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' + + '&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + hostname: 'mt0.google.com', + search: '???&hl=en&src=api&x=2&y=2&z=3&s=', + query: '??&hl=en&src=api&x=2&y=2&z=3&s=', + pathname: '/vt/lyrs=m@114', + path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + auth: 'user:pass', + hostname: 'mt0.google.com', + search: '???&hl=en&src=api&x=2&y=2&z=3&s=', + query: '??&hl=en&src=api&x=2&y=2&z=3&s=', + pathname: '/vt/lyrs=m@114', + path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'file:///etc/passwd': { + href: 'file:///etc/passwd', + slashes: true, + protocol: 'file:', + pathname: '/etc/passwd', + hostname: '', + host: '', + path: '/etc/passwd' + }, + + 'file://localhost/etc/passwd': { + href: 'file://localhost/etc/passwd', + protocol: 'file:', + slashes: true, + pathname: '/etc/passwd', + hostname: 'localhost', + host: 'localhost', + path: '/etc/passwd' + }, + + 'file://foo/etc/passwd': { + href: 'file://foo/etc/passwd', + protocol: 'file:', + slashes: true, + pathname: '/etc/passwd', + hostname: 'foo', + host: 'foo', + path: '/etc/passwd' + }, + + 'file:///etc/node/': { + href: 'file:///etc/node/', + slashes: true, + protocol: 'file:', + pathname: '/etc/node/', + hostname: '', + host: '', + path: '/etc/node/' + }, + + 'file://localhost/etc/node/': { + href: 'file://localhost/etc/node/', + protocol: 'file:', + slashes: true, + pathname: '/etc/node/', + hostname: 'localhost', + host: 'localhost', + path: '/etc/node/' + }, + + 'file://foo/etc/node/': { + href: 'file://foo/etc/node/', + protocol: 'file:', + slashes: true, + pathname: '/etc/node/', + hostname: 'foo', + host: 'foo', + path: '/etc/node/' + }, + + 'http:/baz/../foo/bar': { + href: 'http:/baz/../foo/bar', + protocol: 'http:', + pathname: '/baz/../foo/bar', + path: '/baz/../foo/bar' + }, + + 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag': { + href: 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag', + protocol: 'http:', + slashes: true, + host: 'example.com:8000', + auth: 'user:pass', + port: '8000', + hostname: 'example.com', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + '//user:pass@example.com:8000/foo/bar?baz=quux#frag': { + href: '//user:pass@example.com:8000/foo/bar?baz=quux#frag', + slashes: true, + host: 'example.com:8000', + auth: 'user:pass', + port: '8000', + hostname: 'example.com', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + '/foo/bar?baz=quux#frag': { + href: '/foo/bar?baz=quux#frag', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + 'http:/foo/bar?baz=quux#frag': { + href: 'http:/foo/bar?baz=quux#frag', + protocol: 'http:', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + 'mailto:foo@bar.com?subject=hello': { + href: 'mailto:foo@bar.com?subject=hello', + protocol: 'mailto:', + host: 'bar.com', + auth: 'foo', + hostname: 'bar.com', + search: '?subject=hello', + query: 'subject=hello', + path: '?subject=hello' + }, + + 'javascript:alert(\'hello\');': { + href: 'javascript:alert(\'hello\');', + protocol: 'javascript:', + pathname: 'alert(\'hello\');', + path: 'alert(\'hello\');' + }, + + 'xmpp:isaacschlueter@jabber.org': { + href: 'xmpp:isaacschlueter@jabber.org', + protocol: 'xmpp:', + host: 'jabber.org', + auth: 'isaacschlueter', + hostname: 'jabber.org' + }, + + 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar': { + href: 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', + protocol: 'http:', + slashes: true, + host: '127.0.0.1:8080', + auth: 'atpass:foo@bar', + hostname: '127.0.0.1', + port: '8080', + pathname: '/path', + search: '?search=foo', + query: 'search=foo', + hash: '#bar', + path: '/path?search=foo' + }, + + 'svn+ssh://foo/bar': { + href: 'svn+ssh://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'svn+ssh:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dash-test://foo/bar': { + href: 'dash-test://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dash-test:foo/bar': { + href: 'dash-test:foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + path: '/bar' + }, + + 'dot.test://foo/bar': { + href: 'dot.test://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dot.test:foo/bar': { + href: 'dot.test:foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + path: '/bar' + }, + + // IDNA tests + 'http://www.日本語.com/': { + href: 'http://www.xn--wgv71a119e.com/', + protocol: 'http:', + slashes: true, + host: 'www.xn--wgv71a119e.com', + hostname: 'www.xn--wgv71a119e.com', + pathname: '/', + path: '/' + }, + + 'http://example.Bücher.com/': { + href: 'http://example.xn--bcher-kva.com/', + protocol: 'http:', + slashes: true, + host: 'example.xn--bcher-kva.com', + hostname: 'example.xn--bcher-kva.com', + pathname: '/', + path: '/' + }, + + 'http://www.Äffchen.com/': { + href: 'http://www.xn--ffchen-9ta.com/', + protocol: 'http:', + slashes: true, + host: 'www.xn--ffchen-9ta.com', + hostname: 'www.xn--ffchen-9ta.com', + pathname: '/', + path: '/' + }, + + 'http://www.Äffchen.cOm;A/b/c?d=e#f gi': { + href: 'http://www.xn--ffchen-9ta.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'www.xn--ffchen-9ta.com', + hostname: 'www.xn--ffchen-9ta.com', + pathname: ';A/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';A/b/c?d=e' + }, + + 'http://SÉLIER.COM/': { + href: 'http://xn--slier-bsa.com/', + protocol: 'http:', + slashes: true, + host: 'xn--slier-bsa.com', + hostname: 'xn--slier-bsa.com', + pathname: '/', + path: '/' + }, + + 'http://ليهمابتكلموشعربي؟.ي؟/': { + href: 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/', + protocol: 'http:', + slashes: true, + host: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + hostname: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + pathname: '/', + path: '/' + }, + + 'http://➡.ws/➡': { + href: 'http://xn--hgi.ws/➡', + protocol: 'http:', + slashes: true, + host: 'xn--hgi.ws', + hostname: 'xn--hgi.ws', + pathname: '/➡', + path: '/➡' + }, + + 'http://bucket_name.s3.amazonaws.com/image.jpg': { + protocol: 'http:', + slashes: true, + host: 'bucket_name.s3.amazonaws.com', + hostname: 'bucket_name.s3.amazonaws.com', + pathname: '/image.jpg', + href: 'http://bucket_name.s3.amazonaws.com/image.jpg', + path: '/image.jpg' + }, + + 'git+http://github.com/joyent/node.git': { + protocol: 'git+http:', + slashes: true, + host: 'github.com', + hostname: 'github.com', + pathname: '/joyent/node.git', + path: '/joyent/node.git', + href: 'git+http://github.com/joyent/node.git' + }, + + // If local1@domain1 is uses as a relative URL it may + // be parse into auth@hostname, but here there is no + // way to make it work in url.parse, I add the test to be explicit + 'local1@domain1': { + pathname: 'local1@domain1', + path: 'local1@domain1', + href: 'local1@domain1' + }, + + // While this may seem counter-intuitive, a browser will parse + // as a path. + 'www.example.com': { + href: 'www.example.com', + pathname: 'www.example.com', + path: 'www.example.com' + }, + + // ipv6 support + '[fe80::1]': { + href: '[fe80::1]', + pathname: '[fe80::1]', + path: '[fe80::1]' + }, + + 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': { + protocol: 'coap:', + slashes: true, + host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]', + hostname: 'fedc:ba98:7654:3210:fedc:ba98:7654:3210', + href: 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/', + pathname: '/', + path: '/' + }, + + 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': { + protocol: 'coap:', + slashes: true, + host: '[1080:0:0:0:8:800:200c:417a]:61616', + port: '61616', + hostname: '1080:0:0:0:8:800:200c:417a', + href: 'coap://[1080:0:0:0:8:800:200c:417a]:61616/', + pathname: '/', + path: '/' + }, + + 'http://user:password@[3ffe:2a00:100:7031::1]:8080': { + protocol: 'http:', + slashes: true, + auth: 'user:password', + host: '[3ffe:2a00:100:7031::1]:8080', + port: '8080', + hostname: '3ffe:2a00:100:7031::1', + href: 'http://user:password@[3ffe:2a00:100:7031::1]:8080/', + pathname: '/', + path: '/' + }, + + 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': { + protocol: 'coap:', + slashes: true, + auth: 'u:p', + host: '[::192.9.5.5]:61616', + port: '61616', + hostname: '::192.9.5.5', + href: 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature', + search: '?n=Temperature', + query: 'n=Temperature', + pathname: '/.well-known/r', + path: '/.well-known/r?n=Temperature' + }, + + // empty port + 'http://example.com:': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/', + pathname: '/', + path: '/' + }, + + 'http://example.com:/a/b.html': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/a/b.html', + pathname: '/a/b.html', + path: '/a/b.html' + }, + + 'http://example.com:?a=b': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/?a=b', + search: '?a=b', + query: 'a=b', + pathname: '/', + path: '/?a=b' + }, + + 'http://example.com:#abc': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/#abc', + hash: '#abc', + pathname: '/', + path: '/' + }, + + 'http://[fe80::1]:/a/b?a=b#abc': { + protocol: 'http:', + slashes: true, + host: '[fe80::1]', + hostname: 'fe80::1', + href: 'http://[fe80::1]/a/b?a=b#abc', + search: '?a=b', + query: 'a=b', + hash: '#abc', + pathname: '/a/b', + path: '/a/b?a=b' + }, + + 'http://-lovemonsterz.tumblr.com/rss': { + protocol: 'http:', + slashes: true, + host: '-lovemonsterz.tumblr.com', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://-lovemonsterz.tumblr.com/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://-lovemonsterz.tumblr.com:80/rss': { + protocol: 'http:', + slashes: true, + port: '80', + host: '-lovemonsterz.tumblr.com:80', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://-lovemonsterz.tumblr.com:80/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com/rss': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + host: '-lovemonsterz.tumblr.com', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://user:pass@-lovemonsterz.tumblr.com/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com:80/rss': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + port: '80', + host: '-lovemonsterz.tumblr.com:80', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://user:pass@-lovemonsterz.tumblr.com:80/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://_jabber._tcp.google.com/test': { + protocol: 'http:', + slashes: true, + host: '_jabber._tcp.google.com', + hostname: '_jabber._tcp.google.com', + href: 'http://_jabber._tcp.google.com/test', + pathname: '/test', + path: '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com/test': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + host: '_jabber._tcp.google.com', + hostname: '_jabber._tcp.google.com', + href: 'http://user:pass@_jabber._tcp.google.com/test', + pathname: '/test', + path: '/test', + }, + + 'http://_jabber._tcp.google.com:80/test': { + protocol: 'http:', + slashes: true, + port: '80', + host: '_jabber._tcp.google.com:80', + hostname: '_jabber._tcp.google.com', + href: 'http://_jabber._tcp.google.com:80/test', + pathname: '/test', + path: '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com:80/test': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + port: '80', + host: '_jabber._tcp.google.com:80', + hostname: '_jabber._tcp.google.com', + href: 'http://user:pass@_jabber._tcp.google.com:80/test', + pathname: '/test', + path: '/test', + }, + + 'http://x:1/\' <>"`/{}|\\^~`/': { + protocol: 'http:', + slashes: true, + host: 'x:1', + port: '1', + hostname: 'x', + pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/' + }, + + 'http://a@b@c/': { + protocol: 'http:', + slashes: true, + auth: 'a@b', + host: 'c', + hostname: 'c', + href: 'http://a%40b@c/', + path: '/', + pathname: '/' + }, + + 'http://a@b?@c': { + protocol: 'http:', + slashes: true, + auth: 'a', + host: 'b', + hostname: 'b', + href: 'http://a@b/?@c', + path: '/?@c', + pathname: '/', + search: '?@c', + query: '@c' + }, + + 'http://a.b/\tbc\ndr\ref g"hq\'j?mn\\op^q=r`99{st|uv}wz': { + protocol: 'http:', + slashes: true, + host: 'a.b', + port: null, + hostname: 'a.b', + hash: null, + pathname: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E', + path: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + search: '?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + query: 'mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + href: 'http://a.b/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz' + }, + + 'http://a\r" \t\n<\'b:b@c\r\nd/e?f': { + protocol: 'http:', + slashes: true, + auth: 'a\r" \t\n<\'b:b', + host: 'c', + port: null, + hostname: 'c', + hash: null, + search: '?f', + query: 'f', + pathname: '%0D%0Ad/e', + path: '%0D%0Ad/e?f', + href: 'http://a%0D%22%20%09%0A%3C\'b:b@c/%0D%0Ad/e?f' + }, + + // Git urls used by npm + 'git+ssh://git@github.com:npm/npm': { + protocol: 'git+ssh:', + slashes: true, + auth: 'git', + host: 'github.com', + port: null, + hostname: 'github.com', + hash: null, + search: null, + query: null, + pathname: '/:npm/npm', + path: '/:npm/npm', + href: 'git+ssh://git@github.com/:npm/npm' + }, + + 'https://*': { + protocol: 'https:', + slashes: true, + auth: null, + host: '', + port: null, + hostname: '', + hash: null, + search: null, + query: null, + pathname: '/*', + path: '/*', + href: 'https:///*' + }, + + // The following two URLs are the same, but they differ for a capital A. + // Verify that the protocol is checked in a case-insensitive manner. + 'javascript:alert(1);a=\x27@white-listed.com\x27': { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }, + + 'javAscript:alert(1);a=\x27@white-listed.com\x27': { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }, + + 'ws://www.example.com': { + protocol: 'ws:', + slashes: true, + hostname: 'www.example.com', + host: 'www.example.com', + pathname: '/', + path: '/', + href: 'ws://www.example.com/' + }, + + 'wss://www.example.com': { + protocol: 'wss:', + slashes: true, + hostname: 'www.example.com', + host: 'www.example.com', + pathname: '/', + path: '/', + href: 'wss://www.example.com/' + }, + + '//fhqwhgads@example.com/everybody-to-the-limit': { + protocol: null, + slashes: true, + auth: 'fhqwhgads', + host: 'example.com', + port: null, + hostname: 'example.com', + hash: null, + search: null, + query: null, + pathname: '/everybody-to-the-limit', + path: '/everybody-to-the-limit', + href: '//fhqwhgads@example.com/everybody-to-the-limit' + }, + + '//fhqwhgads@example.com/everybody#to-the-limit': { + protocol: null, + slashes: true, + auth: 'fhqwhgads', + host: 'example.com', + port: null, + hostname: 'example.com', + hash: '#to-the-limit', + search: null, + query: null, + pathname: '/everybody', + path: '/everybody', + href: '//fhqwhgads@example.com/everybody#to-the-limit' + }, + + '\bhttp://example.com/\b': { + protocol: 'http:', + slashes: true, + auth: null, + host: 'example.com', + port: null, + hostname: 'example.com', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'http://example.com/' + } +}; + +for (const u in parseTests) { + let actual = url.parse(u); + const spaced = url.parse(` \t ${u}\n\t`); + let expected = Object.assign(new url.Url(), parseTests[u]); + + Object.keys(actual).forEach(function(i) { + if (expected[i] === undefined && actual[i] === null) { + expected[i] = null; + } + }); + + assert.deepStrictEqual( + actual, + expected, + `expected ${inspect(expected)}, got ${inspect(actual)}` + ); + assert.deepStrictEqual( + spaced, + expected, + `expected ${inspect(expected)}, got ${inspect(spaced)}` + ); + + expected = parseTests[u].href; + actual = url.format(parseTests[u]); + + assert.strictEqual(actual, expected, + `format(${u}) == ${u}\nactual:${actual}`); +} + +{ + const parsed = url.parse('http://nodejs.org/') + .resolveObject('jAvascript:alert(1);a=\x27@white-listed.com\x27'); + + const expected = Object.assign(new url.Url(), { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }); + + assert.deepStrictEqual(parsed, expected); +} diff --git a/cli/tests/node_compat/test/parallel/test-url-parse-invalid-input.js b/cli/tests/node_compat/test/parallel/test-url-parse-invalid-input.js new file mode 100644 index 00000000000000..b4c75ba4994992 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-parse-invalid-input.js @@ -0,0 +1,83 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const url = require('url'); + +// https://github.com/joyent/node/issues/568 +[ + [undefined, 'undefined'], + [null, 'object'], + [true, 'boolean'], + [false, 'boolean'], + [0.0, 'number'], + [0, 'number'], + [[], 'object'], + [{}, 'object'], + [() => {}, 'function'], + [Symbol('foo'), 'symbol'], +].forEach(([val, type]) => { + assert.throws(() => { + url.parse(val); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "url" argument must be of type string.' + + common.invalidArgTypeHelper(val) + }); +}); + +assert.throws(() => { url.parse('http://%E0%A4%A@fail'); }, + (e) => { + // The error should be a URIError. + if (!(e instanceof URIError)) + return false; + + // The error should be from the JS engine and not from Node.js. + // JS engine errors do not have the `code` property. + return e.code === undefined; + }); + +assert.throws(() => { url.parse('http://[127.0.0.1\x00c8763]:8000/'); }, + { code: 'ERR_INVALID_URL', input: 'http://[127.0.0.1\x00c8763]:8000/' } +); + +if (common.hasIntl) { + // An array of Unicode code points whose Unicode NFKD contains a "bad + // character". + const badIDNA = (() => { + const BAD_CHARS = '#%/:?@[\\]^|'; + const out = []; + for (let i = 0x80; i < 0x110000; i++) { + const cp = String.fromCodePoint(i); + for (const badChar of BAD_CHARS) { + if (cp.normalize('NFKD').includes(badChar)) { + out.push(cp); + } + } + } + return out; + })(); + + // The generation logic above should at a minimum produce these two + // characters. + assert(badIDNA.includes('℀')); + assert(badIDNA.includes('@')); + + for (const badCodePoint of badIDNA) { + const badURL = `http://fail${badCodePoint}fail.com/`; + assert.throws(() => { url.parse(badURL); }, + (e) => e.code === 'ERR_INVALID_URL', + `parsing ${badURL}`); + } + + assert.throws(() => { url.parse('http://\u00AD/bad.com/'); }, + (e) => e.code === 'ERR_INVALID_URL', + 'parsing http://\u00AD/bad.com/'); +} diff --git a/cli/tests/node_compat/test/parallel/test-url-parse-query.js b/cli/tests/node_compat/test/parallel/test-url-parse-query.js new file mode 100644 index 00000000000000..5fdbf2c053f1af --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-parse-query.js @@ -0,0 +1,97 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const url = require('url'); + +function createWithNoPrototype(properties = []) { + const noProto = Object.create(null); + properties.forEach((property) => { + noProto[property.key] = property.value; + }); + return noProto; +} + +function check(actual, expected) { + assert.notStrictEqual(Object.getPrototypeOf(actual), Object.prototype); + assert.deepStrictEqual(Object.keys(actual).sort(), + Object.keys(expected).sort()); + Object.keys(expected).forEach(function(key) { + assert.deepStrictEqual(actual[key], expected[key]); + }); +} + +const parseTestsWithQueryString = { + '/foo/bar?baz=quux#frag': { + href: '/foo/bar?baz=quux#frag', + hash: '#frag', + search: '?baz=quux', + query: createWithNoPrototype([{ key: 'baz', value: 'quux' }]), + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + 'http://example.com': { + href: 'http://example.com/', + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + query: createWithNoPrototype(), + search: null, + pathname: '/', + path: '/' + }, + '/example': { + protocol: null, + slashes: null, + auth: undefined, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: createWithNoPrototype(), + pathname: '/example', + path: '/example', + href: '/example' + }, + '/example?query=value': { + protocol: null, + slashes: null, + auth: undefined, + host: null, + port: null, + hostname: null, + hash: null, + search: '?query=value', + query: createWithNoPrototype([{ key: 'query', value: 'value' }]), + pathname: '/example', + path: '/example?query=value', + href: '/example?query=value' + } +}; +for (const u in parseTestsWithQueryString) { + const actual = url.parse(u, true); + const expected = Object.assign(new url.Url(), parseTestsWithQueryString[u]); + for (const i in actual) { + if (actual[i] === null && expected[i] === undefined) { + expected[i] = null; + } + } + + const properties = Object.keys(actual).sort(); + assert.deepStrictEqual(properties, Object.keys(expected).sort()); + properties.forEach((property) => { + if (property === 'query') { + check(actual[property], expected[property]); + } else { + assert.deepStrictEqual(actual[property], expected[property]); + } + }); +} diff --git a/cli/tests/node_compat/test/parallel/test-url-pathtofileurl.js b/cli/tests/node_compat/test/parallel/test-url-pathtofileurl.js new file mode 100644 index 00000000000000..f13ca7307d2ca0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-pathtofileurl.js @@ -0,0 +1,153 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); +const url = require('url'); + +{ + const fileURL = url.pathToFileURL('test/').href; + assert.ok(fileURL.startsWith('file:///')); + assert.ok(fileURL.endsWith('/')); +} + +{ + const fileURL = url.pathToFileURL('test\\').href; + assert.ok(fileURL.startsWith('file:///')); + if (isWindows) + assert.ok(fileURL.endsWith('/')); + else + assert.ok(fileURL.endsWith('%5C')); +} + +{ + const fileURL = url.pathToFileURL('test/%').href; + assert.ok(fileURL.includes('%25')); +} + +{ + if (isWindows) { + // UNC path: \\server\share\resource + + // Missing server: + assert.throws(() => url.pathToFileURL('\\\\\\no-server'), { + code: 'ERR_INVALID_ARG_VALUE' + }); + + // Missing share or resource: + assert.throws(() => url.pathToFileURL('\\\\host'), { + code: 'ERR_INVALID_ARG_VALUE' + }); + } else { + // UNC paths on posix are considered a single path that has backslashes: + const fileURL = url.pathToFileURL('\\\\nas\\share\\path.txt').href; + assert.match(fileURL, /file:\/\/.+%5C%5Cnas%5Cshare%5Cpath\.txt$/); + } +} + +{ + let testCases; + if (isWindows) { + testCases = [ + // Lowercase ascii alpha + { path: 'C:\\foo', expected: 'file:///C:/foo' }, + // Uppercase ascii alpha + { path: 'C:\\FOO', expected: 'file:///C:/FOO' }, + // dir + { path: 'C:\\dir\\foo', expected: 'file:///C:/dir/foo' }, + // trailing separator + { path: 'C:\\dir\\', expected: 'file:///C:/dir/' }, + // dot + { path: 'C:\\foo.mjs', expected: 'file:///C:/foo.mjs' }, + // space + { path: 'C:\\foo bar', expected: 'file:///C:/foo%20bar' }, + // question mark + { path: 'C:\\foo?bar', expected: 'file:///C:/foo%3Fbar' }, + // number sign + { path: 'C:\\foo#bar', expected: 'file:///C:/foo%23bar' }, + // ampersand + { path: 'C:\\foo&bar', expected: 'file:///C:/foo&bar' }, + // equals + { path: 'C:\\foo=bar', expected: 'file:///C:/foo=bar' }, + // colon + { path: 'C:\\foo:bar', expected: 'file:///C:/foo:bar' }, + // semicolon + { path: 'C:\\foo;bar', expected: 'file:///C:/foo;bar' }, + // percent + { path: 'C:\\foo%bar', expected: 'file:///C:/foo%25bar' }, + // backslash + { path: 'C:\\foo\\bar', expected: 'file:///C:/foo/bar' }, + // backspace + { path: 'C:\\foo\bbar', expected: 'file:///C:/foo%08bar' }, + // tab + { path: 'C:\\foo\tbar', expected: 'file:///C:/foo%09bar' }, + // newline + { path: 'C:\\foo\nbar', expected: 'file:///C:/foo%0Abar' }, + // carriage return + { path: 'C:\\foo\rbar', expected: 'file:///C:/foo%0Dbar' }, + // latin1 + { path: 'C:\\fóóbàr', expected: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' }, + // Euro sign (BMP code point) + { path: 'C:\\€', expected: 'file:///C:/%E2%82%AC' }, + // Rocket emoji (non-BMP code point) + { path: 'C:\\🚀', expected: 'file:///C:/%F0%9F%9A%80' }, + // UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows) + { path: '\\\\nas\\My Docs\\File.doc', expected: 'file://nas/My%20Docs/File.doc' }, + ]; + } else { + testCases = [ + // Lowercase ascii alpha + { path: '/foo', expected: 'file:///foo' }, + // Uppercase ascii alpha + { path: '/FOO', expected: 'file:///FOO' }, + // dir + { path: '/dir/foo', expected: 'file:///dir/foo' }, + // trailing separator + { path: '/dir/', expected: 'file:///dir/' }, + // dot + { path: '/foo.mjs', expected: 'file:///foo.mjs' }, + // space + { path: '/foo bar', expected: 'file:///foo%20bar' }, + // question mark + { path: '/foo?bar', expected: 'file:///foo%3Fbar' }, + // number sign + { path: '/foo#bar', expected: 'file:///foo%23bar' }, + // ampersand + { path: '/foo&bar', expected: 'file:///foo&bar' }, + // equals + { path: '/foo=bar', expected: 'file:///foo=bar' }, + // colon + { path: '/foo:bar', expected: 'file:///foo:bar' }, + // semicolon + { path: '/foo;bar', expected: 'file:///foo;bar' }, + // percent + { path: '/foo%bar', expected: 'file:///foo%25bar' }, + // backslash + { path: '/foo\\bar', expected: 'file:///foo%5Cbar' }, + // backspace + { path: '/foo\bbar', expected: 'file:///foo%08bar' }, + // tab + { path: '/foo\tbar', expected: 'file:///foo%09bar' }, + // newline + { path: '/foo\nbar', expected: 'file:///foo%0Abar' }, + // carriage return + { path: '/foo\rbar', expected: 'file:///foo%0Dbar' }, + // latin1 + { path: '/fóóbàr', expected: 'file:///f%C3%B3%C3%B3b%C3%A0r' }, + // Euro sign (BMP code point) + { path: '/€', expected: 'file:///%E2%82%AC' }, + // Rocket emoji (non-BMP code point) + { path: '/🚀', expected: 'file:///%F0%9F%9A%80' }, + ]; + } + + for (const { path, expected } of testCases) { + const actual = url.pathToFileURL(path).href; + assert.strictEqual(actual, expected); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-url-relative.js b/cli/tests/node_compat/test/parallel/test-url-relative.js new file mode 100644 index 00000000000000..0436f0821ac604 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-relative.js @@ -0,0 +1,441 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const inspect = require('util').inspect; +const url = require('url'); + +// When source is false +assert.strictEqual(url.resolveObject('', 'foo'), 'foo'); + +// [from, path, expected] +const relativeTests = [ + ['/foo/bar/baz', 'quux', '/foo/bar/quux'], + ['/foo/bar/baz', 'quux/asdf', '/foo/bar/quux/asdf'], + ['/foo/bar/baz', 'quux/baz', '/foo/bar/quux/baz'], + ['/foo/bar/baz', '../quux/baz', '/foo/quux/baz'], + ['/foo/bar/baz', '/bar', '/bar'], + ['/foo/bar/baz/', 'quux', '/foo/bar/baz/quux'], + ['/foo/bar/baz/', 'quux/baz', '/foo/bar/baz/quux/baz'], + ['/foo/bar/baz', '../../../../../../../../quux/baz', '/quux/baz'], + ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'], + ['/foo', '.', '/'], + ['/foo', '..', '/'], + ['/foo/', '.', '/foo/'], + ['/foo/', '..', '/'], + ['/foo/bar', '.', '/foo/'], + ['/foo/bar', '..', '/'], + ['/foo/bar/', '.', '/foo/bar/'], + ['/foo/bar/', '..', '/foo/'], + ['foo/bar', '../../../baz', '../../baz'], + ['foo/bar/', '../../../baz', '../baz'], + ['http://example.com/b//c//d;p?q#blarg', 'https:#hash2', 'https:///#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https:/p/a/t/h?s#hash2', + 'https://p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https://u:p@h.com/p/a/t/h?s#hash2', + 'https://u:p@h.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https:/a/b/c/d', + 'https://a/b/c/d'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:#hash2', + 'http://example.com/b//c//d;p?q#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:/p/a/t/h?s#hash2', + 'http://example.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http://u:p@h.com/p/a/t/h?s#hash2', + 'http://u:p@h.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:/a/b/c/d', + 'http://example.com/a/b/c/d'], + ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd'], + ['http://localhost', 'file:///Users/foo', 'file:///Users/foo'], + ['http://localhost', 'file://foo/Users', 'file://foo/Users'], + ['https://registry.npmjs.org', '@foo/bar', 'https://registry.npmjs.org/@foo/bar'], +]; +relativeTests.forEach(function(relativeTest) { + const a = url.resolve(relativeTest[0], relativeTest[1]); + const e = relativeTest[2]; + assert.strictEqual(a, e, + `resolve(${relativeTest[0]}, ${relativeTest[1]})` + + ` == ${e}\n actual=${a}`); +}); + +// +// Tests below taken from Chiron +// http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js +// +// Copyright (c) 2002-2008 Kris Kowal +// used with permission under MIT License +// +// Changes marked with @isaacs + +const bases = [ + 'http://a/b/c/d;p?q', + 'http://a/b/c/d;p?q=1/2', + 'http://a/b/c/d;p=1/2?q', + 'fred:///s//a/b/c', + 'http:///s//a/b/c', +]; + +// [to, from, result] +const relativeTests2 = [ + // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html + ['../c', 'foo:a/b', 'foo:c'], + ['foo:.', 'foo:a', 'foo:'], + ['/foo/../../../bar', 'zz:abc', 'zz:/bar'], + ['/foo/../bar', 'zz:abc', 'zz:/bar'], + // @isaacs Disagree. Not how web browsers resolve this. + ['foo/../../../bar', 'zz:abc', 'zz:bar'], + // ['foo/../../../bar', 'zz:abc', 'zz:../../bar'], // @isaacs Added + ['foo/../bar', 'zz:abc', 'zz:bar'], + ['zz:.', 'zz:abc', 'zz:'], + ['/.', bases[0], 'http://a/'], + ['/.foo', bases[0], 'http://a/.foo'], + ['.foo', bases[0], 'http://a/b/c/.foo'], + + // http://gbiv.com/protocols/uri/test/rel_examples1.html + // examples from RFC 2396 + ['g:h', bases[0], 'g:h'], + ['g', bases[0], 'http://a/b/c/g'], + ['./g', bases[0], 'http://a/b/c/g'], + ['g/', bases[0], 'http://a/b/c/g/'], + ['/g', bases[0], 'http://a/g'], + ['//g', bases[0], 'http://g/'], + // Changed with RFC 2396bis + // ('?y', bases[0], 'http://a/b/c/d;p?y'], + ['?y', bases[0], 'http://a/b/c/d;p?y'], + ['g?y', bases[0], 'http://a/b/c/g?y'], + // Changed with RFC 2396bis + // ('#s', bases[0], CURRENT_DOC_URI + '#s'], + ['#s', bases[0], 'http://a/b/c/d;p?q#s'], + ['g#s', bases[0], 'http://a/b/c/g#s'], + ['g?y#s', bases[0], 'http://a/b/c/g?y#s'], + [';x', bases[0], 'http://a/b/c/;x'], + ['g;x', bases[0], 'http://a/b/c/g;x'], + ['g;x?y#s', bases[0], 'http://a/b/c/g;x?y#s'], + // Changed with RFC 2396bis + // ('', bases[0], CURRENT_DOC_URI], + ['', bases[0], 'http://a/b/c/d;p?q'], + ['.', bases[0], 'http://a/b/c/'], + ['./', bases[0], 'http://a/b/c/'], + ['..', bases[0], 'http://a/b/'], + ['../', bases[0], 'http://a/b/'], + ['../g', bases[0], 'http://a/b/g'], + ['../..', bases[0], 'http://a/'], + ['../../', bases[0], 'http://a/'], + ['../../g', bases[0], 'http://a/g'], + ['../../../g', bases[0], ('http://a/../g', 'http://a/g')], + ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')], + // Changed with RFC 2396bis + // ('/./g', bases[0], 'http://a/./g'], + ['/./g', bases[0], 'http://a/g'], + // Changed with RFC 2396bis + // ('/../g', bases[0], 'http://a/../g'], + ['/../g', bases[0], 'http://a/g'], + ['g.', bases[0], 'http://a/b/c/g.'], + ['.g', bases[0], 'http://a/b/c/.g'], + ['g..', bases[0], 'http://a/b/c/g..'], + ['..g', bases[0], 'http://a/b/c/..g'], + ['./../g', bases[0], 'http://a/b/g'], + ['./g/.', bases[0], 'http://a/b/c/g/'], + ['g/./h', bases[0], 'http://a/b/c/g/h'], + ['g/../h', bases[0], 'http://a/b/c/h'], + ['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'], + ['g;x=1/../y', bases[0], 'http://a/b/c/y'], + ['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'], + ['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'], + ['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'], + ['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'], + ['http:g', bases[0], ('http:g', 'http://a/b/c/g')], + ['http:', bases[0], ('http:', bases[0])], + // Not sure where this one originated + ['/a/b/c/./../../g', bases[0], 'http://a/a/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples2.html + // slashes in base URI's query args + ['g', bases[1], 'http://a/b/c/g'], + ['./g', bases[1], 'http://a/b/c/g'], + ['g/', bases[1], 'http://a/b/c/g/'], + ['/g', bases[1], 'http://a/g'], + ['//g', bases[1], 'http://g/'], + // Changed in RFC 2396bis + // ('?y', bases[1], 'http://a/b/c/?y'], + ['?y', bases[1], 'http://a/b/c/d;p?y'], + ['g?y', bases[1], 'http://a/b/c/g?y'], + ['g?y/./x', bases[1], 'http://a/b/c/g?y/./x'], + ['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'], + ['g#s', bases[1], 'http://a/b/c/g#s'], + ['g#s/./x', bases[1], 'http://a/b/c/g#s/./x'], + ['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'], + ['./', bases[1], 'http://a/b/c/'], + ['../', bases[1], 'http://a/b/'], + ['../g', bases[1], 'http://a/b/g'], + ['../../', bases[1], 'http://a/'], + ['../../g', bases[1], 'http://a/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples3.html + // slashes in path params + // all of these changed in RFC 2396bis + ['g', bases[2], 'http://a/b/c/d;p=1/g'], + ['./g', bases[2], 'http://a/b/c/d;p=1/g'], + ['g/', bases[2], 'http://a/b/c/d;p=1/g/'], + ['g?y', bases[2], 'http://a/b/c/d;p=1/g?y'], + [';x', bases[2], 'http://a/b/c/d;p=1/;x'], + ['g;x', bases[2], 'http://a/b/c/d;p=1/g;x'], + ['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'], + ['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'], + ['./', bases[2], 'http://a/b/c/d;p=1/'], + ['../', bases[2], 'http://a/b/c/'], + ['../g', bases[2], 'http://a/b/c/g'], + ['../../', bases[2], 'http://a/b/'], + ['../../g', bases[2], 'http://a/b/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples4.html + // double and triple slash, unknown scheme + ['g:h', bases[3], 'g:h'], + ['g', bases[3], 'fred:///s//a/b/g'], + ['./g', bases[3], 'fred:///s//a/b/g'], + ['g/', bases[3], 'fred:///s//a/b/g/'], + ['/g', bases[3], 'fred:///g'], // May change to fred:///s//a/g + ['//g', bases[3], 'fred://g'], // May change to fred:///s//g + ['//g/x', bases[3], 'fred://g/x'], // May change to fred:///s//g/x + ['///g', bases[3], 'fred:///g'], + ['./', bases[3], 'fred:///s//a/b/'], + ['../', bases[3], 'fred:///s//a/'], + ['../g', bases[3], 'fred:///s//a/g'], + + ['../../', bases[3], 'fred:///s//'], + ['../../g', bases[3], 'fred:///s//g'], + ['../../../g', bases[3], 'fred:///s/g'], + // May change to fred:///s//a/../../../g + ['../../../../g', bases[3], 'fred:///g'], + + // http://gbiv.com/protocols/uri/test/rel_examples5.html + // double and triple slash, well-known scheme + ['g:h', bases[4], 'g:h'], + ['g', bases[4], 'http:///s//a/b/g'], + ['./g', bases[4], 'http:///s//a/b/g'], + ['g/', bases[4], 'http:///s//a/b/g/'], + ['/g', bases[4], 'http:///g'], // May change to http:///s//a/g + ['//g', bases[4], 'http://g/'], // May change to http:///s//g + ['//g/x', bases[4], 'http://g/x'], // May change to http:///s//g/x + ['///g', bases[4], 'http:///g'], + ['./', bases[4], 'http:///s//a/b/'], + ['../', bases[4], 'http:///s//a/'], + ['../g', bases[4], 'http:///s//a/g'], + ['../../', bases[4], 'http:///s//'], + ['../../g', bases[4], 'http:///s//g'], + // May change to http:///s//a/../../g + ['../../../g', bases[4], 'http:///s/g'], + // May change to http:///s//a/../../../g + ['../../../../g', bases[4], 'http:///g'], + + // From Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py + ['bar:abc', 'foo:xyz', 'bar:abc'], + ['../abc', 'http://example/x/y/z', 'http://example/x/abc'], + ['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'], + ['../r', 'http://ex/x/y/z', 'http://ex/x/r'], + ['q/r', 'http://ex/x/y', 'http://ex/x/q/r'], + ['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'], + ['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'], + ['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'], + ['', 'http://ex/x/y', 'http://ex/x/y'], + ['', 'http://ex/x/y/', 'http://ex/x/y/'], + ['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'], + ['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'], + ['#Animal', + 'file:/swap/test/animal.rdf', + 'file:/swap/test/animal.rdf#Animal'], + ['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'], + ['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'], + ['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'], + ['/r', 'file:/ex/x/y/z', 'file:/r'], + ['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'], + ['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'], + ['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'], + ['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'], + ['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'], + ['', 'file:/ex/x/y', 'file:/ex/x/y'], + ['', 'file:/ex/x/y/', 'file:/ex/x/y/'], + ['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'], + ['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'], + ['file://meetings.example.com/cal#m1', + 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3', + 'file://meetings.example.com/cal#m1'], + ['file://meetings.example.com/cal#m1', + 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3', + 'file://meetings.example.com/cal#m1'], + ['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'], + ['./#', 'file:/some/dir/foo', 'file:/some/dir/#'], + // Ryan Lee + ['./', 'http://example/x/abc.efg', 'http://example/x/'], + + + // Graham Klyne's tests + // http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls + // 01-31 are from Connelly's cases + + // 32-49 + ['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'], + ['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'], + ['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'], + ['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'], + ['local/qual@domain.org#frag', + 'mailto:local', + 'mailto:local/qual@domain.org#frag'], + ['more/qual2@domain2.org#frag', + 'mailto:local/qual1@domain1.org', + 'mailto:local/more/qual2@domain2.org#frag'], + ['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'], + ['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'], + ['c/d', 'foo:a/b', 'foo:a/c/d'], + ['/c/d', 'foo:a/b', 'foo:/c/d'], + ['', 'foo:a/b?c#d', 'foo:a/b?c'], + ['b/c', 'foo:a', 'foo:b/c'], + ['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'], + ['./b/c', 'foo:a', 'foo:b/c'], + ['/./b/c', 'foo:a', 'foo:/b/c'], + ['../../d', 'foo://a//b/c', 'foo://a/d'], + ['.', 'foo:a', 'foo:'], + ['..', 'foo:a', 'foo:'], + + // 50-57[cf. TimBL comments -- + // http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html, + // http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html) + ['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'], + ['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'], + ['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'], + ['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'], + ['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'], + ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], + ['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'], + ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], + + // 70-77 + ['local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'], + ['local2@domain2?query2', + 'mailto:local1@domain1', + 'mailto:local2@domain2?query2'], + ['local2@domain2?query2', + 'mailto:local1@domain1?query1', + 'mailto:local2@domain2?query2'], + ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], + ['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'], + ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], + ['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'], + ['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'], + + // 82-88 + // @isaacs Disagree. Not how browsers do it. + // ['http:this', 'http://example.org/base/uri', 'http:this'], + // @isaacs Added + ['http:this', 'http://example.org/base/uri', 'http://example.org/base/this'], + ['http:this', 'http:base', 'http:this'], + ['.//g', 'f:/a', 'f://g'], + ['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'], + ['m2@example.ord/c2@example.org', + 'mid:m@example.ord/c@example.org', + 'mid:m@example.ord/m2@example.ord/c2@example.org'], + ['mini1.xml', + 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/', + 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'], + ['../b/c', 'foo:a/y/z', 'foo:a/b/c'], + + // changeing auth + ['http://diff:auth@www.example.com', + 'http://asdf:qwer@www.example.com', + 'http://diff:auth@www.example.com/'], + + // changing port + ['https://example.com:81/', + 'https://example.com:82/', + 'https://example.com:81/'], + + // https://github.com/nodejs/node/issues/1435 + ['https://another.host.com/', + 'https://user:password@example.org/', + 'https://another.host.com/'], + ['//another.host.com/', + 'https://user:password@example.org/', + 'https://another.host.com/'], + ['http://another.host.com/', + 'https://user:password@example.org/', + 'http://another.host.com/'], + ['mailto:another.host.com', + 'mailto:user@example.org', + 'mailto:another.host.com'], + ['https://example.com/foo', + 'https://user:password@example.com', + 'https://user:password@example.com/foo'], + + // No path at all + ['#hash1', '#hash2', '#hash1'], +]; +relativeTests2.forEach(function(relativeTest) { + const a = url.resolve(relativeTest[1], relativeTest[0]); + const e = url.format(relativeTest[2]); + assert.strictEqual(a, e, + `resolve(${relativeTest[0]}, ${relativeTest[1]})` + + ` == ${e}\n actual=${a}`); +}); + +// If format and parse are inverse operations then +// resolveObject(parse(x), y) == parse(resolve(x, y)) + +// format: [from, path, expected] +relativeTests.forEach(function(relativeTest) { + let actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]); + let expected = url.parse(relativeTest[2]); + + + assert.deepStrictEqual(actual, expected); + + expected = relativeTest[2]; + actual = url.format(actual); + + assert.strictEqual(actual, expected, + `format(${actual}) == ${expected}\n` + + `actual: ${actual}`); +}); + +// format: [to, from, result] +// the test: ['.//g', 'f:/a', 'f://g'] is a fundamental problem +// url.parse('f:/a') does not have a host +// url.resolve('f:/a', './/g') does not have a host because you have moved +// down to the g directory. i.e. f: //g, however when this url is parsed +// f:// will indicate that the host is g which is not the case. +// it is unclear to me how to keep this information from being lost +// it may be that a pathname of ////g should collapse to /g but this seems +// to be a lot of work for an edge case. Right now I remove the test +if (relativeTests2[181][0] === './/g' && + relativeTests2[181][1] === 'f:/a' && + relativeTests2[181][2] === 'f://g') { + relativeTests2.splice(181, 1); +} +relativeTests2.forEach(function(relativeTest) { + let actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]); + let expected = url.parse(relativeTest[2]); + + assert.deepStrictEqual( + actual, + expected, + `expected ${inspect(expected)} but got ${inspect(actual)}` + ); + + expected = url.format(relativeTest[2]); + actual = url.format(actual); + + assert.strictEqual(actual, expected, + `format(${relativeTest[1]}) == ${expected}\n` + + `actual: ${actual}`); +}); diff --git a/cli/tests/node_compat/test/parallel/test-url-urltooptions.js b/cli/tests/node_compat/test/parallel/test-url-urltooptions.js new file mode 100644 index 00000000000000..05813f0ae58154 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-url-urltooptions.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.11.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const { urlToHttpOptions } = require('url'); + +// Test urlToHttpOptions +const urlObj = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test'); +const opts = urlToHttpOptions(urlObj); +assert.strictEqual(opts instanceof URL, false); +assert.strictEqual(opts.protocol, 'http:'); +assert.strictEqual(opts.auth, 'user:pass'); +assert.strictEqual(opts.hostname, 'foo.bar.com'); +assert.strictEqual(opts.port, 21); +assert.strictEqual(opts.path, '/aaa/zzz?l=24'); +assert.strictEqual(opts.pathname, '/aaa/zzz'); +assert.strictEqual(opts.search, '?l=24'); +assert.strictEqual(opts.hash, '#test'); + +const { hostname } = urlToHttpOptions(new URL('http://[::1]:21')); +assert.strictEqual(hostname, '::1'); + +// If a WHATWG URL object is copied, it is possible that the resulting copy +// contains the Symbols that Node uses for brand checking, but not the data +// properties, which are getters. Verify that urlToHttpOptions() can handle +// such a case. +const copiedUrlObj = { ...urlObj }; +const copiedOpts = urlToHttpOptions(copiedUrlObj); +assert.strictEqual(copiedOpts instanceof URL, false); +assert.strictEqual(copiedOpts.protocol, undefined); +assert.strictEqual(copiedOpts.auth, undefined); +assert.strictEqual(copiedOpts.hostname, undefined); +// TODO(wafuwafu13): Fix `AssertionError: Values have the same structure but are not reference-equal` +// assert.strictEqual(copiedOpts.port, NaN); +assert.strictEqual(copiedOpts.path, ''); +assert.strictEqual(copiedOpts.pathname, undefined); +assert.strictEqual(copiedOpts.search, undefined); +assert.strictEqual(copiedOpts.hash, undefined); +assert.strictEqual(copiedOpts.href, undefined); diff --git a/cli/tests/node_compat/test/parallel/test-util-deprecate-invalid-code.js b/cli/tests/node_compat/test/parallel/test-util-deprecate-invalid-code.js new file mode 100644 index 00000000000000..18a5d58cbbf1eb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-deprecate-invalid-code.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const util = require('util'); + +[1, true, false, null, {}].forEach((notString) => { + assert.throws(() => util.deprecate(() => {}, 'message', notString), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "code" argument must be of type string.' + + common.invalidArgTypeHelper(notString) + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-util-deprecate.js b/cli/tests/node_compat/test/parallel/test-util-deprecate.js new file mode 100644 index 00000000000000..973f70e6b5a898 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-deprecate.js @@ -0,0 +1,64 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +// Tests basic functionality of util.deprecate(). + +const assert = require('assert'); +const util = require('util'); + +const expectedWarnings = new Map(); + +// Emits deprecation only once if same function is called. +{ + const msg = 'fhqwhgads'; + const fn = util.deprecate(() => {}, msg); + expectedWarnings.set(msg, { code: undefined, count: 1 }); + fn(); + fn(); +} + +// Emits deprecation twice for different functions. +{ + const msg = 'sterrance'; + const fn1 = util.deprecate(() => {}, msg); + const fn2 = util.deprecate(() => {}, msg); + expectedWarnings.set(msg, { code: undefined, count: 2 }); + fn1(); + fn2(); +} + +// Emits deprecation only once if optional code is the same, even for different +// functions. +{ + const msg = 'cannonmouth'; + const code = 'deprecatesque'; + const fn1 = util.deprecate(() => {}, msg, code); + const fn2 = util.deprecate(() => {}, msg, code); + expectedWarnings.set(msg, { code, count: 1 }); + fn1(); + fn2(); + fn1(); + fn2(); +} + +process.on('warning', (warning) => { + assert.strictEqual(warning.name, 'DeprecationWarning'); + assert.ok(expectedWarnings.has(warning.message)); + const expected = expectedWarnings.get(warning.message); + assert.strictEqual(warning.code, expected.code); + expected.count = expected.count - 1; + if (expected.count === 0) + expectedWarnings.delete(warning.message); +}); + +process.on('exit', () => { + assert.deepStrictEqual(expectedWarnings, new Map()); +}); diff --git a/cli/tests/node_compat/test/parallel/test-util-format.js b/cli/tests/node_compat/test/parallel/test-util-format.js new file mode 100644 index 00000000000000..9d474c481cda62 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-format.js @@ -0,0 +1,504 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const util = require('util'); +const symbol = Symbol('foo'); + +assert.strictEqual(util.format(), ''); +assert.strictEqual(util.format(''), ''); +assert.strictEqual(util.format([]), '[]'); +assert.strictEqual(util.format([0]), '[ 0 ]'); +assert.strictEqual(util.format({}), '{}'); +assert.strictEqual(util.format({ foo: 42 }), '{ foo: 42 }'); +assert.strictEqual(util.format(null), 'null'); +assert.strictEqual(util.format(true), 'true'); +assert.strictEqual(util.format(false), 'false'); +assert.strictEqual(util.format('test'), 'test'); + +// CHECKME this is for console.log() compatibility - but is it *right*? +assert.strictEqual(util.format('foo', 'bar', 'baz'), 'foo bar baz'); + +// ES6 Symbol handling +assert.strictEqual(util.format(symbol), 'Symbol(foo)'); +assert.strictEqual(util.format('foo', symbol), 'foo Symbol(foo)'); +assert.strictEqual(util.format('%s', symbol), 'Symbol(foo)'); +assert.strictEqual(util.format('%j', symbol), 'undefined'); + +// Number format specifier +assert.strictEqual(util.format('%d'), '%d'); +assert.strictEqual(util.format('%d', 42.0), '42'); +assert.strictEqual(util.format('%d', 42), '42'); +assert.strictEqual(util.format('%d', '42'), '42'); +assert.strictEqual(util.format('%d', '42.0'), '42'); +assert.strictEqual(util.format('%d', 1.5), '1.5'); +assert.strictEqual(util.format('%d', -0.5), '-0.5'); +assert.strictEqual(util.format('%d', -0.0), '-0'); +assert.strictEqual(util.format('%d', ''), '0'); +assert.strictEqual(util.format('%d', ' -0.000'), '-0'); +assert.strictEqual(util.format('%d', Symbol()), 'NaN'); +assert.strictEqual(util.format('%d', Infinity), 'Infinity'); +assert.strictEqual(util.format('%d', -Infinity), '-Infinity'); +assert.strictEqual(util.format('%d %d', 42, 43), '42 43'); +assert.strictEqual(util.format('%d %d', 42), '42 %d'); +assert.strictEqual( + util.format('%d', 1180591620717411303424), + '1.1805916207174113e+21' +); +assert.strictEqual( + util.format('%d', 1180591620717411303424n), + '1180591620717411303424n' +); +assert.strictEqual( + util.format('%d %d', 1180591620717411303424n, 12345678901234567890123n), + '1180591620717411303424n 12345678901234567890123n' +); + +// Integer format specifier +assert.strictEqual(util.format('%i'), '%i'); +assert.strictEqual(util.format('%i', 42.0), '42'); +assert.strictEqual(util.format('%i', 42), '42'); +assert.strictEqual(util.format('%i', '42'), '42'); +assert.strictEqual(util.format('%i', '42.0'), '42'); +assert.strictEqual(util.format('%i', 1.5), '1'); +assert.strictEqual(util.format('%i', -0.5), '-0'); +assert.strictEqual(util.format('%i', ''), 'NaN'); +assert.strictEqual(util.format('%i', Infinity), 'NaN'); +assert.strictEqual(util.format('%i', -Infinity), 'NaN'); +assert.strictEqual(util.format('%i', Symbol()), 'NaN'); +assert.strictEqual(util.format('%i %i', 42, 43), '42 43'); +assert.strictEqual(util.format('%i %i', 42), '42 %i'); +assert.strictEqual( + util.format('%i', 1180591620717411303424), + '1' +); +assert.strictEqual( + util.format('%i', 1180591620717411303424n), + '1180591620717411303424n' +); +assert.strictEqual( + util.format('%i %i', 1180591620717411303424n, 12345678901234567890123n), + '1180591620717411303424n 12345678901234567890123n' +); + +assert.strictEqual( + util.format('%d %i', 1180591620717411303424n, 12345678901234567890123n), + '1180591620717411303424n 12345678901234567890123n' +); + +assert.strictEqual( + util.format('%i %d', 1180591620717411303424n, 12345678901234567890123n), + '1180591620717411303424n 12345678901234567890123n' +); + +// Float format specifier +assert.strictEqual(util.format('%f'), '%f'); +assert.strictEqual(util.format('%f', 42.0), '42'); +assert.strictEqual(util.format('%f', 42), '42'); +assert.strictEqual(util.format('%f', '42'), '42'); +assert.strictEqual(util.format('%f', '-0.0'), '-0'); +assert.strictEqual(util.format('%f', '42.0'), '42'); +assert.strictEqual(util.format('%f', 1.5), '1.5'); +assert.strictEqual(util.format('%f', -0.5), '-0.5'); +assert.strictEqual(util.format('%f', Math.PI), '3.141592653589793'); +assert.strictEqual(util.format('%f', ''), 'NaN'); +assert.strictEqual(util.format('%f', Symbol('foo')), 'NaN'); +assert.strictEqual(util.format('%f', 5n), '5'); +assert.strictEqual(util.format('%f', Infinity), 'Infinity'); +assert.strictEqual(util.format('%f', -Infinity), '-Infinity'); +assert.strictEqual(util.format('%f %f', 42, 43), '42 43'); +assert.strictEqual(util.format('%f %f', 42), '42 %f'); + +// String format specifier +assert.strictEqual(util.format('%s'), '%s'); +assert.strictEqual(util.format('%s', undefined), 'undefined'); +assert.strictEqual(util.format('%s', null), 'null'); +assert.strictEqual(util.format('%s', 'foo'), 'foo'); +assert.strictEqual(util.format('%s', 42), '42'); +assert.strictEqual(util.format('%s', '42'), '42'); +assert.strictEqual(util.format('%s', -0), '-0'); +assert.strictEqual(util.format('%s', '-0.0'), '-0.0'); +assert.strictEqual(util.format('%s %s', 42, 43), '42 43'); +assert.strictEqual(util.format('%s %s', 42), '42 %s'); +assert.strictEqual(util.format('%s', 42n), '42n'); +assert.strictEqual(util.format('%s', Symbol('foo')), 'Symbol(foo)'); +assert.strictEqual(util.format('%s', true), 'true'); +assert.strictEqual(util.format('%s', { a: [1, 2, 3] }), '{ a: [Array] }'); +assert.strictEqual(util.format('%s', { toString() { return 'Foo'; } }), 'Foo'); +assert.strictEqual(util.format('%s', { toString: 5 }), '{ toString: 5 }'); +assert.strictEqual(util.format('%s', () => 5), '() => 5'); +assert.strictEqual(util.format('%s', Infinity), 'Infinity'); +assert.strictEqual(util.format('%s', -Infinity), '-Infinity'); + +// TODO(wafuwafu13): Fix +// // String format specifier including `toString` properties on the prototype. +// { +// class Foo { toString() { return 'Bar'; } } +// assert.strictEqual(util.format('%s', new Foo()), 'Bar'); +// assert.strictEqual( +// util.format('%s', Object.setPrototypeOf(new Foo(), null)), +// '[Foo: null prototype] {}' +// ); +// global.Foo = Foo; +// assert.strictEqual(util.format('%s', new Foo()), 'Bar'); +// delete global.Foo; +// class Bar { abc = true; } +// assert.strictEqual(util.format('%s', new Bar()), 'Bar { abc: true }'); +// class Foobar extends Array { aaa = true; } +// assert.strictEqual( +// util.format('%s', new Foobar(5)), +// 'Foobar(5) [ <5 empty items>, aaa: true ]' +// ); + +// // Subclassing: +// class B extends Foo {} + +// function C() {} +// C.prototype.toString = function() { +// return 'Custom'; +// }; + +// function D() { +// C.call(this); +// } +// D.prototype = Object.create(C.prototype); + +// assert.strictEqual( +// util.format('%s', new B()), +// 'Bar' +// ); +// assert.strictEqual( +// util.format('%s', new C()), +// 'Custom' +// ); +// assert.strictEqual( +// util.format('%s', new D()), +// 'Custom' +// ); + +// D.prototype.constructor = D; +// assert.strictEqual( +// util.format('%s', new D()), +// 'Custom' +// ); + +// D.prototype.constructor = null; +// assert.strictEqual( +// util.format('%s', new D()), +// 'Custom' +// ); + +// D.prototype.constructor = { name: 'Foobar' }; +// assert.strictEqual( +// util.format('%s', new D()), +// 'Custom' +// ); + +// Object.defineProperty(D.prototype, 'constructor', { +// get() { +// throw new Error(); +// }, +// configurable: true +// }); +// assert.strictEqual( +// util.format('%s', new D()), +// 'Custom' +// ); + +// assert.strictEqual( +// util.format('%s', Object.create(null)), +// '[Object: null prototype] {}' +// ); +// } + +// JSON format specifier +assert.strictEqual(util.format('%j'), '%j'); +assert.strictEqual(util.format('%j', 42), '42'); +assert.strictEqual(util.format('%j', '42'), '"42"'); +assert.strictEqual(util.format('%j %j', 42, 43), '42 43'); +assert.strictEqual(util.format('%j %j', 42), '42 %j'); + +// Object format specifier +const obj = { + foo: 'bar', + foobar: 1, + func: function() {} +}; +const nestedObj = { + foo: 'bar', + foobar: { + foo: 'bar', + func: function() {} + } +}; +const nestedObj2 = { + foo: 'bar', + foobar: 1, + func: [{ a: function() {} }] +}; +assert.strictEqual(util.format('%o'), '%o'); +assert.strictEqual(util.format('%o', 42), '42'); +assert.strictEqual(util.format('%o', 'foo'), '\'foo\''); +assert.strictEqual( + util.format('%o', obj), + '{\n' + + ' foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + '}'); +assert.strictEqual( + util.format('%o', nestedObj2), + '{\n' + + ' foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: [\n' + + ' {\n' + + ' a: [Function: a] {\n' + + ' [length]: 0,\n' + + ' [name]: \'a\',\n' + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + ' },\n' + + ' [length]: 1\n' + + ' ]\n' + + '}'); +assert.strictEqual( + util.format('%o', nestedObj), + '{\n' + + ' foo: \'bar\',\n' + + ' foobar: {\n' + + ' foo: \'bar\',\n' + + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + ' }\n' + + '}'); +assert.strictEqual( + util.format('%o %o', obj, obj), + '{\n' + + ' foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + '} {\n' + + ' foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + '}'); +assert.strictEqual( + util.format('%o %o', obj), + '{\n' + + ' foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + '} %o'); + +assert.strictEqual(util.format('%O'), '%O'); +assert.strictEqual(util.format('%O', 42), '42'); +assert.strictEqual(util.format('%O', 'foo'), '\'foo\''); +assert.strictEqual( + util.format('%O', obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); +assert.strictEqual( + util.format('%O', nestedObj), + '{ foo: \'bar\', foobar: { foo: \'bar\', func: [Function: func] } }'); +assert.strictEqual( + util.format('%O %O', obj, obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] } ' + + '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); +assert.strictEqual( + util.format('%O %O', obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] } %O'); + +// Various format specifiers +assert.strictEqual(util.format('%%s%s', 'foo'), '%sfoo'); +assert.strictEqual(util.format('%s:%s'), '%s:%s'); +assert.strictEqual(util.format('%s:%s', undefined), 'undefined:%s'); +assert.strictEqual(util.format('%s:%s', 'foo'), 'foo:%s'); +assert.strictEqual(util.format('%s:%i', 'foo'), 'foo:%i'); +assert.strictEqual(util.format('%s:%f', 'foo'), 'foo:%f'); +assert.strictEqual(util.format('%s:%s', 'foo', 'bar'), 'foo:bar'); +assert.strictEqual(util.format('%s:%s', 'foo', 'bar', 'baz'), 'foo:bar baz'); +assert.strictEqual(util.format('%%%s%%', 'hi'), '%hi%'); +assert.strictEqual(util.format('%%%s%%%%', 'hi'), '%hi%%'); +assert.strictEqual(util.format('%sbc%%def', 'a'), 'abc%def'); +assert.strictEqual(util.format('%d:%d', 12, 30), '12:30'); +assert.strictEqual(util.format('%d:%d', 12), '12:%d'); +assert.strictEqual(util.format('%d:%d'), '%d:%d'); +assert.strictEqual(util.format('%i:%i', 12, 30), '12:30'); +assert.strictEqual(util.format('%i:%i', 12), '12:%i'); +assert.strictEqual(util.format('%i:%i'), '%i:%i'); +assert.strictEqual(util.format('%f:%f', 12, 30), '12:30'); +assert.strictEqual(util.format('%f:%f', 12), '12:%f'); +assert.strictEqual(util.format('%f:%f'), '%f:%f'); +assert.strictEqual(util.format('o: %j, a: %j', {}, []), 'o: {}, a: []'); +assert.strictEqual(util.format('o: %j, a: %j', {}), 'o: {}, a: %j'); +assert.strictEqual(util.format('o: %j, a: %j'), 'o: %j, a: %j'); +assert.strictEqual(util.format('o: %o, a: %O', {}, []), 'o: {}, a: []'); +assert.strictEqual(util.format('o: %o, a: %o', {}), 'o: {}, a: %o'); +assert.strictEqual(util.format('o: %O, a: %O'), 'o: %O, a: %O'); + + +// Invalid format specifiers +assert.strictEqual(util.format('a% b', 'x'), 'a% b x'); +assert.strictEqual(util.format('percent: %d%, fraction: %d', 10, 0.1), + 'percent: 10%, fraction: 0.1'); +assert.strictEqual(util.format('abc%', 1), 'abc% 1'); + +// Additional arguments after format specifiers +assert.strictEqual(util.format('%i', 1, 'number'), '1 number'); +assert.strictEqual(util.format('%i', 1, () => {}), '1 [Function (anonymous)]'); + +// %c from https://console.spec.whatwg.org/ +assert.strictEqual(util.format('%c'), '%c'); +assert.strictEqual(util.format('%cab'), '%cab'); +assert.strictEqual(util.format('%cab', 'color: blue'), 'ab'); +assert.strictEqual(util.format('%cab', 'color: blue', 'c'), 'ab c'); + +{ + const o = {}; + o.o = o; + assert.strictEqual(util.format('%j', o), '[Circular]'); +} + +{ + const o = { + toJSON() { + throw new Error('Not a circular object but still not serializable'); + } + }; + assert.throws(() => util.format('%j', o), + /^Error: Not a circular object but still not serializable$/); +} + +// Errors +const err = new Error('foo'); +assert.strictEqual(util.format(err), err.stack); +class CustomError extends Error { + constructor(msg) { + super(); + Object.defineProperty(this, 'message', + { value: msg, enumerable: false }); + Object.defineProperty(this, 'name', + { value: 'CustomError', enumerable: false }); + Error.captureStackTrace(this, CustomError); + } +} +const customError = new CustomError('bar'); +assert.strictEqual(util.format(customError), customError.stack); +// Doesn't capture stack trace +function BadCustomError(msg) { + Error.call(this); + Object.defineProperty(this, 'message', + { value: msg, enumerable: false }); + Object.defineProperty(this, 'name', + { value: 'BadCustomError', enumerable: false }); +} +Object.setPrototypeOf(BadCustomError.prototype, Error.prototype); +Object.setPrototypeOf(BadCustomError, Error); +assert.strictEqual(util.format(new BadCustomError('foo')), + '[BadCustomError: foo]'); + +// The format of arguments should not depend on type of the first argument +assert.strictEqual(util.format('1', '1'), '1 1'); +assert.strictEqual(util.format(1, '1'), '1 1'); +assert.strictEqual(util.format('1', 1), '1 1'); +assert.strictEqual(util.format(1, -0), '1 -0'); +assert.strictEqual(util.format('1', () => {}), '1 [Function (anonymous)]'); +assert.strictEqual(util.format(1, () => {}), '1 [Function (anonymous)]'); +assert.strictEqual(util.format('1', "'"), "1 '"); +assert.strictEqual(util.format(1, "'"), "1 '"); +assert.strictEqual(util.format('1', 'number'), '1 number'); +assert.strictEqual(util.format(1, 'number'), '1 number'); +assert.strictEqual(util.format(5n), '5n'); +assert.strictEqual(util.format(5n, 5n), '5n 5n'); + +// Check `formatWithOptions`. +assert.strictEqual( + util.formatWithOptions( + { colors: true }, + true, undefined, Symbol(), 1, 5n, null, 'foobar' + ), + '\u001b[33mtrue\u001b[39m ' + + '\u001b[90mundefined\u001b[39m ' + + '\u001b[32mSymbol()\u001b[39m ' + + '\u001b[33m1\u001b[39m ' + + '\u001b[33m5n\u001b[39m ' + + '\u001b[1mnull\u001b[22m ' + + 'foobar' +); + +// TODO(wafuwafu13): Fix +// assert.strictEqual( +// util.format(new SharedArrayBuffer(4)), +// 'SharedArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' +// ); + +assert.strictEqual( + util.formatWithOptions( + { colors: true, compact: 3 }, + '%s', [ 1, { a: true }] + ), + '[ 1, [Object] ]' +); + +[ + undefined, + null, + false, + 5n, + 5, + 'test', + Symbol(), +].forEach((invalidOptions) => { + assert.throws(() => { + util.formatWithOptions(invalidOptions, { a: true }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + message: /"inspectOptions".+object/ + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-util-inherits.js b/cli/tests/node_compat/test/parallel/test-util-inherits.js new file mode 100644 index 00000000000000..8403f475dafda4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-inherits.js @@ -0,0 +1,117 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const { inherits } = require('util'); + +// Super constructor +function A() { + this._a = 'a'; +} +A.prototype.a = function() { return this._a; }; + +// One level of inheritance +function B(value) { + A.call(this); + this._b = value; +} +inherits(B, A); +B.prototype.b = function() { return this._b; }; + +assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(B, 'super_'), + { + value: A, + enumerable: false, + configurable: true, + writable: true + } +); + +const b = new B('b'); +assert.strictEqual(b.a(), 'a'); +assert.strictEqual(b.b(), 'b'); +assert.strictEqual(b.constructor, B); + +// Two levels of inheritance +function C() { + B.call(this, 'b'); + this._c = 'c'; +} +inherits(C, B); +C.prototype.c = function() { return this._c; }; +C.prototype.getValue = function() { return this.a() + this.b() + this.c(); }; + +assert.strictEqual(C.super_, B); + +const c = new C(); +assert.strictEqual(c.getValue(), 'abc'); +assert.strictEqual(c.constructor, C); + +// Inherits can be called after setting prototype properties +function D() { + C.call(this); + this._d = 'd'; +} + +D.prototype.d = function() { return this._d; }; +inherits(D, C); + +assert.strictEqual(D.super_, C); + +const d = new D(); +assert.strictEqual(d.c(), 'c'); +assert.strictEqual(d.d(), 'd'); +assert.strictEqual(d.constructor, D); + +// ES6 classes can inherit from a constructor function +class E { + constructor() { + D.call(this); + this._e = 'e'; + } + e() { return this._e; } +} +inherits(E, D); + +assert.strictEqual(E.super_, D); + +const e = new E(); +assert.strictEqual(e.getValue(), 'abc'); +assert.strictEqual(e.d(), 'd'); +assert.strictEqual(e.e(), 'e'); +assert.strictEqual(e.constructor, E); + +// Should throw with invalid arguments +assert.throws(() => { + inherits(A, {}); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "superCtor.prototype" property must be of type object. ' + + 'Received undefined' +}); + +assert.throws(() => { + inherits(A, null); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "superCtor" argument must be of type function. ' + + 'Received null' +}); + +assert.throws(() => { + inherits(null, A); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "ctor" argument must be of type function. Received null' +}); diff --git a/cli/tests/node_compat/test/parallel/test-util-inspect-long-running.js b/cli/tests/node_compat/test/parallel/test-util-inspect-long-running.js new file mode 100644 index 00000000000000..c4e6ec559e4910 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-inspect-long-running.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +// Test that huge objects don't crash due to exceeding the maximum heap size. + +const util = require('util'); + +// Create a difficult to stringify object. Without the artificial limitation +// this would crash or throw an maximum string size error. +let last = {}; +const obj = last; + +for (let i = 0; i < 1000; i++) { + last.next = { circular: obj, last, obj: { a: 1, b: 2, c: true } }; + last = last.next; + obj[i] = last; +} + +util.inspect(obj, { depth: Infinity }); diff --git a/cli/tests/node_compat/test/parallel/test-util-inspect-namespace.js b/cli/tests/node_compat/test/parallel/test-util-inspect-namespace.js new file mode 100644 index 00000000000000..786f0567198677 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-inspect-namespace.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --experimental-vm-modules +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// TODO(wafuwafu13): Implement 'vm' +// const { SourceTextModule } = require('vm'); +const { inspect } = require('util'); + +// (async () => { +// const m = new SourceTextModule('export const a = 1; export var b = 2'); +// await m.link(() => 0); +// assert.strictEqual( +// inspect(m.namespace), +// '[Module: null prototype] { a: , b: undefined }'); +// await m.evaluate(); +// assert.strictEqual( +// inspect(m.namespace), +// '[Module: null prototype] { a: 1, b: 2 }' +// ); +// })().then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-util-inspect-proxy.js b/cli/tests/node_compat/test/parallel/test-util-inspect-proxy.js new file mode 100644 index 00000000000000..ef78ab07a2f5c3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-inspect-proxy.js @@ -0,0 +1,172 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); +const util = require('util'); +// TODO(wafuwafu13): Implement 'internal/test/binding' +// const { internalBinding } = require('internal/test/binding'); +// const processUtil = internalBinding('util'); +const opts = { showProxy: true }; + +// let proxyObj; +// let called = false; +// const target = { +// [util.inspect.custom](depth, { showProxy }) { +// if (showProxy === false) { +// called = true; +// if (proxyObj !== this) { +// throw new Error('Failed'); +// } +// } +// return [1, 2, 3]; +// } +// }; + +// // TODO(wafuwafu13): Fix Uncaught Error +// const handler = { +// getPrototypeOf() { throw new Error('getPrototypeOf'); }, +// setPrototypeOf() { throw new Error('setPrototypeOf'); }, +// isExtensible() { throw new Error('isExtensible'); }, +// preventExtensions() { throw new Error('preventExtensions'); }, +// getOwnPropertyDescriptor() { throw new Error('getOwnPropertyDescriptor'); }, +// defineProperty() { throw new Error('defineProperty'); }, +// has() { throw new Error('has'); }, +// get() { throw new Error('get'); }, +// set() { throw new Error('set'); }, +// deleteProperty() { throw new Error('deleteProperty'); }, +// ownKeys() { throw new Error('ownKeys'); }, +// apply() { throw new Error('apply'); }, +// construct() { throw new Error('construct'); } +// }; +// proxyObj = new Proxy(target, handler); + +// // Inspecting the proxy should not actually walk it's properties +// util.inspect(proxyObj, opts); + +// // Make sure inspecting object does not trigger any proxy traps. +// util.format('%s', proxyObj); + +// TODO(wafuwafu13): Implement processUtil +// // getProxyDetails is an internal method, not intended for public use. +// // This is here to test that the internals are working correctly. +// let details = processUtil.getProxyDetails(proxyObj, true); +// assert.strictEqual(target, details[0]); +// assert.strictEqual(handler, details[1]); + +// details = processUtil.getProxyDetails(proxyObj); +// assert.strictEqual(target, details[0]); +// assert.strictEqual(handler, details[1]); + +// details = processUtil.getProxyDetails(proxyObj, false); +// assert.strictEqual(target, details); + +// assert.strictEqual( +// util.inspect(proxyObj, opts), +// 'Proxy [\n' + +// ' [ 1, 2, 3 ],\n' + +// ' {\n' + +// ' getPrototypeOf: [Function: getPrototypeOf],\n' + +// ' setPrototypeOf: [Function: setPrototypeOf],\n' + +// ' isExtensible: [Function: isExtensible],\n' + +// ' preventExtensions: [Function: preventExtensions],\n' + +// ' getOwnPropertyDescriptor: [Function: getOwnPropertyDescriptor],\n' + +// ' defineProperty: [Function: defineProperty],\n' + +// ' has: [Function: has],\n' + +// ' get: [Function: get],\n' + +// ' set: [Function: set],\n' + +// ' deleteProperty: [Function: deleteProperty],\n' + +// ' ownKeys: [Function: ownKeys],\n' + +// ' apply: [Function: apply],\n' + +// ' construct: [Function: construct]\n' + +// ' }\n' + +// ']' +// ); + +// TODO(wafuwafu13): Implement processUtil +// // Using getProxyDetails with non-proxy returns undefined +// assert.strictEqual(processUtil.getProxyDetails({}), undefined); + +// // Inspecting a proxy without the showProxy option set to true should not +// // trigger any proxy handlers. +// assert.strictEqual(util.inspect(proxyObj), '[ 1, 2, 3 ]'); +// assert(called); + +// Yo dawg, I heard you liked Proxy so I put a Proxy +// inside your Proxy that proxies your Proxy's Proxy. +const proxy1 = new Proxy({}, {}); +const proxy2 = new Proxy(proxy1, {}); +const proxy3 = new Proxy(proxy2, proxy1); +const proxy4 = new Proxy(proxy1, proxy2); +const proxy5 = new Proxy(proxy3, proxy4); +const proxy6 = new Proxy(proxy5, proxy5); +const expected0 = '{}'; +const expected1 = 'Proxy [ {}, {} ]'; +const expected2 = 'Proxy [ Proxy [ {}, {} ], {} ]'; +const expected3 = 'Proxy [ Proxy [ Proxy [ {}, {} ], {} ], Proxy [ {}, {} ] ]'; +const expected4 = 'Proxy [ Proxy [ {}, {} ], Proxy [ Proxy [ {}, {} ], {} ] ]'; +const expected5 = 'Proxy [\n ' + + 'Proxy [ Proxy [ Proxy [Array], {} ], Proxy [ {}, {} ] ],\n' + + ' Proxy [ Proxy [ {}, {} ], Proxy [ Proxy [Array], {} ] ]' + + '\n]'; +const expected6 = 'Proxy [\n' + + ' Proxy [\n' + + ' Proxy [ Proxy [Array], Proxy [Array] ],\n' + + ' Proxy [ Proxy [Array], Proxy [Array] ]\n' + + ' ],\n' + + ' Proxy [\n' + + ' Proxy [ Proxy [Array], Proxy [Array] ],\n' + + ' Proxy [ Proxy [Array], Proxy [Array] ]\n' + + ' ]\n' + + ']'; +// assert.strictEqual( +// util.inspect(proxy1, { showProxy: 1, depth: null }), +// expected1); +// assert.strictEqual(util.inspect(proxy2, opts), expected2); +// assert.strictEqual(util.inspect(proxy3, opts), expected3); +// assert.strictEqual(util.inspect(proxy4, opts), expected4); +// assert.strictEqual(util.inspect(proxy5, opts), expected5); +// assert.strictEqual(util.inspect(proxy6, opts), expected6); +// assert.strictEqual(util.inspect(proxy1), expected0); +// assert.strictEqual(util.inspect(proxy2), expected0); +// assert.strictEqual(util.inspect(proxy3), expected0); +// assert.strictEqual(util.inspect(proxy4), expected0); +// assert.strictEqual(util.inspect(proxy5), expected0); +// assert.strictEqual(util.inspect(proxy6), expected0); + +// // Just for fun, let's create a Proxy using Arrays. +// const proxy7 = new Proxy([], []); +// const expected7 = 'Proxy [ [], [] ]'; +// assert.strictEqual(util.inspect(proxy7, opts), expected7); +// assert.strictEqual(util.inspect(proxy7), '[]'); + +// // Now we're just getting silly, right? +// const proxy8 = new Proxy(Date, []); +// const proxy9 = new Proxy(Date, String); +// const expected8 = 'Proxy [ [Function: Date], [] ]'; +// const expected9 = 'Proxy [ [Function: Date], [Function: String] ]'; +// assert.strictEqual(util.inspect(proxy8, opts), expected8); +// assert.strictEqual(util.inspect(proxy9, opts), expected9); +// assert.strictEqual(util.inspect(proxy8), '[Function: Date]'); +// assert.strictEqual(util.inspect(proxy9), '[Function: Date]'); + +// const proxy10 = new Proxy(() => {}, {}); +// const proxy11 = new Proxy(() => {}, { +// get() { +// return proxy11; +// }, +// apply() { +// return proxy11; +// } +// }); +// const expected10 = '[Function (anonymous)]'; +// const expected11 = '[Function (anonymous)]'; +// assert.strictEqual(util.inspect(proxy10), expected10); +// assert.strictEqual(util.inspect(proxy11), expected11); diff --git a/cli/tests/node_compat/test/parallel/test-util-inspect.js b/cli/tests/node_compat/test/parallel/test-util-inspect.js new file mode 100644 index 00000000000000..fd3243ec5e9112 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-inspect.js @@ -0,0 +1,3200 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +'use strict'; +const common = require('../common'); +const assert = require('assert'); +// TODO(wafuwafu13): Implement 'internal/test/binding' +// const { internalBinding } = require('internal/test/binding'); +// const JSStream = internalBinding('js_stream').JSStream; +const util = require('util'); +// TODO(wafuwafu13): Implement 'vm' +// const vm = require('vm'); +// TODO(wafuwafu13): Implement 'v8' +// const v8 = require('v8'); +// TODO(wafuwafu13): Implement 'internal/test/binding' +// const { previewEntries } = internalBinding('util'); +const { inspect } = util; +// TODO(wafuwafu13): Implement MessageChannel +// const { MessageChannel } = require('worker_threads'); + +assert.strictEqual(util.inspect(1), '1'); +assert.strictEqual(util.inspect(false), 'false'); +assert.strictEqual(util.inspect(''), "''"); +assert.strictEqual(util.inspect('hello'), "'hello'"); +assert.strictEqual(util.inspect(function abc() {}), '[Function: abc]'); +assert.strictEqual(util.inspect(() => {}), '[Function (anonymous)]'); +assert.strictEqual( + util.inspect(async function() {}), + '[AsyncFunction (anonymous)]' +); +assert.strictEqual(util.inspect(async () => {}), '[AsyncFunction (anonymous)]'); + +// Special function inspection. +{ + const fn = (() => function*() {})(); + assert.strictEqual( + util.inspect(fn), + '[GeneratorFunction (anonymous)]' + ); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(async function* abc() {}), + // '[AsyncGeneratorFunction: abc]' + // ); + Object.setPrototypeOf(fn, Object.getPrototypeOf(async () => {})); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(fn), + // '[GeneratorFunction (anonymous)] AsyncFunction' + // ); + Object.defineProperty(fn, 'name', { value: 5, configurable: true }); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(fn), + // '[GeneratorFunction: 5] AsyncFunction' + // ); + Object.defineProperty(fn, Symbol.toStringTag, { + value: 'Foobar', + configurable: true + }); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect({ ['5']: fn }), + // "{ '5': [GeneratorFunction: 5] AsyncFunction [Foobar] }" + // ); + Object.defineProperty(fn, 'name', { value: '5', configurable: true }); + Object.setPrototypeOf(fn, null); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(fn), + // '[GeneratorFunction (null prototype): 5] [Foobar]' + // ); + // assert.strictEqual( + // util.inspect({ ['5']: fn }), + // "{ '5': [GeneratorFunction (null prototype): 5] [Foobar] }" + // ); +} + +assert.strictEqual(util.inspect(undefined), 'undefined'); +assert.strictEqual(util.inspect(null), 'null'); +assert.strictEqual(util.inspect(/foo(bar\n)?/gi), '/foo(bar\\n)?/gi'); +assert.strictEqual( + util.inspect(new Date('Sun, 14 Feb 2010 11:48:40 GMT')), + new Date('2010-02-14T12:48:40+01:00').toISOString() +); +assert.strictEqual(util.inspect(new Date('')), (new Date('')).toString()); +assert.strictEqual(util.inspect('\n\x01'), "'\\n\\x01'"); +assert.strictEqual( + util.inspect(`${Array(75).fill(1)}'\n\x1d\n\x03\x85\x7f\x7e\x9f\xa0`), + // eslint-disable-next-line no-irregular-whitespace + `"${Array(75).fill(1)}'\\n" +\n '\\x1D\\n' +\n '\\x03\\x85\\x7F~\\x9F '` +); +assert.strictEqual(util.inspect([]), '[]'); +assert.strictEqual(util.inspect(Object.create([])), 'Array {}'); +assert.strictEqual(util.inspect([1, 2]), '[ 1, 2 ]'); +assert.strictEqual(util.inspect([1, [2, 3]]), '[ 1, [ 2, 3 ] ]'); +assert.strictEqual(util.inspect({}), '{}'); +assert.strictEqual(util.inspect({ a: 1 }), '{ a: 1 }'); +assert.strictEqual(util.inspect({ a: function() {} }), '{ a: [Function: a] }'); +assert.strictEqual(util.inspect({ a: () => {} }), '{ a: [Function: a] }'); +// eslint-disable-next-line func-name-matching +assert.strictEqual(util.inspect({ a: async function abc() {} }), + '{ a: [AsyncFunction: abc] }'); +assert.strictEqual(util.inspect({ a: async () => {} }), + '{ a: [AsyncFunction: a] }'); +assert.strictEqual(util.inspect({ a: function*() {} }), + '{ a: [GeneratorFunction: a] }'); +assert.strictEqual(util.inspect({ a: 1, b: 2 }), '{ a: 1, b: 2 }'); +assert.strictEqual(util.inspect({ 'a': {} }), '{ a: {} }'); +assert.strictEqual(util.inspect({ 'a': { 'b': 2 } }), '{ a: { b: 2 } }'); +assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }), + '{ a: { b: { c: [Object] } } }'); +assert.strictEqual( + util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }, false, null), + '{\n a: { b: { c: { d: 2 } } }\n}'); +// TODO(wafuwafu13): Fix +// assert.strictEqual(util.inspect([1, 2, 3], true), '[ 1, 2, 3, [length]: 3 ]'); +assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 0), + '{ a: [Object] }'); +assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 1), + '{ a: { b: [Object] } }'); +assert.strictEqual(util.inspect({ 'a': { 'b': ['c'] } }, false, 1), + '{ a: { b: [Array] } }'); +// TODO(wafuwafu13): Fix +// assert.strictEqual(util.inspect(new Uint8Array(0)), 'Uint8Array(0) []'); +// assert(inspect(new Uint8Array(0), { showHidden: true }).includes('[buffer]')); +assert.strictEqual( + util.inspect( + Object.create( + {}, + { visible: { value: 1, enumerable: true }, hidden: { value: 2 } } + ) + ), + '{ visible: 1 }' +); +// TODO(wafuwafu13): Fix +// assert.strictEqual( +// util.inspect( +// Object.assign(new String('hello'), { [Symbol('foo')]: 123 }), +// { showHidden: true } +// ), +// "[String: 'hello'] { [length]: 5, [Symbol(foo)]: 123 }" +// ); + +// TODO(wafuwafu13): Implement JSStream +// assert.match(util.inspect((new JSStream())._externalStream), +// /^\[External: [0-9a-f]+\]$/); + +{ + const regexp = /regexp/; + regexp.aprop = 42; + assert.strictEqual(util.inspect({ a: regexp }, false, 0), '{ a: /regexp/ }'); +} + +assert.match( + util.inspect({ a: { a: { a: { a: {} } } } }, undefined, undefined, true), + /Object/ +); +assert.doesNotMatch( + util.inspect({ a: { a: { a: { a: {} } } } }, undefined, null, true), + /Object/ +); + +// TODO(wafuwafu13): Fix +// { +// const showHidden = true; +// const ab = new Uint8Array([1, 2, 3, 4]).buffer; +// const dv = new DataView(ab, 1, 2); +// assert.strictEqual( +// util.inspect(ab, showHidden), +// 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }' +// ); +// assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden), +// 'DataView {\n' + +// ' byteLength: 2,\n' + +// ' byteOffset: 1,\n' + +// ' buffer: ArrayBuffer {' + +// ' [Uint8Contents]: <01 02 03 04>, byteLength: 4 }\n}'); +// assert.strictEqual( +// util.inspect(ab, showHidden), +// 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }' +// ); +// assert.strictEqual(util.inspect(dv, showHidden), +// 'DataView {\n' + +// ' byteLength: 2,\n' + +// ' byteOffset: 1,\n' + +// ' buffer: ArrayBuffer { [Uint8Contents]: ' + +// '<01 02 03 04>, byteLength: 4 }\n}'); +// ab.x = 42; +// dv.y = 1337; +// assert.strictEqual(util.inspect(ab, showHidden), +// 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, ' + +// 'byteLength: 4, x: 42 }'); +// assert.strictEqual(util.inspect(dv, showHidden), +// 'DataView {\n' + +// ' byteLength: 2,\n' + +// ' byteOffset: 1,\n' + +// ' buffer: ArrayBuffer { [Uint8Contents]: <01 02 03 04>,' + +// ' byteLength: 4, x: 42 },\n' + +// ' y: 1337\n}'); +// } + +// TODO(wafuwafu13): Implement `MessageChannel` +// { +// const ab = new ArrayBuffer(42); +// assert.strictEqual(ab.byteLength, 42); +// new MessageChannel().port1.postMessage(ab, [ ab ]); +// assert.strictEqual(ab.byteLength, 0); +// assert.strictEqual(util.inspect(ab), +// 'ArrayBuffer { (detached), byteLength: 0 }'); +// } + +// TODO(wafuwafu13): Fix +// // Truncate output for ArrayBuffers using plural or singular bytes +// { +// const ab = new ArrayBuffer(3); +// assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 2 }), +// 'ArrayBuffer { [Uint8Contents]' + +// ': <00 00 ... 1 more byte>, byteLength: 3 }'); +// assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 1 }), +// 'ArrayBuffer { [Uint8Contents]' + +// ': <00 ... 2 more bytes>, byteLength: 3 }'); +// } + +// TODO(wafuwafu13): Implement 'vm' +// // Now do the same checks but from a different context. +// { +// const showHidden = false; +// const ab = vm.runInNewContext('new ArrayBuffer(4)'); +// const dv = vm.runInNewContext('new DataView(ab, 1, 2)', { ab }); +// assert.strictEqual( +// util.inspect(ab, showHidden), +// 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' +// ); +// assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden), +// 'DataView {\n' + +// ' byteLength: 2,\n' + +// ' byteOffset: 1,\n' + +// ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + +// ' byteLength: 4 }\n}'); +// assert.strictEqual( +// util.inspect(ab, showHidden), +// 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' +// ); +// assert.strictEqual(util.inspect(dv, showHidden), +// 'DataView {\n' + +// ' byteLength: 2,\n' + +// ' byteOffset: 1,\n' + +// ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + +// ' byteLength: 4 }\n}'); +// ab.x = 42; +// dv.y = 1337; +// assert.strictEqual(util.inspect(ab, showHidden), +// 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, ' + +// 'byteLength: 4, x: 42 }'); +// assert.strictEqual(util.inspect(dv, showHidden), +// 'DataView {\n' + +// ' byteLength: 2,\n' + +// ' byteOffset: 1,\n' + +// ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + +// ' byteLength: 4, x: 42 },\n' + +// ' y: 1337\n}'); +// } + +// TODO(wafuwafu13): Fix +// [ Float32Array, +// Float64Array, +// Int16Array, +// Int32Array, +// Int8Array, +// Uint16Array, +// Uint32Array, +// Uint8Array, +// Uint8ClampedArray ].forEach((constructor) => { +// const length = 2; +// const byteLength = length * constructor.BYTES_PER_ELEMENT; +// const array = new constructor(new ArrayBuffer(byteLength), 0, length); +// array[0] = 65; +// array[1] = 97; +// assert.strictEqual( +// util.inspect(array, { showHidden: true }), +// `${constructor.name}(${length}) [\n` + +// ' 65,\n' + +// ' 97,\n' + +// ` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` + +// ` [length]: ${length},\n` + +// ` [byteLength]: ${byteLength},\n` + +// ' [byteOffset]: 0,\n' + +// ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); +// assert.strictEqual( +// util.inspect(array, false), +// `${constructor.name}(${length}) [ 65, 97 ]` +// ); +// }); + +// TODO(wafuwafu13): Implement 'vm' +// // Now check that declaring a TypedArray in a different context works the same. +// [ Float32Array, +// Float64Array, +// Int16Array, +// Int32Array, +// Int8Array, +// Uint16Array, +// Uint32Array, +// Uint8Array, +// Uint8ClampedArray ].forEach((constructor) => { +// const length = 2; +// const byteLength = length * constructor.BYTES_PER_ELEMENT; +// const array = vm.runInNewContext( +// 'new constructor(new ArrayBuffer(byteLength), 0, length)', +// { constructor, byteLength, length } +// ); +// array[0] = 65; +// array[1] = 97; +// assert.strictEqual( +// util.inspect(array, true), +// `${constructor.name}(${length}) [\n` + +// ' 65,\n' + +// ' 97,\n' + +// ` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` + +// ` [length]: ${length},\n` + +// ` [byteLength]: ${byteLength},\n` + +// ' [byteOffset]: 0,\n' + +// ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); +// assert.strictEqual( +// util.inspect(array, false), +// `${constructor.name}(${length}) [ 65, 97 ]` +// ); +// }); + +// TODO(wafuwafu13): Fix +// { +// const brokenLength = new Float32Array(2); +// Object.defineProperty(brokenLength, 'length', { value: -1 }); +// assert.strictEqual(inspect(brokenLength), 'Float32Array(2) [ 0n, 0n ]'); +// } + +assert.strictEqual( + util.inspect(Object.create({}, { + visible: { value: 1, enumerable: true }, + hidden: { value: 2 } + }), { showHidden: true }), + '{ visible: 1, [hidden]: 2 }' +); +// Objects without prototype. +assert.strictEqual( + util.inspect(Object.create(null, { + name: { value: 'Tim', enumerable: true }, + hidden: { value: 'secret' } + }), { showHidden: true }), + "[Object: null prototype] { name: 'Tim', [hidden]: 'secret' }" +); + +assert.strictEqual( + util.inspect(Object.create(null, { + name: { value: 'Tim', enumerable: true }, + hidden: { value: 'secret' } + })), + "[Object: null prototype] { name: 'Tim' }" +); + +// Dynamic properties. +{ + assert.strictEqual( + util.inspect({ get readonly() { return 1; } }), + '{ readonly: [Getter] }'); + + assert.strictEqual( + util.inspect({ get readwrite() { return 1; }, set readwrite(val) {} }), + '{ readwrite: [Getter/Setter] }'); + + assert.strictEqual( + // eslint-disable-next-line accessor-pairs + util.inspect({ set writeonly(val) {} }), + '{ writeonly: [Setter] }'); + + const value = {}; + value.a = value; + assert.strictEqual(util.inspect(value), ' { a: [Circular *1] }'); + const getterFn = { + get one() { + return null; + } + }; + assert.strictEqual( + util.inspect(getterFn, { getters: true }), + '{ one: [Getter: null] }' + ); +} + +// TODO(wafuwafu13): Fix +// // Array with dynamic properties. +// { +// const value = [1, 2, 3]; +// Object.defineProperty( +// value, +// 'growingLength', +// { +// enumerable: true, +// get: function() { this.push(true); return this.length; } +// } +// ); +// Object.defineProperty( +// value, +// '-1', +// { +// enumerable: true, +// value: -1 +// } +// ); +// assert.strictEqual(util.inspect(value), +// "[ 1, 2, 3, growingLength: [Getter], '-1': -1 ]"); +// } + +// Array with inherited number properties. +{ + class CustomArray extends Array {} + CustomArray.prototype[5] = 'foo'; + CustomArray.prototype[49] = 'bar'; + CustomArray.prototype.foo = true; + const arr = new CustomArray(50); + arr[49] = 'I win'; + assert.strictEqual( + util.inspect(arr), + "CustomArray(50) [ <49 empty items>, 'I win' ]" + ); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(arr, { showHidden: true }), + // 'CustomArray(50) [\n' + + // ' <49 empty items>,\n' + + // " 'I win',\n" + + // ' [length]: 50,\n' + + // " '5': 'foo',\n" + + // ' foo: true\n' + + // ']' + // ); +} + +// TODO(wafuwafu13): Fix +// // Array with extra properties. +// { +// const arr = [1, 2, 3, , ]; +// arr.foo = 'bar'; +// assert.strictEqual(util.inspect(arr), +// "[ 1, 2, 3, <1 empty item>, foo: 'bar' ]"); + +// const arr2 = []; +// assert.strictEqual(util.inspect([], { showHidden: true }), '[ [length]: 0 ]'); +// arr2['00'] = 1; +// assert.strictEqual(util.inspect(arr2), "[ '00': 1 ]"); +// assert.strictEqual(util.inspect(arr2, { showHidden: true }), +// "[ [length]: 0, '00': 1 ]"); +// arr2[1] = 0; +// assert.strictEqual(util.inspect(arr2), "[ <1 empty item>, 0, '00': 1 ]"); +// assert.strictEqual(util.inspect(arr2, { showHidden: true }), +// "[ <1 empty item>, 0, [length]: 2, '00': 1 ]"); +// delete arr2[1]; +// assert.strictEqual(util.inspect(arr2), "[ <2 empty items>, '00': 1 ]"); +// assert.strictEqual(util.inspect(arr2, { showHidden: true }), +// "[ <2 empty items>, [length]: 2, '00': 1 ]"); +// arr2['01'] = 2; +// assert.strictEqual(util.inspect(arr2), +// "[ <2 empty items>, '00': 1, '01': 2 ]"); +// assert.strictEqual(util.inspect(arr2, { showHidden: true }), +// "[ <2 empty items>, [length]: 2, '00': 1, '01': 2 ]"); +// delete arr2['00']; +// arr2[0] = 0; +// assert.strictEqual(util.inspect(arr2), +// "[ 0, <1 empty item>, '01': 2 ]"); +// assert.strictEqual(util.inspect(arr2, { showHidden: true }), +// "[ 0, <1 empty item>, [length]: 2, '01': 2 ]"); +// delete arr2['01']; +// arr2[2 ** 32 - 2] = 'max'; +// arr2[2 ** 32 - 1] = 'too far'; +// assert.strictEqual( +// util.inspect(arr2), +// "[ 0, <4294967293 empty items>, 'max', '4294967295': 'too far' ]" +// ); + +// const arr3 = []; +// arr3[-1] = -1; +// assert.strictEqual(util.inspect(arr3), "[ '-1': -1 ]"); +// } + +// TODO(wafuwafu13): Fix +// // Indices out of bounds. +// { +// const arr = []; +// arr[2 ** 32] = true; // Not a valid array index. +// assert.strictEqual(util.inspect(arr), "[ '4294967296': true ]"); +// arr[0] = true; +// arr[10] = true; +// assert.strictEqual(util.inspect(arr), +// "[ true, <9 empty items>, true, '4294967296': true ]"); +// arr[2 ** 32 - 2] = true; +// arr[2 ** 32 - 1] = true; +// arr[2 ** 32 + 1] = true; +// delete arr[0]; +// delete arr[10]; +// assert.strictEqual(util.inspect(arr), +// ['[', +// '<4294967294 empty items>,', +// 'true,', +// "'4294967296': true,", +// "'4294967295': true,", +// "'4294967297': true\n]", +// ].join('\n ')); +// } + +// Function with properties. +{ + const value = () => {}; + value.aprop = 42; + assert.strictEqual(util.inspect(value), '[Function: value] { aprop: 42 }'); +} + +// Anonymous function with properties. +{ + const value = (() => function() {})(); + value.aprop = 42; + assert.strictEqual( + util.inspect(value), + '[Function (anonymous)] { aprop: 42 }' + ); +} + +// Regular expressions with properties. +{ + const value = /123/ig; + value.aprop = 42; + assert.strictEqual(util.inspect(value), '/123/gi { aprop: 42 }'); +} + +// Dates with properties. +{ + const value = new Date('Sun, 14 Feb 2010 11:48:40 GMT'); + value.aprop = 42; + assert.strictEqual(util.inspect(value), + '2010-02-14T11:48:40.000Z { aprop: 42 }'); +} + +// TODO(wafuwafu13): Implement 'vm' +// // Test the internal isDate implementation. +// { +// const Date2 = vm.runInNewContext('Date'); +// const d = new Date2(); +// const orig = util.inspect(d); +// Date2.prototype.foo = 'bar'; +// const after = util.inspect(d); +// assert.strictEqual(orig, after); +// } + +// Test positive/negative zero. +assert.strictEqual(util.inspect(0), '0'); +assert.strictEqual(util.inspect(-0), '-0'); +// Edge case from check. +assert.strictEqual(util.inspect(-5e-324), '-5e-324'); + +// Test for sparse array. +{ + const a = ['foo', 'bar', 'baz']; + assert.strictEqual(util.inspect(a), "[ 'foo', 'bar', 'baz' ]"); + delete a[1]; + assert.strictEqual(util.inspect(a), "[ 'foo', <1 empty item>, 'baz' ]"); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(a, true), + // "[ 'foo', <1 empty item>, 'baz', [length]: 3 ]" + // ); + assert.strictEqual(util.inspect(new Array(5)), '[ <5 empty items> ]'); + a[3] = 'bar'; + a[100] = 'qux'; + assert.strictEqual( + util.inspect(a, { breakLength: Infinity }), + "[ 'foo', <1 empty item>, 'baz', 'bar', <96 empty items>, 'qux' ]" + ); + delete a[3]; + assert.strictEqual( + util.inspect(a, { maxArrayLength: 4 }), + "[ 'foo', <1 empty item>, 'baz', <97 empty items>, ... 1 more item ]" + ); + // test 4 special case + assert.strictEqual(util.inspect(a, { + maxArrayLength: 2 + }), "[ 'foo', <1 empty item>, ... 99 more items ]"); +} + +// TODO(wafuwafu13): Implement `previewEntries` +// Test for Array constructor in different context. +// { +// const map = new Map(); +// map.set(1, 2); +// // Passing only a single argument to indicate a set iterator. +// const valsSetIterator = previewEntries(map.entries()); +// // Passing through true to indicate a map iterator. +// const valsMapIterEntries = previewEntries(map.entries(), true); +// const valsMapIterKeys = previewEntries(map.keys(), true); + +// assert.strictEqual(util.inspect(valsSetIterator), '[ 1, 2 ]'); +// assert.strictEqual(util.inspect(valsMapIterEntries), '[ [ 1, 2 ], true ]'); +// assert.strictEqual(util.inspect(valsMapIterKeys), '[ [ 1 ], false ]'); +// } + +// TODO(wafuwafu13): Implement 'vm' +// // Test for other constructors in different context. +// { +// let obj = vm.runInNewContext('(function(){return {}})()', {}); +// assert.strictEqual(util.inspect(obj), '{}'); +// obj = vm.runInNewContext('const m=new Map();m.set(1,2);m', {}); +// assert.strictEqual(util.inspect(obj), 'Map(1) { 1 => 2 }'); +// obj = vm.runInNewContext('const s=new Set();s.add(1);s.add(2);s', {}); +// assert.strictEqual(util.inspect(obj), 'Set(2) { 1, 2 }'); +// obj = vm.runInNewContext('fn=function(){};new Promise(fn,fn)', {}); +// assert.strictEqual(util.inspect(obj), 'Promise { }'); +// } + +// Test for property descriptors. +{ + const getter = Object.create(null, { + a: { + get: function() { return 'aaa'; } + } + }); + const setter = Object.create(null, { + b: { // eslint-disable-line accessor-pairs + set: function() {} + } + }); + const getterAndSetter = Object.create(null, { + c: { + get: function() { return 'ccc'; }, + set: function() {} + } + }); + assert.strictEqual( + util.inspect(getter, true), + '[Object: null prototype] { [a]: [Getter] }' + ); + assert.strictEqual( + util.inspect(setter, true), + '[Object: null prototype] { [b]: [Setter] }' + ); + assert.strictEqual( + util.inspect(getterAndSetter, true), + '[Object: null prototype] { [c]: [Getter/Setter] }' + ); +} + +// Exceptions should print the error message, not '{}'. +{ + [ + new Error(), + new Error('FAIL'), + new TypeError('FAIL'), + new SyntaxError('FAIL'), + ].forEach((err) => { + assert.strictEqual(util.inspect(err), err.stack); + }); + assert.throws( + () => undef(), // eslint-disable-line no-undef + (e) => { + assert.strictEqual(util.inspect(e), e.stack); + return true; + } + ); + + const ex = util.inspect(new Error('FAIL'), true); + assert(ex.includes('Error: FAIL')); + assert(ex.includes('[stack]')); + assert(ex.includes('[message]')); +} + +{ + const tmp = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + const err = new Error('foo'); + const err2 = new Error('foo\nbar'); + assert.strictEqual(util.inspect(err, { compact: true }), '[Error: foo]'); + assert(err.stack); + delete err.stack; + assert(!err.stack); + // TODO(wafuwafu13): Fix + // assert.strictEqual(util.inspect(err, { compact: true }), '[Error: foo]'); + // assert.strictEqual( + // util.inspect(err2, { compact: true }), + // '[Error: foo\nbar]' + // ); + + // err.bar = true; + // err2.bar = true; + + // assert.strictEqual( + // util.inspect(err, { compact: true }), + // '{ [Error: foo] bar: true }' + // ); + // assert.strictEqual( + // util.inspect(err2, { compact: true }), + // '{ [Error: foo\nbar]\n bar: true }' + // ); + // assert.strictEqual( + // util.inspect(err, { compact: true, breakLength: 5 }), + // '{ [Error: foo]\n bar: true }' + // ); + // assert.strictEqual( + // util.inspect(err, { compact: true, breakLength: 1 }), + // '{ [Error: foo]\n bar:\n true }' + // ); + // assert.strictEqual( + // util.inspect(err2, { compact: true, breakLength: 5 }), + // '{ [Error: foo\nbar]\n bar: true }' + // ); + // assert.strictEqual( + // util.inspect(err, { compact: false }), + // '[Error: foo] {\n bar: true\n}' + // ); + // assert.strictEqual( + // util.inspect(err2, { compact: false }), + // '[Error: foo\nbar] {\n bar: true\n}' + // ); + + // Error.stackTraceLimit = tmp; +} + +// TODO(wafuwafu13): Fix +// // Prevent enumerable error properties from being printed. +// { +// let err = new Error(); +// err.message = 'foobar'; +// let out = util.inspect(err).split('\n'); +// assert.strictEqual(out[0], 'Error: foobar'); +// assert(out[out.length - 1].startsWith(' at ')); +// // Reset the error, the stack is otherwise not recreated. +// err = new Error(); +// err.message = 'foobar'; +// err.name = 'Unique'; +// Object.defineProperty(err, 'stack', { value: err.stack, enumerable: true }); +// out = util.inspect(err).split('\n'); +// assert.strictEqual(out[0], 'Unique: foobar'); +// assert(out[out.length - 1].startsWith(' at ')); +// err.name = 'Baz'; +// out = util.inspect(err).split('\n'); +// assert.strictEqual(out[0], 'Unique: foobar'); +// assert.strictEqual(out[out.length - 2], " name: 'Baz'"); +// assert.strictEqual(out[out.length - 1], '}'); +// } + +// // Doesn't capture stack trace. +{ + function BadCustomError(msg) { + Error.call(this); + Object.defineProperty(this, 'message', + { value: msg, enumerable: false }); + Object.defineProperty(this, 'name', + { value: 'BadCustomError', enumerable: false }); + } + Object.setPrototypeOf(BadCustomError.prototype, Error.prototype); + Object.setPrototypeOf(BadCustomError, Error); + assert.strictEqual( + util.inspect(new BadCustomError('foo')), + '[BadCustomError: foo]' + ); +} + +// TODO(wafuwafu13): Fix +// // Tampered error stack or name property (different type than string). +// // Note: Symbols are not supported by `Error#toString()` which is called by +// // accessing the `stack` property. +// [ +// [404, '404: foo', '[404]'], +// [0, '0: foo', '[RangeError: foo]'], +// [0n, '0: foo', '[RangeError: foo]'], +// [null, 'null: foo', '[RangeError: foo]'], +// [undefined, 'RangeError: foo', '[RangeError: foo]'], +// [false, 'false: foo', '[RangeError: foo]'], +// ['', 'foo', '[RangeError: foo]'], +// [[1, 2, 3], '1,2,3: foo', '[1,2,3]'], +// ].forEach(([value, outputStart, stack]) => { +// let err = new RangeError('foo'); +// err.name = value; +// assert( +// util.inspect(err).startsWith(outputStart), +// util.format( +// 'The name set to %o did not result in the expected output "%s"', +// value, +// outputStart +// ) +// ); + +// err = new RangeError('foo'); +// err.stack = value; +// assert.strictEqual(util.inspect(err), stack); +// }); + +// https://github.com/nodejs/node-v0.x-archive/issues/1941 +assert.strictEqual(util.inspect(Object.create(Date.prototype)), 'Date {}'); + +// https://github.com/nodejs/node-v0.x-archive/issues/1944 +{ + const d = new Date(); + d.toUTCString = null; + util.inspect(d); +} + +// TODO(wafuwafu13): Fix +// // Should not throw. +// { +// const d = new Date(); +// d.toISOString = null; +// util.inspect(d); +// } + +// TODO(wafuwafu13): Fix +// // Should not throw. +// { +// const r = /regexp/; +// r.toString = null; +// util.inspect(r); +// } + +// TODO(wafuwafu13): Fix +// // See https://github.com/nodejs/node-v0.x-archive/issues/2225 +// { +// const x = { [util.inspect.custom]: util.inspect }; +// assert(util.inspect(x).includes( +// '[Symbol(nodejs.util.inspect.custom)]: [Function: inspect] {\n')); +// } + +// TODO(wafuwafu13): Fix +// // `util.inspect` should display the escaped value of a key. +// { +// const w = { +// '\\': 1, +// '\\\\': 2, +// '\\\\\\': 3, +// '\\\\\\\\': 4, +// '\n': 5, +// '\r': 6 +// }; + +// const y = ['a', 'b', 'c']; +// y['\\\\'] = 'd'; +// y['\n'] = 'e'; +// y['\r'] = 'f'; + +// assert.strictEqual( +// util.inspect(w), +// "{ '\\\\': 1, '\\\\\\\\': 2, '\\\\\\\\\\\\': 3, " + +// "'\\\\\\\\\\\\\\\\': 4, '\\n': 5, '\\r': 6 }" +// ); +// assert.strictEqual( +// util.inspect(y), +// "[ 'a', 'b', 'c', '\\\\\\\\': 'd', " + +// "'\\n': 'e', '\\r': 'f' ]" +// ); +// } + +// Test util.inspect.styles and util.inspect.colors. +{ + function testColorStyle(style, input, implicit) { + const colorName = util.inspect.styles[style]; + let color = ['', '']; + if (util.inspect.colors[colorName]) + color = util.inspect.colors[colorName]; + + const withoutColor = util.inspect(input, false, 0, false); + const withColor = util.inspect(input, false, 0, true); + const expect = `\u001b[${color[0]}m${withoutColor}\u001b[${color[1]}m`; + assert.strictEqual( + withColor, + expect, + `util.inspect color for style ${style}`); + } + + testColorStyle('special', function() {}); + testColorStyle('number', 123.456); + testColorStyle('boolean', true); + testColorStyle('undefined', undefined); + testColorStyle('null', null); + testColorStyle('string', 'test string'); + testColorStyle('date', new Date()); + testColorStyle('regexp', /regexp/); +} + +// An object with "hasOwnProperty" overwritten should not throw. +util.inspect({ hasOwnProperty: null }); + +// New API, accepts an "options" object. +{ + const subject = { foo: 'bar', hello: 31, a: { b: { c: { d: 0 } } } }; + Object.defineProperty(subject, 'hidden', { enumerable: false, value: null }); + + assert.strictEqual( + util.inspect(subject, { showHidden: false }).includes('hidden'), + false + ); + assert.strictEqual( + util.inspect(subject, { showHidden: true }).includes('hidden'), + true + ); + assert.strictEqual( + util.inspect(subject, { colors: false }).includes('\u001b[32m'), + false + ); + assert.strictEqual( + util.inspect(subject, { colors: true }).includes('\u001b[32m'), + true + ); + assert.strictEqual( + util.inspect(subject, { depth: 2 }).includes('c: [Object]'), + true + ); + assert.strictEqual( + util.inspect(subject, { depth: 0 }).includes('a: [Object]'), + true + ); + assert.strictEqual( + util.inspect(subject, { depth: null }).includes('{ d: 0 }'), + true + ); + assert.strictEqual( + util.inspect(subject, { depth: undefined }).includes('{ d: 0 }'), + true + ); +} + +{ + // "customInspect" option can enable/disable calling [util.inspect.custom](). + const subject = { [util.inspect.custom]: () => 123 }; + + assert.strictEqual( + util.inspect(subject, { customInspect: true }).includes('123'), + true + ); + assert.strictEqual( + util.inspect(subject, { customInspect: true }).includes('inspect'), + false + ); + assert.strictEqual( + util.inspect(subject, { customInspect: false }).includes('123'), + false + ); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(subject, { customInspect: false }).includes('inspect'), + // true + // ); + + // A custom [util.inspect.custom]() should be able to return other Objects. + subject[util.inspect.custom] = () => ({ foo: 'bar' }); + + assert.strictEqual(util.inspect(subject), "{ foo: 'bar' }"); + + subject[util.inspect.custom] = common.mustCall((depth, opts) => { + const clone = { ...opts }; + // This might change at some point but for now we keep the stylize function. + // The function should either be documented or an alternative should be + // implemented. + assert.strictEqual(typeof opts.stylize, 'function'); + assert.strictEqual(opts.seen, undefined); + assert.strictEqual(opts.budget, undefined); + assert.strictEqual(opts.indentationLvl, undefined); + assert.strictEqual(opts.showHidden, false); + assert.deepStrictEqual( + new Set(Object.keys(util.inspect.defaultOptions).concat(['stylize'])), + new Set(Object.keys(opts)) + ); + opts.showHidden = true; + return { [util.inspect.custom]: common.mustCall((depth, opts2) => { + assert.deepStrictEqual(clone, opts2); + }) }; + }); + + util.inspect(subject); + + // util.inspect.custom is a shared symbol which can be accessed as + // Symbol.for("nodejs.util.inspect.custom"). + const inspect = Symbol.for('nodejs.util.inspect.custom'); + + subject[inspect] = () => ({ baz: 'quux' }); + + assert.strictEqual(util.inspect(subject), '{ baz: \'quux\' }'); + + subject[inspect] = (depth, opts) => { + assert.strictEqual(opts.customInspectOptions, true); + assert.strictEqual(opts.seen, null); + return {}; + }; + + util.inspect(subject, { customInspectOptions: true, seen: null }); +} + +{ + const subject = { [util.inspect.custom]: common.mustCall((depth, opts) => { + assert.strictEqual(depth, null); + assert.strictEqual(opts.compact, true); + }) }; + util.inspect(subject, { depth: null, compact: true }); +} + +// TODO(wafuwafu13): Fix +// { +// // Returning `this` from a custom inspection function works. +// const subject = { a: 123, [util.inspect.custom]() { return this; } }; +// const UIC = 'nodejs.util.inspect.custom'; +// assert.strictEqual( +// util.inspect(subject), +// `{\n a: 123,\n [Symbol(${UIC})]: [Function: [${UIC}]]\n}` +// ); +// } + +// Verify that it's possible to use the stylize function to manipulate input. +assert.strictEqual( + util.inspect([1, 2, 3], { stylize() { return 'x'; } }), + '[ x, x, x ]' +); + +// Using `util.inspect` with "colors" option should produce as many lines as +// without it. +{ + function testLines(input) { + const countLines = (str) => (str.match(/\n/g) || []).length; + const withoutColor = util.inspect(input); + const withColor = util.inspect(input, { colors: true }); + assert.strictEqual(countLines(withoutColor), countLines(withColor)); + } + + const bigArray = new Array(100).fill().map((value, index) => index); + + testLines([1, 2, 3, 4, 5, 6, 7]); + testLines(bigArray); + testLines({ foo: 'bar', baz: 35, b: { a: 35 } }); + testLines({ a: { a: 3, b: 1, c: 1, d: 1, e: 1, f: 1, g: 1, h: 1 }, b: 1 }); + testLines({ + foo: 'bar', + baz: 35, + b: { a: 35 }, + veryLongKey: 'very long value', + evenLongerKey: ['with even longer value in array'] + }); +} + +// Test boxed primitives output the correct values. +assert.strictEqual(util.inspect(new String('test')), "[String: 'test']"); +assert.strictEqual( + util.inspect(new String('test'), { colors: true }), + "\u001b[32m[String: 'test']\u001b[39m" +); +assert.strictEqual( + util.inspect(Object(Symbol('test'))), + '[Symbol: Symbol(test)]' +); +assert.strictEqual(util.inspect(new Boolean(false)), '[Boolean: false]'); +// TODO(wafuwafu13): Fix +// assert.strictEqual( +// util.inspect(Object.setPrototypeOf(new Boolean(true), null)), +// '[Boolean (null prototype): true]' +// ); +// assert.strictEqual(util.inspect(new Number(0)), '[Number: 0]'); +// assert.strictEqual( +// util.inspect( +// Object.defineProperty( +// Object.setPrototypeOf(new Number(-0), Array.prototype), +// Symbol.toStringTag, +// { value: 'Foobar' } +// ) +// ), +// '[Number (Array): -0] [Foobar]' +// ); +assert.strictEqual(util.inspect(new Number(-1.1)), '[Number: -1.1]'); +assert.strictEqual(util.inspect(new Number(13.37)), '[Number: 13.37]'); + +// Test boxed primitives with own properties. +{ + const str = new String('baz'); + str.foo = 'bar'; + assert.strictEqual(util.inspect(str), "[String: 'baz'] { foo: 'bar' }"); + + const bool = new Boolean(true); + bool.foo = 'bar'; + assert.strictEqual(util.inspect(bool), "[Boolean: true] { foo: 'bar' }"); + + const num = new Number(13.37); + num.foo = 'bar'; + assert.strictEqual(util.inspect(num), "[Number: 13.37] { foo: 'bar' }"); + + const sym = Object(Symbol('foo')); + sym.foo = 'bar'; + assert.strictEqual(util.inspect(sym), "[Symbol: Symbol(foo)] { foo: 'bar' }"); + + const big = Object(BigInt(55)); + big.foo = 'bar'; + assert.strictEqual(util.inspect(big), "[BigInt: 55n] { foo: 'bar' }"); +} + +// Test es6 Symbol. +if (typeof Symbol !== 'undefined') { + assert.strictEqual(util.inspect(Symbol()), 'Symbol()'); + assert.strictEqual(util.inspect(Symbol(123)), 'Symbol(123)'); + assert.strictEqual(util.inspect(Symbol('hi')), 'Symbol(hi)'); + assert.strictEqual(util.inspect([Symbol()]), '[ Symbol() ]'); + assert.strictEqual(util.inspect({ foo: Symbol() }), '{ foo: Symbol() }'); + + const options = { showHidden: true }; + let subject = {}; + + subject[Symbol('sym\nbol')] = 42; + + // TODO(wafuwafu13): Fix + // assert.strictEqual(util.inspect(subject), '{ [Symbol(sym\\nbol)]: 42 }'); + // assert.strictEqual( + // util.inspect(subject, options), + // '{ [Symbol(sym\\nbol)]: 42 }' + // ); + + // Object.defineProperty( + // subject, + // Symbol(), + // { enumerable: false, value: 'non-enum' }); + // assert.strictEqual(util.inspect(subject), '{ [Symbol(sym\\nbol)]: 42 }'); + // assert.strictEqual( + // util.inspect(subject, options), + // "{ [Symbol(sym\\nbol)]: 42, [Symbol()]: 'non-enum' }" + // ); + + // subject = [1, 2, 3]; + // subject[Symbol('symbol')] = 42; + + // assert.strictEqual(util.inspect(subject), + // '[ 1, 2, 3, [Symbol(symbol)]: 42 ]'); +} + +// Test Set. +{ + assert.strictEqual(util.inspect(new Set()), 'Set(0) {}'); + // TODO(wafuwafu13): Fix + // assert.strictEqual(util.inspect(new Set([1, 2, 3])), 'Set(3) { 1, 2, 3 }'); + // const set = new Set(['foo']); + // set.bar = 42; + // assert.strictEqual( + // util.inspect(set, { showHidden: true }), + // "Set(1) { 'foo', bar: 42 }" + // ); +} + +// TODO(wafuwafu13): Fix +// // Test circular Set. +// { +// const set = new Set(); +// set.add(set); +// assert.strictEqual(util.inspect(set), ' Set(1) { [Circular *1] }'); +// } + +// Test Map. +{ + assert.strictEqual(util.inspect(new Map()), 'Map(0) {}'); + assert.strictEqual(util.inspect(new Map([[1, 'a'], [2, 'b'], [3, 'c']])), + "Map(3) { 1 => 'a', 2 => 'b', 3 => 'c' }"); + const map = new Map([['foo', null]]); + map.bar = 42; + assert.strictEqual(util.inspect(map, true), + "Map(1) { 'foo' => null, bar: 42 }"); +} + +// Test circular Map. +{ + const map = new Map(); + map.set(map, 'map'); + assert.strictEqual( + inspect(map), + " Map(1) { [Circular *1] => 'map' }" + ); + map.set(map, map); + assert.strictEqual( + inspect(map), + ' Map(1) { [Circular *1] => [Circular *1] }' + ); + map.delete(map); + map.set('map', map); + assert.strictEqual( + inspect(map), + " Map(1) { 'map' => [Circular *1] }" + ); +} + +// Test multiple circular references. +{ + const obj = {}; + obj.a = [obj]; + obj.b = {}; + obj.b.inner = obj.b; + obj.b.obj = obj; + + assert.strictEqual( + inspect(obj), + ' {\n' + + ' a: [ [Circular *1] ],\n' + + ' b: { inner: [Circular *2], obj: [Circular *1] }\n' + + '}' + ); +} + +// TODO(wafuwafu13): Fix +// // Test Promise. +// { +// const resolved = Promise.resolve(3); +// assert.strictEqual(util.inspect(resolved), 'Promise { 3 }'); + +// const rejected = Promise.reject(3); +// assert.strictEqual(util.inspect(rejected), 'Promise { 3 }'); +// // Squelch UnhandledPromiseRejection. +// rejected.catch(() => {}); + +// const pending = new Promise(() => {}); +// assert.strictEqual(util.inspect(pending), 'Promise { }'); + +// const promiseWithProperty = Promise.resolve('foo'); +// promiseWithProperty.bar = 42; +// assert.strictEqual(util.inspect(promiseWithProperty), +// "Promise { 'foo', bar: 42 }"); +// } + +// Make sure it doesn't choke on polyfills. Unlike Set/Map, there is no standard +// interface to synchronously inspect a Promise, so our techniques only work on +// a bonafide native Promise. +{ + const oldPromise = Promise; + global.Promise = function() { this.bar = 42; }; + assert.strictEqual(util.inspect(new Promise()), '{ bar: 42 }'); + global.Promise = oldPromise; +} + +// TODO(wafuwafu13): Fix +// // Test Map iterators. +// { +// const map = new Map([['foo', 'bar']]); +// assert.strictEqual(util.inspect(map.keys()), '[Map Iterator] { \'foo\' }'); +// const mapValues = map.values(); +// Object.defineProperty(mapValues, Symbol.toStringTag, { value: 'Foo' }); +// assert.strictEqual( +// util.inspect(mapValues), +// '[Foo] [Map Iterator] { \'bar\' }' +// ); +// map.set('A', 'B!'); +// assert.strictEqual(util.inspect(map.entries(), { maxArrayLength: 1 }), +// "[Map Entries] { [ 'foo', 'bar' ], ... 1 more item }"); +// // Make sure the iterator doesn't get consumed. +// const keys = map.keys(); +// assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo', 'A' }"); +// assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo', 'A' }"); +// keys.extra = true; +// assert.strictEqual( +// util.inspect(keys, { maxArrayLength: 0 }), +// '[Map Iterator] { ... 2 more items, extra: true }'); +// } + +// TODO(wafuwafu13): Fix +// // Test Set iterators. +// { +// const aSet = new Set([1]); +// assert.strictEqual(util.inspect(aSet.entries(), { compact: false }), +// '[Set Entries] {\n [\n 1,\n 1\n ]\n}'); +// aSet.add(3); +// assert.strictEqual(util.inspect(aSet.keys()), '[Set Iterator] { 1, 3 }'); +// assert.strictEqual(util.inspect(aSet.values()), '[Set Iterator] { 1, 3 }'); +// const setEntries = aSet.entries(); +// Object.defineProperty(setEntries, Symbol.toStringTag, { value: 'Foo' }); +// assert.strictEqual(util.inspect(setEntries), +// '[Foo] [Set Entries] { [ 1, 1 ], [ 3, 3 ] }'); +// // Make sure the iterator doesn't get consumed. +// const keys = aSet.keys(); +// Object.defineProperty(keys, Symbol.toStringTag, { value: null }); +// assert.strictEqual(util.inspect(keys), '[Set Iterator] { 1, 3 }'); +// assert.strictEqual(util.inspect(keys), '[Set Iterator] { 1, 3 }'); +// keys.extra = true; +// assert.strictEqual( +// util.inspect(keys, { maxArrayLength: 1 }), +// '[Set Iterator] { 1, ... 1 more item, extra: true }'); +// } + +// Minimal inspection should still return as much information as possible about +// the constructor and Symbol.toStringTag. +{ + class Foo { + get [Symbol.toStringTag]() { + return 'ABC'; + } + } + const a = new Foo(); + assert.strictEqual(inspect(a, { depth: -1 }), 'Foo [ABC] {}'); + a.foo = true; + assert.strictEqual(inspect(a, { depth: -1 }), '[Foo [ABC]]'); + Object.defineProperty(a, Symbol.toStringTag, { + value: 'Foo', + configurable: true, + writable: true + }); + assert.strictEqual(inspect(a, { depth: -1 }), '[Foo]'); + delete a[Symbol.toStringTag]; + Object.setPrototypeOf(a, null); + // TODO(wafuwafu13): Fix + // assert.strictEqual(inspect(a, { depth: -1 }), '[Foo: null prototype]'); + // delete a.foo; + // assert.strictEqual(inspect(a, { depth: -1 }), '[Foo: null prototype] {}'); + // Object.defineProperty(a, Symbol.toStringTag, { + // value: 'ABC', + // configurable: true + // }); + // assert.strictEqual( + // inspect(a, { depth: -1 }), + // '[Foo: null prototype] [ABC] {}' + // ); + // Object.defineProperty(a, Symbol.toStringTag, { + // value: 'Foo', + // configurable: true + // }); + // assert.strictEqual( + // inspect(a, { depth: -1 }), + // '[Object: null prototype] [Foo] {}' + // ); +} + +// Test alignment of items in container. +// Assumes that the first numeric character is the start of an item. +{ + function checkAlignment(container, start, lineX, end) { + const lines = util.inspect(container).split('\n'); + lines.forEach((line, i) => { + if (i === 0) { + assert.strictEqual(line, start); + } else if (i === lines.length - 1) { + assert.strictEqual(line, end); + } else { + let expected = lineX.replace('X', i - 1); + if (i !== lines.length - 2) + expected += ','; + assert.strictEqual(line, expected); + } + }); + } + + const bigArray = []; + for (let i = 0; i < 100; i++) { + bigArray.push(i); + } + + const obj = {}; + bigArray.forEach((prop) => { + obj[prop] = null; + }); + + checkAlignment(obj, '{', " 'X': null", '}'); + // TODO(wafuwafu13): Fix + // checkAlignment(new Set(bigArray), 'Set(100) {', ' X', '}'); + checkAlignment( + new Map(bigArray.map((number) => [number, null])), + 'Map(100) {', ' X => null', '}' + ); +} + + +// Test display of constructors. +{ + class ObjectSubclass {} + class ArraySubclass extends Array {} + class SetSubclass extends Set {} + class MapSubclass extends Map {} + class PromiseSubclass extends Promise {} + + const x = new ObjectSubclass(); + x.foo = 42; + assert.strictEqual(util.inspect(x), + 'ObjectSubclass { foo: 42 }'); + assert.strictEqual(util.inspect(new ArraySubclass(1, 2, 3)), + 'ArraySubclass(3) [ 1, 2, 3 ]'); + // TODO(wafuwafu13): Fix + // assert.strictEqual(util.inspect(new SetSubclass([1, 2, 3])), + // 'SetSubclass(3) [Set] { 1, 2, 3 }'); + assert.strictEqual(util.inspect(new MapSubclass([['foo', 42]])), + "MapSubclass(1) [Map] { 'foo' => 42 }"); + // TODO(wafuwafu13): Fix + // assert.strictEqual(util.inspect(new PromiseSubclass(() => {})), + // 'PromiseSubclass [Promise] { }'); + assert.strictEqual( + util.inspect({ a: { b: new ArraySubclass([1, [2], 3]) } }, { depth: 1 }), + '{ a: { b: [ArraySubclass] } }' + ); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(Object.setPrototypeOf(x, null)), + // '[ObjectSubclass: null prototype] { foo: 42 }' + // ); +} + +// Empty and circular before depth. +{ + const arr = [[[[]]]]; + assert.strictEqual(util.inspect(arr), '[ [ [ [] ] ] ]'); + arr[0][0][0][0] = []; + assert.strictEqual(util.inspect(arr), '[ [ [ [Array] ] ] ]'); + arr[0][0][0] = {}; + assert.strictEqual(util.inspect(arr), '[ [ [ {} ] ] ]'); + arr[0][0][0] = { a: 2 }; + assert.strictEqual(util.inspect(arr), '[ [ [ [Object] ] ] ]'); + arr[0][0][0] = arr; + assert.strictEqual(util.inspect(arr), ' [ [ [ [Circular *1] ] ] ]'); + arr[0][0][0] = arr[0][0]; + assert.strictEqual(util.inspect(arr), '[ [ [ [Circular *1] ] ] ]'); +} + +// Corner cases. +{ + const x = { constructor: 42 }; + assert.strictEqual(util.inspect(x), '{ constructor: 42 }'); +} + +{ + const x = {}; + Object.defineProperty(x, 'constructor', { + get: function() { + throw new Error('should not access constructor'); + }, + enumerable: true + }); + assert.strictEqual(util.inspect(x), '{ constructor: [Getter] }'); +} + +{ + const x = new function() {}; // eslint-disable-line new-parens + assert.strictEqual(util.inspect(x), '{}'); +} + +{ + const x = Object.create(null); + assert.strictEqual(util.inspect(x), '[Object: null prototype] {}'); +} + +// TODO(wafuwafu13): Fix +// { +// const x = []; +// x[''] = 1; +// assert.strictEqual(util.inspect(x), "[ '': 1 ]"); +// } + +// TODO(wafuwafu13): Fix +// // The following maxArrayLength tests were introduced after v6.0.0 was released. +// // Do not backport to v5/v4 unless all of +// // https://github.com/nodejs/node/pull/6334 is backported. +// { +// const x = new Array(101).fill(); +// assert(util.inspect(x).endsWith('1 more item\n]')); +// assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item\n]')); +// assert.strictEqual( +// util.inspect(x, { maxArrayLength: -1 }), +// '[ ... 101 more items ]' +// ); +// assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }), +// '[ ... 101 more items ]'); +// } + +{ + const x = Array(101); + assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }), + '[ ... 101 more items ]'); + assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item\n]')); + assert(!util.inspect( + x, { maxArrayLength: Infinity } + ).endsWith('1 more item ]')); +} + +{ + const x = new Uint8Array(101); + // TODO(wafuwafu13): Fix + // assert(util.inspect(x).endsWith('1 more item\n]')); + assert(!util.inspect(x, { maxArrayLength: 101 }).includes('1 more item')); + // TODO(wafuwafu13): Fix + // assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }), + // 'Uint8Array(101) [ ... 101 more items ]'); + assert(!util.inspect(x, { maxArrayLength: null }).includes('1 more item')); + // TODO(wafuwafu13): Fix + // assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0, 0\n]')); +} + +{ + const obj = { foo: 'abc', bar: 'xyz' }; + const oneLine = util.inspect(obj, { breakLength: Infinity }); + // Subtract four for the object's two curly braces and two spaces of padding. + // Add one more to satisfy the strictly greater than condition in the code. + const breakpoint = oneLine.length - 5; + const twoLines = util.inspect(obj, { breakLength: breakpoint }); + + assert.strictEqual(oneLine, "{ foo: 'abc', bar: 'xyz' }"); + assert.strictEqual( + util.inspect(obj, { breakLength: breakpoint + 1 }), + twoLines + ); + assert.strictEqual(twoLines, "{\n foo: 'abc',\n bar: 'xyz'\n}"); +} + +// util.inspect.defaultOptions tests. +{ + const arr = new Array(101).fill(); + const obj = { a: { a: { a: { a: 1 } } } }; + + const oldOptions = { ...util.inspect.defaultOptions }; + + // Set single option through property assignment. + util.inspect.defaultOptions.maxArrayLength = null; + assert.doesNotMatch(util.inspect(arr), /1 more item/); + util.inspect.defaultOptions.maxArrayLength = oldOptions.maxArrayLength; + // TODO(wafuwafu13): Fix + // assert.match(util.inspect(arr), /1 more item/); + util.inspect.defaultOptions.depth = null; + assert.doesNotMatch(util.inspect(obj), /Object/); + util.inspect.defaultOptions.depth = oldOptions.depth; + assert.match(util.inspect(obj), /Object/); + assert.strictEqual( + JSON.stringify(util.inspect.defaultOptions), + JSON.stringify(oldOptions) + ); + + // Set multiple options through object assignment. + util.inspect.defaultOptions = { maxArrayLength: null, depth: 2 }; + assert.doesNotMatch(util.inspect(arr), /1 more item/); + assert.match(util.inspect(obj), /Object/); + util.inspect.defaultOptions = oldOptions; + // assert.match(util.inspect(arr), /1 more item/); + assert.match(util.inspect(obj), /Object/); + assert.strictEqual( + JSON.stringify(util.inspect.defaultOptions), + JSON.stringify(oldOptions) + ); + + assert.throws(() => { + util.inspect.defaultOptions = null; + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options" argument must be of type object. ' + + 'Received null' + } + ); + + assert.throws(() => { + util.inspect.defaultOptions = 'bad'; + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options" argument must be of type object. ' + + "Received type string ('bad')" + } + ); +} + +util.inspect(process); + +// TODO(wafuwafu13): Fix +// // Setting custom inspect property to a non-function should do nothing. +// { +// const obj = { [util.inspect.custom]: 'fhqwhgads' }; +// assert.strictEqual( +// util.inspect(obj), +// "{ [Symbol(nodejs.util.inspect.custom)]: 'fhqwhgads' }" +// ); +// } + +{ + // @@toStringTag + const obj = { [Symbol.toStringTag]: 'a' }; + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(obj), + // "{ [Symbol(Symbol.toStringTag)]: 'a' }" + // ); + Object.defineProperty(obj, Symbol.toStringTag, { + value: 'a', + enumerable: false + }); + assert.strictEqual(util.inspect(obj), 'Object [a] {}'); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // util.inspect(obj, { showHidden: true }), + // "{ [Symbol(Symbol.toStringTag)]: 'a' }" + // ); + + class Foo { + constructor() { + this.foo = 'bar'; + } + + get [Symbol.toStringTag]() { + return this.foo; + } + } + + // TODO(wafuwafu13): Fix + // assert.strictEqual(util.inspect( + // Object.create(null, { [Symbol.toStringTag]: { value: 'foo' } })), + // '[Object: null prototype] [foo] {}'); + + assert.strictEqual(util.inspect(new Foo()), "Foo [bar] { foo: 'bar' }"); + + assert.strictEqual( + util.inspect(new (class extends Foo {})()), + "Foo [bar] { foo: 'bar' }"); + + assert.strictEqual( + util.inspect(Object.create(Object.create(Foo.prototype), { + foo: { value: 'bar', enumerable: true } + })), + "Foo [bar] { foo: 'bar' }"); + + class ThrowingClass { + get [Symbol.toStringTag]() { + throw new Error('toStringTag error'); + } + } + + assert.throws(() => util.inspect(new ThrowingClass()), /toStringTag error/); + + class NotStringClass { + get [Symbol.toStringTag]() { + return null; + } + } + + assert.strictEqual(util.inspect(new NotStringClass()), + 'NotStringClass {}'); +} + +{ + const o = { + a: [1, 2, [[ + 'Lorem ipsum dolor\nsit amet,\tconsectetur adipiscing elit, sed do ' + + 'eiusmod tempor incididunt ut labore et dolore magna aliqua.', + 'test', + 'foo']], 4], + b: new Map([['za', 1], ['zb', 'test']]) + }; + + let out = util.inspect(o, { compact: true, depth: 5, breakLength: 80 }); + let expect = [ + '{ a:', + ' [ 1,', + ' 2,', + " [ [ 'Lorem ipsum dolor\\nsit amet,\\tconsectetur adipiscing elit, " + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',", + " 'test',", + " 'foo' ] ],", + ' 4 ],', + " b: Map(2) { 'za' => 1, 'zb' => 'test' } }", + ].join('\n'); + assert.strictEqual(out, expect); + + out = util.inspect(o, { compact: false, depth: 5, breakLength: 60 }); + expect = [ + '{', + ' a: [', + ' 1,', + ' 2,', + ' [', + ' [', + " 'Lorem ipsum dolor\\n' +", + " 'sit amet,\\tconsectetur adipiscing elit, sed do eiusmod " + + "tempor incididunt ut labore et dolore magna aliqua.',", + " 'test',", + " 'foo'", + ' ]', + ' ],', + ' 4', + ' ],', + ' b: Map(2) {', + " 'za' => 1,", + " 'zb' => 'test'", + ' }', + '}', + ].join('\n'); + assert.strictEqual(out, expect); + + out = util.inspect(o.a[2][0][0], { compact: false, breakLength: 30 }); + expect = [ + "'Lorem ipsum dolor\\n' +", + " 'sit amet,\\tconsectetur adipiscing elit, sed do eiusmod tempor " + + "incididunt ut labore et dolore magna aliqua.'", + ].join('\n'); + assert.strictEqual(out, expect); + + out = util.inspect( + '12345678901234567890123456789012345678901234567890', + { compact: false, breakLength: 3 }); + expect = "'12345678901234567890123456789012345678901234567890'"; + assert.strictEqual(out, expect); + + out = util.inspect( + '12 45 78 01 34 67 90 23 56 89 123456789012345678901234567890', + { compact: false, breakLength: 3 }); + expect = [ + "'12 45 78 01 34 67 90 23 56 89 123456789012345678901234567890'", + ].join('\n'); + assert.strictEqual(out, expect); + + // TODO(wafuwafu13): Fix + o.a = () => {}; + o.b = new Number(3); + out = util.inspect(o, { compact: false, breakLength: 3 }); + expect = [ + '{', + ' a: [Function (anonymous)],', + ' b: [Number: 3]', + '}', + ].join('\n'); + assert.strictEqual(out, expect); + + out = util.inspect(o, { compact: false, breakLength: 3, showHidden: true }); + expect = [ + '{', + ' a: [Function (anonymous)] {', + ' [length]: 0,', + " [name]: ''", + ' },', + ' b: [Number: 3]', + '}', + ].join('\n'); + assert.strictEqual(out, expect); + + o[util.inspect.custom] = () => 42; + out = util.inspect(o, { compact: false, breakLength: 3 }); + expect = '42'; + assert.strictEqual(out, expect); + + o[util.inspect.custom] = () => '12 45 78 01 34 67 90 23'; + out = util.inspect(o, { compact: false, breakLength: 3 }); + expect = '12 45 78 01 34 67 90 23'; + assert.strictEqual(out, expect); + + o[util.inspect.custom] = () => ({ a: '12 45 78 01 34 67 90 23' }); + out = util.inspect(o, { compact: false, breakLength: 3 }); + expect = "{\n a: '12 45 78 01 34 67 90 23'\n}"; + assert.strictEqual(out, expect); +} + +// TODO(wafuwafu13): Fix +// // Check compact indentation. +// { +// const typed = new Uint8Array(); +// typed.buffer.foo = true; +// const set = new Set([[1, 2]]); +// const promise = Promise.resolve([[1, set]]); +// const map = new Map([[promise, typed]]); +// map.set(set.values(), map.values()); + +// let out = util.inspect(map, { compact: false, showHidden: true, depth: 9 }); +// let expected = [ +// 'Map(2) {', +// ' Promise {', +// ' [', +// ' [', +// ' 1,', +// ' Set(1) {', +// ' [', +// ' 1,', +// ' 2,', +// ' [length]: 2', +// ' ]', +// ' },', +// ' [length]: 2', +// ' ],', +// ' [length]: 1', +// ' ]', +// ' } => Uint8Array(0) [', +// ' [BYTES_PER_ELEMENT]: 1,', +// ' [length]: 0,', +// ' [byteLength]: 0,', +// ' [byteOffset]: 0,', +// ' [buffer]: ArrayBuffer {', +// ' byteLength: 0,', +// ' foo: true', +// ' }', +// ' ],', +// ' [Set Iterator] {', +// ' [', +// ' 1,', +// ' 2,', +// ' [length]: 2', +// ' ],', +// " [Symbol(Symbol.toStringTag)]: 'Set Iterator'", +// ' } => [Map Iterator] {', +// ' Uint8Array(0) [', +// ' [BYTES_PER_ELEMENT]: 1,', +// ' [length]: 0,', +// ' [byteLength]: 0,', +// ' [byteOffset]: 0,', +// ' [buffer]: ArrayBuffer {', +// ' byteLength: 0,', +// ' foo: true', +// ' }', +// ' ],', +// ' [Circular *1],', +// " [Symbol(Symbol.toStringTag)]: 'Map Iterator'", +// ' }', +// '}', +// ].join('\n'); + +// assert.strict.equal(out, expected); + +// out = util.inspect(map, { compact: 2, showHidden: true, depth: 9 }); + +// expected = [ +// 'Map(2) {', +// ' Promise {', +// ' [', +// ' [', +// ' 1,', +// ' Set(1) { [ 1, 2, [length]: 2 ] },', +// ' [length]: 2', +// ' ],', +// ' [length]: 1', +// ' ]', +// ' } => Uint8Array(0) [', +// ' [BYTES_PER_ELEMENT]: 1,', +// ' [length]: 0,', +// ' [byteLength]: 0,', +// ' [byteOffset]: 0,', +// ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', +// ' ],', +// ' [Set Iterator] {', +// ' [ 1, 2, [length]: 2 ],', +// " [Symbol(Symbol.toStringTag)]: 'Set Iterator'", +// ' } => [Map Iterator] {', +// ' Uint8Array(0) [', +// ' [BYTES_PER_ELEMENT]: 1,', +// ' [length]: 0,', +// ' [byteLength]: 0,', +// ' [byteOffset]: 0,', +// ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', +// ' ],', +// ' [Circular *1],', +// " [Symbol(Symbol.toStringTag)]: 'Map Iterator'", +// ' }', +// '}', +// ].join('\n'); + +// assert.strict.equal(out, expected); + +// out = util.inspect(map, { +// showHidden: true, depth: 9, breakLength: 4, compact: true +// }); +// expected = [ +// 'Map(2) {', +// ' Promise {', +// ' [ [ 1,', +// ' Set(1) {', +// ' [ 1,', +// ' 2,', +// ' [length]: 2 ] },', +// ' [length]: 2 ],', +// ' [length]: 1 ] } => Uint8Array(0) [', +// ' [BYTES_PER_ELEMENT]: 1,', +// ' [length]: 0,', +// ' [byteLength]: 0,', +// ' [byteOffset]: 0,', +// ' [buffer]: ArrayBuffer {', +// ' byteLength: 0,', +// ' foo: true } ],', +// ' [Set Iterator] {', +// ' [ 1,', +// ' 2,', +// ' [length]: 2 ],', +// ' [Symbol(Symbol.toStringTag)]:', +// " 'Set Iterator' } => [Map Iterator] {", +// ' Uint8Array(0) [', +// ' [BYTES_PER_ELEMENT]: 1,', +// ' [length]: 0,', +// ' [byteLength]: 0,', +// ' [byteOffset]: 0,', +// ' [buffer]: ArrayBuffer {', +// ' byteLength: 0,', +// ' foo: true } ],', +// ' [Circular *1],', +// ' [Symbol(Symbol.toStringTag)]:', +// " 'Map Iterator' } }", +// ].join('\n'); + +// assert.strict.equal(out, expected); +// } + +// TODO(wafuwafu13): Fix +// { // Test WeakMap && WeakSet +// const obj = {}; +// const arr = []; +// const weakMap = new WeakMap([[obj, arr], [arr, obj]]); +// let out = util.inspect(weakMap, { showHidden: true }); +// let expect = 'WeakMap { [ [length]: 0 ] => {}, {} => [ [length]: 0 ] }'; +// assert.strictEqual(out, expect); + +// out = util.inspect(weakMap); +// expect = 'WeakMap { }'; +// assert.strictEqual(out, expect); + +// out = util.inspect(weakMap, { maxArrayLength: 0, showHidden: true }); +// expect = 'WeakMap { ... 2 more items }'; +// assert.strictEqual(out, expect); + +// weakMap.extra = true; +// out = util.inspect(weakMap, { maxArrayLength: 1, showHidden: true }); +// // It is not possible to determine the output reliable. +// expect = 'WeakMap { [ [length]: 0 ] => {}, ... 1 more item, extra: true }'; +// let expectAlt = 'WeakMap { {} => [ [length]: 0 ], ... 1 more item, ' + +// 'extra: true }'; +// assert(out === expect || out === expectAlt, +// `Found: "${out}"\nrather than: "${expect}"\nor: "${expectAlt}"`); + +// // Test WeakSet +// arr.push(1); +// const weakSet = new WeakSet([obj, arr]); +// out = util.inspect(weakSet, { showHidden: true }); +// expect = 'WeakSet { [ 1, [length]: 1 ], {} }'; +// assert.strictEqual(out, expect); + +// out = util.inspect(weakSet); +// expect = 'WeakSet { }'; +// assert.strictEqual(out, expect); + +// out = util.inspect(weakSet, { maxArrayLength: -2, showHidden: true }); +// expect = 'WeakSet { ... 2 more items }'; +// assert.strictEqual(out, expect); + +// weakSet.extra = true; +// out = util.inspect(weakSet, { maxArrayLength: 1, showHidden: true }); +// // It is not possible to determine the output reliable. +// expect = 'WeakSet { {}, ... 1 more item, extra: true }'; +// expectAlt = 'WeakSet { [ 1, [length]: 1 ], ... 1 more item, extra: true }'; +// assert(out === expect || out === expectAlt, +// `Found: "${out}"\nrather than: "${expect}"\nor: "${expectAlt}"`); +// // Keep references to the WeakMap entries, otherwise they could be GCed too +// // early. +// assert(obj && arr); +// } + +{ // Test argument objects. + const args = (function() { return arguments; })('a'); + assert.strictEqual(util.inspect(args), "[Arguments] { '0': 'a' }"); +} + +{ + // Test that a long linked list can be inspected without throwing an error. + const list = {}; + let head = list; + // A linked list of length 100k should be inspectable in some way, even though + // the real cutoff value is much lower than 100k. + for (let i = 0; i < 100000; i++) + head = head.next = {}; + assert.strictEqual( + util.inspect(list), + '{ next: { next: { next: [Object] } } }' + ); + const longList = util.inspect(list, { depth: Infinity }); + const match = longList.match(/next/g); + assert(match.length > 500 && match.length < 10000); + // TODO(wafuwafu13): Fix + // assert(longList.includes('[Object: Inspection interrupted ' + + // 'prematurely. Maximum call stack size exceeded.]')); +} + +// Do not escape single quotes if no double quote or backtick is present. +assert.strictEqual(util.inspect("'"), '"\'"'); +assert.strictEqual(util.inspect('"\''), '`"\'`'); +// eslint-disable-next-line no-template-curly-in-string +assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); + +// TODO(wafuwafu13): Fix +// // Errors should visualize as much information as possible. +// // If the name is not included in the stack, visualize it as well. +// [ +// [class Foo extends TypeError {}, 'test'], +// [class Foo extends TypeError {}, undefined], +// [class BarError extends Error {}, 'test'], +// [class BazError extends Error { +// get name() { +// return 'BazError'; +// } +// }, undefined], +// ].forEach(([Class, message], i) => { +// console.log('Test %i', i); +// const foo = new Class(message); +// const name = foo.name; +// const extra = Class.name.includes('Error') ? '' : ` [${foo.name}]`; +// assert( +// util.inspect(foo).startsWith( +// `${Class.name}${extra}${message ? `: ${message}` : '\n'}`), +// util.inspect(foo) +// ); +// Object.defineProperty(foo, Symbol.toStringTag, { +// value: 'WOW', +// writable: true, +// configurable: true +// }); +// const stack = foo.stack; +// foo.stack = 'This is a stack'; +// assert.strictEqual( +// util.inspect(foo), +// '[This is a stack]' +// ); +// foo.stack = stack; +// assert( +// util.inspect(foo).startsWith( +// `${Class.name} [WOW]${extra}${message ? `: ${message}` : '\n'}`), +// util.inspect(foo) +// ); +// Object.setPrototypeOf(foo, null); +// assert( +// util.inspect(foo).startsWith( +// `[${name}: null prototype] [WOW]${message ? `: ${message}` : '\n'}` +// ), +// util.inspect(foo) +// ); +// foo.bar = true; +// delete foo[Symbol.toStringTag]; +// assert( +// util.inspect(foo).startsWith( +// `[${name}: null prototype]${message ? `: ${message}` : '\n'}`), +// util.inspect(foo) +// ); +// foo.stack = 'This is a stack'; +// assert.strictEqual( +// util.inspect(foo), +// '[[Error: null prototype]: This is a stack] { bar: true }' +// ); +// foo.stack = stack.split('\n')[0]; +// assert.strictEqual( +// util.inspect(foo), +// `[[${name}: null prototype]${message ? `: ${message}` : ''}] { bar: true }` +// ); +// }); + +// TODO(wafuwafu13): Fix +// // Verify that classes are properly inspected. +// [ +// /* eslint-disable spaced-comment, no-multi-spaces, brace-style */ +// // The whitespace is intentional. +// [class { }, '[class (anonymous)]'], +// [class extends Error { log() {} }, '[class (anonymous) extends Error]'], +// [class A { constructor(a) { this.a = a; } log() { return this.a; } }, +// '[class A]'], +// [class +// // Random { // comments /* */ are part of the toString() result +// /* eslint-disable-next-line space-before-blocks */ +// äß/**/extends/*{*/TypeError{}, '[class äß extends TypeError]'], +// /* The whitespace and new line is intended! */ +// // Foobar !!! +// [class X extends /****/ Error +// // More comments +// {}, '[class X extends Error]'], +// /* eslint-enable spaced-comment, no-multi-spaces, brace-style */ +// ].forEach(([clazz, string]) => { +// const inspected = util.inspect(clazz); +// assert.strictEqual(inspected, string); +// Object.defineProperty(clazz, Symbol.toStringTag, { +// value: 'Woohoo' +// }); +// const parts = inspected.slice(0, -1).split(' '); +// const [, name, ...rest] = parts; +// rest.unshift('[Woohoo]'); +// if (rest.length) { +// rest[rest.length - 1] += ']'; +// } +// assert.strictEqual( +// util.inspect(clazz), +// ['[class', name, ...rest].join(' ') +// ); +// if (rest.length) { +// rest[rest.length - 1] = rest[rest.length - 1].slice(0, -1); +// rest.length = 1; +// } +// Object.setPrototypeOf(clazz, Map.prototype); +// assert.strictEqual( +// util.inspect(clazz), +// ['[class', name, '[Map]', ...rest].join(' ') + ']' +// ); +// Object.setPrototypeOf(clazz, null); +// assert.strictEqual( +// util.inspect(clazz), +// ['[class', name, ...rest, 'extends [null prototype]]'].join(' ') +// ); +// Object.defineProperty(clazz, 'name', { value: 'Foo' }); +// const res = ['[class', 'Foo', ...rest, 'extends [null prototype]]'].join(' '); +// assert.strictEqual(util.inspect(clazz), res); +// clazz.foo = true; +// assert.strictEqual(util.inspect(clazz), `${res} { foo: true }`); +// }); + +// "class" properties should not be detected as "class". +{ + // eslint-disable-next-line space-before-function-paren + let obj = { class () {} }; + assert.strictEqual( + util.inspect(obj), + '{ class: [Function: class] }' + ); + obj = { class: () => {} }; + assert.strictEqual( + util.inspect(obj), + '{ class: [Function: class] }' + ); + obj = { ['class Foo {}']() {} }; + assert.strictEqual( + util.inspect(obj), + "{ 'class Foo {}': [Function: class Foo {}] }" + ); + function Foo() {} + Object.defineProperty(Foo, 'toString', { value: () => 'class Foo {}' }); + assert.strictEqual( + util.inspect(Foo), + '[Function: Foo]' + ); + function fn() {} + Object.defineProperty(fn, 'name', { value: 'class Foo {}' }); + assert.strictEqual( + util.inspect(fn), + '[Function: class Foo {}]' + ); +} + +// TODO(wafuwafu13): Fix +// // Verify that throwing in valueOf and toString still produces nice results. +// [ +// [new String(55), "[String: '55']"], +// [new Boolean(true), '[Boolean: true]'], +// [new Number(55), '[Number: 55]'], +// [Object(BigInt(55)), '[BigInt: 55n]'], +// [Object(Symbol('foo')), '[Symbol: Symbol(foo)]'], +// [function() {}, '[Function (anonymous)]'], +// [() => {}, '[Function (anonymous)]'], +// [[1, 2], '[ 1, 2 ]'], +// [[, , 5, , , , ], '[ <2 empty items>, 5, <3 empty items> ]'], +// [{ a: 5 }, '{ a: 5 }'], +// [new Set([1, 2]), 'Set(2) { 1, 2 }'], +// [new Map([[1, 2]]), 'Map(1) { 1 => 2 }'], +// [new Set([1, 2]).entries(), '[Set Entries] { [ 1, 1 ], [ 2, 2 ] }'], +// [new Map([[1, 2]]).keys(), '[Map Iterator] { 1 }'], +// [new Date(2000), '1970-01-01T00:00:02.000Z'], +// [new Uint8Array(2), 'Uint8Array(2) [ 0, 0 ]'], +// [new Promise((resolve) => setTimeout(resolve, 10)), 'Promise { }'], +// [new WeakSet(), 'WeakSet { }'], +// [new WeakMap(), 'WeakMap { }'], +// [/foobar/g, '/foobar/g'], +// ].forEach(([value, expected]) => { +// Object.defineProperty(value, 'valueOf', { +// get() { +// throw new Error('valueOf'); +// } +// }); +// Object.defineProperty(value, 'toString', { +// get() { +// throw new Error('toString'); +// } +// }); +// assert.strictEqual(util.inspect(value), expected); +// value.foo = 'bar'; +// assert.notStrictEqual(util.inspect(value), expected); +// delete value.foo; +// value[Symbol('foo')] = 'yeah'; +// assert.notStrictEqual(util.inspect(value), expected); +// }); + +// TODO(wafuwafu13): Fix +// // Verify that having no prototype still produces nice results. +// [ +// [[1, 3, 4], '[Array(3): null prototype] [ 1, 3, 4 ]'], +// [new Set([1, 2]), '[Set(2): null prototype] { 1, 2 }'], +// [new Map([[1, 2]]), '[Map(1): null prototype] { 1 => 2 }'], +// [new Promise((resolve) => setTimeout(resolve, 10)), +// '[Promise: null prototype] { }'], +// [new WeakSet(), '[WeakSet: null prototype] { }'], +// [new WeakMap(), '[WeakMap: null prototype] { }'], +// [new Uint8Array(2), '[Uint8Array(2): null prototype] [ 0, 0 ]'], +// [new Uint16Array(2), '[Uint16Array(2): null prototype] [ 0, 0 ]'], +// [new Uint32Array(2), '[Uint32Array(2): null prototype] [ 0, 0 ]'], +// [new Int8Array(2), '[Int8Array(2): null prototype] [ 0, 0 ]'], +// [new Int16Array(2), '[Int16Array(2): null prototype] [ 0, 0 ]'], +// [new Int32Array(2), '[Int32Array(2): null prototype] [ 0, 0 ]'], +// [new Float32Array(2), '[Float32Array(2): null prototype] [ 0, 0 ]'], +// [new Float64Array(2), '[Float64Array(2): null prototype] [ 0, 0 ]'], +// [new BigInt64Array(2), '[BigInt64Array(2): null prototype] [ 0n, 0n ]'], +// [new BigUint64Array(2), '[BigUint64Array(2): null prototype] [ 0n, 0n ]'], +// [new ArrayBuffer(16), '[ArrayBuffer: null prototype] {\n' + +// ' [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,\n' + +// ' byteLength: undefined\n}'], +// [new DataView(new ArrayBuffer(16)), +// '[DataView: null prototype] {\n byteLength: undefined,\n ' + +// 'byteOffset: undefined,\n buffer: undefined\n}'], +// [new SharedArrayBuffer(2), '[SharedArrayBuffer: null prototype] ' + +// '{\n [Uint8Contents]: <00 00>,\n byteLength: undefined\n}'], +// [/foobar/, '[RegExp: null prototype] /foobar/'], +// [new Date('Sun, 14 Feb 2010 11:48:40 GMT'), +// '[Date: null prototype] 2010-02-14T11:48:40.000Z'], +// ].forEach(([value, expected]) => { +// assert.strictEqual( +// util.inspect(Object.setPrototypeOf(value, null)), +// expected +// ); +// value.foo = 'bar'; +// assert.notStrictEqual(util.inspect(value), expected); +// delete value.foo; +// value[Symbol('foo')] = 'yeah'; +// assert.notStrictEqual(util.inspect(value), expected); +// }); + +// TODO(wafuwafu13): Fix +// // Verify that subclasses with and without prototype produce nice results. +// [ +// [RegExp, ['foobar', 'g'], '/foobar/g'], +// [WeakSet, [[{}]], '{ }'], +// [WeakMap, [[[{}, {}]]], '{ }'], +// [BigInt64Array, +// [10], +// '[\n 0n, 0n, 0n, 0n, 0n,\n 0n, 0n, 0n, 0n, 0n\n]'], +// [Date, ['Sun, 14 Feb 2010 11:48:40 GMT'], '2010-02-14T11:48:40.000Z'], +// [Date, ['invalid_date'], 'Invalid Date'], +// ].forEach(([base, input, rawExpected]) => { +// class Foo extends base {} +// const value = new Foo(...input); +// const symbol = value[Symbol.toStringTag]; +// const size = base.name.includes('Array') ? `(${input[0]})` : ''; +// const expected = `Foo${size} ${symbol ? `[${symbol}] ` : ''}${rawExpected}`; +// const expectedWithoutProto = +// `[${base.name}${size}: null prototype] ${rawExpected}`; +// assert.strictEqual(util.inspect(value), expected); +// value.foo = 'bar'; +// assert.notStrictEqual(util.inspect(value), expected); +// delete value.foo; +// assert.strictEqual( +// util.inspect(Object.setPrototypeOf(value, null)), +// expectedWithoutProto +// ); +// value.foo = 'bar'; +// let res = util.inspect(value); +// assert.notStrictEqual(res, expectedWithoutProto); +// assert.match(res, /foo: 'bar'/); +// delete value.foo; +// value[Symbol('foo')] = 'yeah'; +// res = util.inspect(value); +// assert.notStrictEqual(res, expectedWithoutProto); +// assert.match(res, /\[Symbol\(foo\)]: 'yeah'/); +// }); + +assert.strictEqual(inspect(1n), '1n'); +assert.strictEqual(inspect(Object(-1n)), '[BigInt: -1n]'); +assert.strictEqual(inspect(Object(13n)), '[BigInt: 13n]'); +// TODO(wafuwafu13): Fix +// assert.strictEqual(inspect(new BigInt64Array([0n])), 'BigInt64Array(1) [ 0n ]'); +// assert.strictEqual( +// inspect(new BigUint64Array([0n])), 'BigUint64Array(1) [ 0n ]'); + +// Verify non-enumerable keys get escaped. +{ + const obj = {}; + Object.defineProperty(obj, 'Non\nenumerable\tkey', { value: true }); + assert.strictEqual( + util.inspect(obj, { showHidden: true }), + '{ [Non\\nenumerable\\tkey]: true }' + ); +} + +// Check for special colors. +{ + const special = inspect.colors[inspect.styles.special]; + const string = inspect.colors[inspect.styles.string]; + + assert.strictEqual( + inspect(new WeakSet(), { colors: true }), + `WeakSet { \u001b[${special[0]}m\u001b[${special[1]}m }` + ); + assert.strictEqual( + inspect(new WeakMap(), { colors: true }), + `WeakMap { \u001b[${special[0]}m\u001b[${special[1]}m }` + ); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // inspect(new Promise(() => {}), { colors: true }), + // `Promise { \u001b[${special[0]}m\u001b[${special[1]}m }` + // ); + + // const rejection = Promise.reject('Oh no!'); + // assert.strictEqual( + // inspect(rejection, { colors: true }), + // `Promise { \u001b[${special[0]}m\u001b[${special[1]}m ` + + // `\u001b[${string[0]}m'Oh no!'\u001b[${string[1]}m }` + // ); + // rejection.catch(() => {}); + + // Verify that aliases do not show up as key while checking `inspect.colors`. + const colors = Object.keys(inspect.colors); + const aliases = Object.getOwnPropertyNames(inspect.colors) + .filter((c) => !colors.includes(c)); + assert(!colors.includes('grey')); + assert(colors.includes('gray')); + // Verify that all aliases are correctly mapped. + for (const alias of aliases) { + assert(Array.isArray(inspect.colors[alias])); + } + // Check consistent naming. + [ + 'black', + 'red', + 'green', + 'yellow', + 'blue', + 'magenta', + 'cyan', + 'white', + ].forEach((color, i) => { + assert.deepStrictEqual(inspect.colors[color], [30 + i, 39]); + assert.deepStrictEqual(inspect.colors[`${color}Bright`], [90 + i, 39]); + const bgColor = `bg${color[0].toUpperCase()}${color.slice(1)}`; + assert.deepStrictEqual(inspect.colors[bgColor], [40 + i, 49]); + assert.deepStrictEqual(inspect.colors[`${bgColor}Bright`], [100 + i, 49]); + }); + + // Unknown colors are handled gracefully: + const stringStyle = inspect.styles.string; + inspect.styles.string = 'UNKNOWN'; + assert.strictEqual(inspect('foobar', { colors: true }), "'foobar'"); + inspect.styles.string = stringStyle; +} + +assert.strictEqual( + inspect([1, 3, 2], { sorted: true }), + inspect([1, 3, 2]) +); +assert.strictEqual( + inspect({ c: 3, a: 1, b: 2 }, { sorted: true }), + '{ a: 1, b: 2, c: 3 }' +); +assert.strictEqual( + inspect( + { a200: 4, a100: 1, a102: 3, a101: 2 }, + { sorted(a, b) { return b.localeCompare(a); } } + ), + '{ a200: 4, a102: 3, a101: 2, a100: 1 }' +); + +// TODO(wafuwafu13): Fix +// // Non-indices array properties are sorted as well. +// { +// const arr = [3, 2, 1]; +// arr.b = 2; +// arr.c = 3; +// arr.a = 1; +// arr[Symbol('b')] = true; +// arr[Symbol('a')] = false; +// assert.strictEqual( +// inspect(arr, { sorted: true }), +// '[ 3, 2, 1, [Symbol(a)]: false, [Symbol(b)]: true, a: 1, b: 2, c: 3 ]' +// ); +// } + +// TODO(wafuwafu13): Fix +// // Manipulate the prototype in weird ways. +// { +// let obj = { a: true }; +// let value = (function() { return function() {}; })(); +// Object.setPrototypeOf(value, null); +// Object.setPrototypeOf(obj, value); +// assert.strictEqual( +// util.inspect(obj), +// 'Object <[Function (null prototype) (anonymous)]> { a: true }' +// ); +// assert.strictEqual( +// util.inspect(obj, { colors: true }), +// 'Object <\u001b[36m[Function (null prototype) (anonymous)]\u001b[39m> ' + +// '{ a: \u001b[33mtrue\u001b[39m }' +// ); + +// obj = { a: true }; +// value = []; +// Object.setPrototypeOf(value, null); +// Object.setPrototypeOf(obj, value); +// assert.strictEqual( +// util.inspect(obj), +// 'Object <[Array(0): null prototype] []> { a: true }' +// ); + +// function StorageObject() {} +// StorageObject.prototype = Object.create(null); +// assert.strictEqual( +// util.inspect(new StorageObject()), +// 'StorageObject <[Object: null prototype] {}> {}' +// ); + +// obj = [1, 2, 3]; +// Object.setPrototypeOf(obj, Number.prototype); +// assert.strictEqual(inspect(obj), "Number { '0': 1, '1': 2, '2': 3 }"); + +// Object.setPrototypeOf(obj, Object.create(null)); +// assert.strictEqual( +// inspect(obj), +// "Array <[Object: null prototype] {}> { '0': 1, '1': 2, '2': 3 }" +// ); + +// StorageObject.prototype = Object.create(null); +// Object.setPrototypeOf(StorageObject.prototype, Object.create(null)); +// Object.setPrototypeOf( +// Object.getPrototypeOf(StorageObject.prototype), +// Object.create(null) +// ); +// assert.strictEqual( +// util.inspect(new StorageObject()), +// 'StorageObject >> {}' +// ); +// assert.strictEqual( +// util.inspect(new StorageObject(), { depth: 1 }), +// 'StorageObject >> {}' +// ); +// } + +// TODO(wafuwafu13): Fix +// // Check that the fallback always works. +// { +// const obj = new Set([1, 2]); +// const iterator = obj[Symbol.iterator]; +// Object.setPrototypeOf(obj, null); +// Object.defineProperty(obj, Symbol.iterator, { +// value: iterator, +// configurable: true +// }); +// assert.strictEqual(util.inspect(obj), '[Set(2): null prototype] { 1, 2 }'); +// Object.defineProperty(obj, Symbol.iterator, { +// value: true, +// configurable: true +// }); +// Object.defineProperty(obj, 'size', { +// value: NaN, +// configurable: true, +// enumerable: true +// }); +// assert.strictEqual( +// util.inspect(obj), +// '[Set(2): null prototype] { 1, 2, size: NaN }' +// ); +// } + +// TODO(wafuwafu13): Fix +// Check the getter option. +{ + let foo = 1; + const get = { get foo() { return foo; } }; + const getset = { + get foo() { return foo; }, + set foo(val) { foo = val; }, + get inc() { return ++foo; } + }; + const thrower = { get foo() { throw new Error('Oops'); } }; + assert.strictEqual( + inspect(get, { getters: true, colors: true }), + '{ foo: \u001b[36m[Getter:\u001b[39m ' + + '\u001b[33m1\u001b[39m\u001b[36m]\u001b[39m }'); + assert.strictEqual( + inspect(thrower, { getters: true }), + '{ foo: [Getter: ] }'); + assert.strictEqual( + inspect(getset, { getters: true }), + '{ foo: [Getter/Setter: 1], inc: [Getter: 2] }'); + assert.strictEqual( + inspect(getset, { getters: 'get' }), + '{ foo: [Getter/Setter], inc: [Getter: 3] }'); + assert.strictEqual( + inspect(getset, { getters: 'set' }), + '{ foo: [Getter/Setter: 3], inc: [Getter] }'); + getset.foo = new Set([[{ a: true }, 2, {}], 'foobar', { x: 1 }]); + // assert.strictEqual( + // inspect(getset, { getters: true }), + // '{\n foo: [Getter/Setter] Set(3) { [ [Object], 2, {} ], ' + + // "'foobar', { x: 1 } },\n inc: [Getter: NaN]\n}"); +} + +// Check compact number mode. +{ + let obj = { + a: { + b: { + x: 5, + c: { + x: '10000000000000000 00000000000000000 '.repeat(1e1), + d: 2, + e: 3 + } + } + }, + b: [ + 1, + 2, + [ 1, 2, { a: 1, b: 2, c: 3 } ], + ], + c: ['foo', 4, 444444], + d: Array.from({ length: 101 }).map((e, i) => { + return i % 2 === 0 ? i * i : i; + }), + e: Array(6).fill('foobar'), + f: Array(9).fill('foobar'), + g: Array(21).fill('foobar baz'), + h: [100].concat(Array.from({ length: 9 }).map((e, n) => (n))), + long: Array(9).fill('This text is too long for grouping!') + }; + + let out = util.inspect(obj, { compact: 3, depth: 10, breakLength: 60 }); + let expected = [ + '{', + ' a: {', + ' b: {', + ' x: 5,', + ' c: {', + " x: '10000000000000000 00000000000000000 10000000000000000 " + + '00000000000000000 10000000000000000 00000000000000000 ' + + '10000000000000000 00000000000000000 10000000000000000 ' + + '00000000000000000 10000000000000000 00000000000000000 ' + + '10000000000000000 00000000000000000 10000000000000000 ' + + '00000000000000000 10000000000000000 00000000000000000 ' + + "10000000000000000 00000000000000000 ',", + ' d: 2,', + ' e: 3', + ' }', + ' }', + ' },', + ' b: [ 1, 2, [ 1, 2, { a: 1, b: 2, c: 3 } ] ],', + " c: [ 'foo', 4, 444444 ],", + ' d: [', + ' 0, 1, 4, 3, 16, 5, 36, 7, 64,', + ' 9, 100, 11, 144, 13, 196, 15, 256, 17,', + ' 324, 19, 400, 21, 484, 23, 576, 25, 676,', + ' 27, 784, 29, 900, 31, 1024, 33, 1156, 35,', + ' 1296, 37, 1444, 39, 1600, 41, 1764, 43, 1936,', + ' 45, 2116, 47, 2304, 49, 2500, 51, 2704, 53,', + ' 2916, 55, 3136, 57, 3364, 59, 3600, 61, 3844,', + ' 63, 4096, 65, 4356, 67, 4624, 69, 4900, 71,', + ' 5184, 73, 5476, 75, 5776, 77, 6084, 79, 6400,', + ' 81, 6724, 83, 7056, 85, 7396, 87, 7744, 89,', + ' 8100, 91, 8464, 93, 8836, 95, 9216, 97, 9604,', + ' 99,', + ' ... 1 more item', + ' ],', + ' e: [', + " 'foobar',", + " 'foobar',", + " 'foobar',", + " 'foobar',", + " 'foobar',", + " 'foobar'", + ' ],', + ' f: [', + " 'foobar', 'foobar',", + " 'foobar', 'foobar',", + " 'foobar', 'foobar',", + " 'foobar', 'foobar',", + " 'foobar'", + ' ],', + ' g: [', + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz', 'foobar baz',", + " 'foobar baz'", + ' ],', + ' h: [', + ' 100, 0, 1, 2, 3,', + ' 4, 5, 6, 7, 8', + ' ],', + ' long: [', + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!',", + " 'This text is too long for grouping!'", + ' ]', + '}', + ].join('\n'); + + // TODO(wafuwafu13): Fix + // assert.strictEqual(out, expected); + + obj = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 123456789, + ]; + + out = util.inspect(obj, { compact: 3 }); + + expected = [ + '[', + ' 1, 1, 1, 1,', + ' 1, 1, 1, 1,', + ' 1, 1, 1, 1,', + ' 1, 1, 1, 1,', + ' 1, 1, 1, 1,', + ' 1, 1, 1, 1,', + ' 1, 1, 123456789', + ']', + ].join('\n'); + + // TODO(wafuwafu13): Fix + // assert.strictEqual(out, expected); + + // Unicode support. あ has a length of one and a width of two. + obj = [ + '123', '123', '123', '123', 'あああ', + '123', '123', '123', '123', 'あああ', + ]; + + out = util.inspect(obj, { compact: 3 }); + + expected = [ + '[', + " '123', '123',", + " '123', '123',", + " 'あああ', '123',", + " '123', '123',", + " '123', 'あああ'", + ']', + ].join('\n'); + + // TODO(wafuwafu13): Fix + // assert.strictEqual(out, expected); + + // Verify that array grouping and line consolidation does not happen together. + obj = { + a: { + b: { + x: 5, + c: { + d: 2, + e: 3 + } + } + }, + b: Array.from({ length: 9 }).map((e, n) => { + return n % 2 === 0 ? 'foobar' : 'baz'; + }) + }; + + out = util.inspect(obj, { compact: 1, breakLength: Infinity, colors: true }); + + expected = [ + '{', + ' a: {', + ' b: { x: \u001b[33m5\u001b[39m, c: \u001b[36m[Object]\u001b[39m }', + ' },', + ' b: [', + " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", + " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", + " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", + " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", + " \u001b[32m'foobar'\u001b[39m", + ' ]', + '}', + ].join('\n'); + + // TODO(wafuwafu13): Fix + // assert.strictEqual(out, expected); + + obj = Array.from({ length: 60 }).map((e, i) => i); + out = util.inspect(obj, { compact: 1, breakLength: Infinity, colors: true }); + + expected = [ + '[', + /* eslint-disable max-len */ + ' \u001b[33m0\u001b[39m, \u001b[33m1\u001b[39m, \u001b[33m2\u001b[39m, \u001b[33m3\u001b[39m,', + ' \u001b[33m4\u001b[39m, \u001b[33m5\u001b[39m, \u001b[33m6\u001b[39m, \u001b[33m7\u001b[39m,', + ' \u001b[33m8\u001b[39m, \u001b[33m9\u001b[39m, \u001b[33m10\u001b[39m, \u001b[33m11\u001b[39m,', + ' \u001b[33m12\u001b[39m, \u001b[33m13\u001b[39m, \u001b[33m14\u001b[39m, \u001b[33m15\u001b[39m,', + ' \u001b[33m16\u001b[39m, \u001b[33m17\u001b[39m, \u001b[33m18\u001b[39m, \u001b[33m19\u001b[39m,', + ' \u001b[33m20\u001b[39m, \u001b[33m21\u001b[39m, \u001b[33m22\u001b[39m, \u001b[33m23\u001b[39m,', + ' \u001b[33m24\u001b[39m, \u001b[33m25\u001b[39m, \u001b[33m26\u001b[39m, \u001b[33m27\u001b[39m,', + ' \u001b[33m28\u001b[39m, \u001b[33m29\u001b[39m, \u001b[33m30\u001b[39m, \u001b[33m31\u001b[39m,', + ' \u001b[33m32\u001b[39m, \u001b[33m33\u001b[39m, \u001b[33m34\u001b[39m, \u001b[33m35\u001b[39m,', + ' \u001b[33m36\u001b[39m, \u001b[33m37\u001b[39m, \u001b[33m38\u001b[39m, \u001b[33m39\u001b[39m,', + ' \u001b[33m40\u001b[39m, \u001b[33m41\u001b[39m, \u001b[33m42\u001b[39m, \u001b[33m43\u001b[39m,', + ' \u001b[33m44\u001b[39m, \u001b[33m45\u001b[39m, \u001b[33m46\u001b[39m, \u001b[33m47\u001b[39m,', + ' \u001b[33m48\u001b[39m, \u001b[33m49\u001b[39m, \u001b[33m50\u001b[39m, \u001b[33m51\u001b[39m,', + ' \u001b[33m52\u001b[39m, \u001b[33m53\u001b[39m, \u001b[33m54\u001b[39m, \u001b[33m55\u001b[39m,', + ' \u001b[33m56\u001b[39m, \u001b[33m57\u001b[39m, \u001b[33m58\u001b[39m, \u001b[33m59\u001b[39m', + /* eslint-enable max-len */ + ']', + ].join('\n'); + + // TODO(wafuwafu13): Fix + // assert.strictEqual(out, expected); + + out = util.inspect([1, 2, 3, 4], { compact: 1, colors: true }); + expected = '[ \u001b[33m1\u001b[39m, \u001b[33m2\u001b[39m, ' + + '\u001b[33m3\u001b[39m, \u001b[33m4\u001b[39m ]'; + + assert.strictEqual(out, expected); + + obj = [ + 'Object', 'Function', 'Array', + 'Number', 'parseFloat', 'parseInt', + 'Infinity', 'NaN', 'undefined', + 'Boolean', 'String', 'Symbol', + 'Date', 'Promise', 'RegExp', + 'Error', 'EvalError', 'RangeError', + 'ReferenceError', 'SyntaxError', 'TypeError', + 'URIError', 'JSON', 'Math', + 'console', 'Intl', 'ArrayBuffer', + 'Uint8Array', 'Int8Array', 'Uint16Array', + 'Int16Array', 'Uint32Array', 'Int32Array', + 'Float32Array', 'Float64Array', 'Uint8ClampedArray', + 'BigUint64Array', 'BigInt64Array', 'DataView', + 'Map', 'BigInt', 'Set', + 'WeakMap', 'WeakSet', 'Proxy', + 'Reflect', 'decodeURI', 'decodeURIComponent', + 'encodeURI', 'encodeURIComponent', 'escape', + 'unescape', 'eval', 'isFinite', + 'isNaN', 'SharedArrayBuffer', 'Atomics', + 'globalThis', 'WebAssembly', 'global', + 'process', 'Buffer', 'URL', + 'URLSearchParams', 'TextEncoder', 'TextDecoder', + 'clearInterval', 'clearTimeout', 'setInterval', + 'setTimeout', 'queueMicrotask', 'clearImmediate', + 'setImmediate', 'module', 'require', + 'assert', 'async_hooks', 'buffer', + 'child_process', 'cluster', 'crypto', + 'dgram', 'dns', 'domain', + 'events', 'fs', 'http', + 'http2', 'https', 'inspector', + 'net', 'os', 'path', + 'perf_hooks', 'punycode', 'querystring', + 'readline', 'repl', 'stream', + 'string_decoder', 'tls', 'trace_events', + 'tty', 'url', 'v8', + 'vm', 'worker_threads', 'zlib', + '_', '_error', 'util', + ]; + + out = util.inspect( + obj, + { compact: 3, breakLength: 80, maxArrayLength: 250 } + ); + expected = [ + '[', + " 'Object', 'Function', 'Array',", + " 'Number', 'parseFloat', 'parseInt',", + " 'Infinity', 'NaN', 'undefined',", + " 'Boolean', 'String', 'Symbol',", + " 'Date', 'Promise', 'RegExp',", + " 'Error', 'EvalError', 'RangeError',", + " 'ReferenceError', 'SyntaxError', 'TypeError',", + " 'URIError', 'JSON', 'Math',", + " 'console', 'Intl', 'ArrayBuffer',", + " 'Uint8Array', 'Int8Array', 'Uint16Array',", + " 'Int16Array', 'Uint32Array', 'Int32Array',", + " 'Float32Array', 'Float64Array', 'Uint8ClampedArray',", + " 'BigUint64Array', 'BigInt64Array', 'DataView',", + " 'Map', 'BigInt', 'Set',", + " 'WeakMap', 'WeakSet', 'Proxy',", + " 'Reflect', 'decodeURI', 'decodeURIComponent',", + " 'encodeURI', 'encodeURIComponent', 'escape',", + " 'unescape', 'eval', 'isFinite',", + " 'isNaN', 'SharedArrayBuffer', 'Atomics',", + " 'globalThis', 'WebAssembly', 'global',", + " 'process', 'Buffer', 'URL',", + " 'URLSearchParams', 'TextEncoder', 'TextDecoder',", + " 'clearInterval', 'clearTimeout', 'setInterval',", + " 'setTimeout', 'queueMicrotask', 'clearImmediate',", + " 'setImmediate', 'module', 'require',", + " 'assert', 'async_hooks', 'buffer',", + " 'child_process', 'cluster', 'crypto',", + " 'dgram', 'dns', 'domain',", + " 'events', 'fs', 'http',", + " 'http2', 'https', 'inspector',", + " 'net', 'os', 'path',", + " 'perf_hooks', 'punycode', 'querystring',", + " 'readline', 'repl', 'stream',", + " 'string_decoder', 'tls', 'trace_events',", + " 'tty', 'url', 'v8',", + " 'vm', 'worker_threads', 'zlib',", + " '_', '_error', 'util'", + ']', + ].join('\n'); + + // TODO(wafuwafu13): Fix + // assert.strictEqual(out, expected); +} + +// TODO(wafuwafu13): Fix +// { +// // Use a fake stack to verify the expected colored outcome. +// const stack = [ +// 'TypedError: Wonderful message!', +// ' at A. (/test/node_modules/foo/node_modules/bar/baz.js:2:7)', +// ' at Module._compile (node:internal/modules/cjs/loader:827:30)', +// ' at Fancy (node:vm:697:32)', +// // This file is not an actual Node.js core file. +// ' at tryModuleLoad (node:internal/modules/cjs/foo:629:12)', +// ' at Function.Module._load (node:internal/modules/cjs/loader:621:3)', +// // This file is not an actual Node.js core file. +// ' at Module.require [as weird/name] (node:internal/aaaaa/loader:735:19)', +// ' at require (node:internal/modules/cjs/helpers:14:16)', +// ' at /test/test-util-inspect.js:2239:9', +// ' at getActual (node:assert:592:5)', +// ]; +// const isNodeCoreFile = [ +// false, false, true, true, false, true, false, true, false, true, +// ]; +// const err = new TypeError('Wonderful message!'); +// err.stack = stack.join('\n'); +// util.inspect(err, { colors: true }).split('\n').forEach((line, i) => { +// let actual = stack[i].replace(/node_modules\/([a-z]+)/g, (a, m) => { +// return `node_modules/\u001b[4m${m}\u001b[24m`; +// }); +// if (isNodeCoreFile[i]) { +// actual = `\u001b[90m${actual}\u001b[39m`; +// } +// assert.strictEqual(actual, line); +// }); +// } + +// { +// // Cross platform checks. +// const err = new Error('foo'); +// util.inspect(err, { colors: true }).split('\n').forEach((line, i) => { +// assert(i < 2 || line.startsWith('\u001b[90m')); +// }); +// } + +// TODO(wafuwafu13): Implement "trace_events" +// { +// // Tracing class respects inspect depth. +// try { +// const trace = require('trace_events').createTracing({ categories: ['fo'] }); +// const actualDepth0 = util.inspect({ trace }, { depth: 0 }); +// assert.strictEqual(actualDepth0, '{ trace: [Tracing] }'); +// const actualDepth1 = util.inspect({ trace }, { depth: 1 }); +// assert.strictEqual( +// actualDepth1, +// "{ trace: Tracing { enabled: false, categories: 'fo' } }" +// ); +// } catch (err) { +// if (err.code !== 'ERR_TRACE_EVENTS_UNAVAILABLE') +// throw err; +// } +// } + +// Inspect prototype properties. +{ + class Foo extends Map { + prop = false; + prop2 = true; + get abc() { + return true; + } + get def() { + return false; + } + set def(v) {} + get xyz() { + return 'Should be ignored'; + } + func(a) {} + [util.inspect.custom]() { + return this; + } + } + + class Bar extends Foo { + abc = true; + prop = true; + get xyz() { + return 'YES!'; + } + [util.inspect.custom]() { + return this; + } + } + + const bar = new Bar(); + + assert.strictEqual( + inspect(bar), + 'Bar(0) [Map] { prop: true, prop2: true, abc: true }' + ); + // TODO(wafuwafu13): Fix + // assert.strictEqual( + // inspect(bar, { showHidden: true, getters: true, colors: false }), + // 'Bar(0) [Map] {\n' + + // ' prop: true,\n' + + // ' prop2: true,\n' + + // ' abc: true,\n' + + // " [xyz]: [Getter: 'YES!'],\n" + + // ' [def]: [Getter/Setter: false]\n' + + // '}' + // ); + // assert.strictEqual( + // inspect(bar, { showHidden: true, getters: false, colors: true }), + // 'Bar(0) [Map] {\n' + + // ' prop: \x1B[33mtrue\x1B[39m,\n' + + // ' prop2: \x1B[33mtrue\x1B[39m,\n' + + // ' abc: \x1B[33mtrue\x1B[39m,\n' + + // ' \x1B[2m[xyz]: \x1B[36m[Getter]\x1B[39m\x1B[22m,\n' + + // ' \x1B[2m[def]: \x1B[36m[Getter/Setter]\x1B[39m\x1B[22m\n' + + // '}' + // ); + + // const obj = Object.create({ abc: true, def: 5, toString() {} }); + // assert.strictEqual( + // inspect(obj, { showHidden: true, colors: true }), + // '{ \x1B[2mabc: \x1B[33mtrue\x1B[39m\x1B[22m, ' + + // '\x1B[2mdef: \x1B[33m5\x1B[39m\x1B[22m }' + // ); + + // assert.strictEqual( + // inspect(Object.getPrototypeOf(bar), { showHidden: true, getters: true }), + // ' Foo [Map] {\n' + + // ' [constructor]: [class Bar extends Foo] {\n' + + // ' [length]: 0,\n' + + // " [name]: 'Bar',\n" + + // ' [prototype]: [Circular *1],\n' + + // ' [Symbol(Symbol.species)]: [Getter: ]\n" + + // ' },\n' + + // " [xyz]: [Getter: 'YES!'],\n" + + // ' [Symbol(nodejs.util.inspect.custom)]: ' + + // '[Function: [nodejs.util.inspect.custom]] {\n' + + // ' [length]: 0,\n' + + // " [name]: '[nodejs.util.inspect.custom]'\n" + + // ' },\n' + + // ' [abc]: [Getter: true],\n' + + // ' [def]: [Getter/Setter: false]\n' + + // ' }' + // ); + + // assert.strictEqual( + // inspect(Object.getPrototypeOf(bar)), + // 'Foo [Map] {}' + // ); + + // assert.strictEqual( + // inspect(Object.getPrototypeOf(new Foo())), + // 'Map {}' + // ); +} + +// Check that prototypes with a null prototype are inspectable. +// Regression test for https://github.com/nodejs/node/issues/35730 +{ + function Func() {} + Func.prototype = null; + const object = {}; + object.constructor = Func; + + assert.strictEqual(util.inspect(object), '{ constructor: [Function: Func] }'); +} + +// Test changing util.inspect.colors colors and aliases. +{ + const colors = util.inspect.colors; + + const originalValue = colors.gray; + + // "grey" is reference-equal alias of "gray". + assert.strictEqual(colors.grey, colors.gray); + + // Assigninging one should assign the other. This tests that the alias setter + // function keeps things reference-equal. + colors.gray = [0, 0]; + assert.deepStrictEqual(colors.gray, [0, 0]); + assert.strictEqual(colors.grey, colors.gray); + + colors.grey = [1, 1]; + assert.deepStrictEqual(colors.grey, [1, 1]); + assert.strictEqual(colors.grey, colors.gray); + + // Restore original value to avoid side effects in other tests. + colors.gray = originalValue; + assert.deepStrictEqual(colors.gray, originalValue); + assert.strictEqual(colors.grey, colors.gray); +} + +// TODO(wafuwafu13): Implement 'vm' +// // https://github.com/nodejs/node/issues/31889 +// { +// v8.setFlagsFromString('--allow-natives-syntax'); +// const undetectable = vm.runInThisContext('%GetUndetectable()'); +// v8.setFlagsFromString('--no-allow-natives-syntax'); +// assert.strictEqual(inspect(undetectable), '{}'); +// } + +// Truncate output for Primitives with 1 character left +{ + assert.strictEqual(util.inspect('bl', { maxStringLength: 1 }), + "'b'... 1 more character"); +} + +{ + const x = 'a'.repeat(1e6); + assert(util.inspect(x).endsWith('... 990000 more characters')); + assert.strictEqual( + util.inspect(x, { maxStringLength: 4 }), + "'aaaa'... 999996 more characters" + ); + assert.match(util.inspect(x, { maxStringLength: null }), /a'$/); +} + +// TODO(wafuwafu13): Implement 'vm' +// { +// // Verify that util.inspect() invokes custom inspect functions on objects +// // from other vm.Contexts but does not pass data from its own Context to that +// // function. +// const target = vm.runInNewContext(` +// ({ +// [Symbol.for('nodejs.util.inspect.custom')](depth, ctx) { +// this.depth = depth; +// this.ctx = ctx; +// try { +// this.stylized = ctx.stylize('🐈'); +// } catch (e) { +// this.stylizeException = e; +// } +// return this.stylized; +// } +// }) +// `, Object.create(null)); +// assert.strictEqual(target.ctx, undefined); + +// { +// // Subtest 1: Just try to inspect the object with default options. +// assert.strictEqual(util.inspect(target), '🐈'); +// assert.strictEqual(typeof target.ctx, 'object'); +// const objectGraph = fullObjectGraph(target); +// assert(!objectGraph.has(Object)); +// assert(!objectGraph.has(Function)); +// } + +// { +// // Subtest 2: Use a stylize function that returns a non-primitive. +// const output = util.inspect(target, { +// stylize: common.mustCall((str) => { +// return {}; +// }) +// }); +// assert.strictEqual(output, '[object Object]'); +// assert.strictEqual(typeof target.ctx, 'object'); +// const objectGraph = fullObjectGraph(target); +// assert(!objectGraph.has(Object)); +// assert(!objectGraph.has(Function)); +// } + +// { +// // Subtest 3: Use a stylize function that throws an exception. +// const output = util.inspect(target, { +// stylize: common.mustCall((str) => { +// throw new Error('oops'); +// }) +// }); +// assert.strictEqual(output, '🐈'); +// assert.strictEqual(typeof target.ctx, 'object'); +// const objectGraph = fullObjectGraph(target); +// assert(!objectGraph.has(Object)); +// assert(!objectGraph.has(Function)); +// } + +// function fullObjectGraph(value) { +// const graph = new Set([value]); + +// for (const entry of graph) { +// if ((typeof entry !== 'object' && typeof entry !== 'function') || +// entry === null) { +// continue; +// } + +// graph.add(Object.getPrototypeOf(entry)); +// const descriptors = Object.values( +// Object.getOwnPropertyDescriptors(entry)); +// for (const descriptor of descriptors) { +// graph.add(descriptor.value); +// graph.add(descriptor.set); +// graph.add(descriptor.get); +// } +// } + +// return graph; +// } + +// // Consistency check. +// assert(fullObjectGraph(global).has(Function.prototype)); +// } + +{ + // Confirm that own constructor value displays correctly. + + function Fhqwhgads() {} + + const sterrance = new Fhqwhgads(); + sterrance.constructor = Fhqwhgads; + + assert.strictEqual( + util.inspect(sterrance, { showHidden: true }), + 'Fhqwhgads {\n' + + ' constructor: [Function: Fhqwhgads] {\n' + + ' [length]: 0,\n' + + " [name]: 'Fhqwhgads',\n" + + ' [prototype]: { [constructor]: [Circular *1] }\n' + + ' }\n' + + '}' + ); +} + +// TODO(wafuwafu13): Fix TypeError: main.hasOwnProperty is not a function +// { +// // Confirm null prototype of generator prototype displays as expected. + +// function getProtoOfProto() { +// return Object.getPrototypeOf(Object.getPrototypeOf(function* () {})); +// } + +// function* generator() {} + +// const generatorPrototype = Object.getPrototypeOf(generator); +// const originalProtoOfProto = Object.getPrototypeOf(generatorPrototype); +// assert.strictEqual(getProtoOfProto(), originalProtoOfProto); +// Object.setPrototypeOf(generatorPrototype, null); +// assert.notStrictEqual(getProtoOfProto, originalProtoOfProto); + +// // This is the actual test. The other assertions in this block are about +// // making sure the test is set up correctly and isn't polluting other tests. +// assert.strictEqual( +// util.inspect(generator, { showHidden: true }), +// '[GeneratorFunction: generator] {\n' + +// ' [length]: 0,\n' + +// " [name]: 'generator',\n" + +// " [prototype]: Object [Generator] { [Symbol(Symbol.toStringTag)]: 'Generator' },\n" + // eslint-disable-line max-len +// " [Symbol(Symbol.toStringTag)]: 'GeneratorFunction'\n" + +// '}' +// ); + +// // Reset so we don't pollute other tests +// Object.setPrototypeOf(generatorPrototype, originalProtoOfProto); +// assert.strictEqual(getProtoOfProto(), originalProtoOfProto); +// } + +{ + // Test for when breakLength results in a single column. + const obj = Array(9).fill('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf'); + assert.strictEqual( + util.inspect(obj, { breakLength: 256 }), + '[\n' + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" + + " 'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf'\n" + + ']' + ); +} + +{ + assert.strictEqual( + util.inspect({ ['__proto__']: { a: 1 } }), + "{ ['__proto__']: { a: 1 } }" + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-util-isDeepStrictEqual.js b/cli/tests/node_compat/test/parallel/test-util-isDeepStrictEqual.js new file mode 100644 index 00000000000000..25caac1f7021db --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-isDeepStrictEqual.js @@ -0,0 +1,608 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +"use strict"; + +// Confirm functionality of `util.isDeepStrictEqual()`. + +require("../common"); + +const assert = require("assert"); +const util = require("util"); + +class MyDate extends Date { + constructor(...args) { + super(...args); + this[0] = "1"; + } +} + +class MyRegExp extends RegExp { + constructor(...args) { + super(...args); + this[0] = "1"; + } +} + +{ + const arr = new Uint8Array([120, 121, 122, 10]); + const buf = Buffer.from(arr); + // They have different [[Prototype]] + assert.strictEqual(util.isDeepStrictEqual(arr, buf), false); + + const buf2 = Buffer.from(arr); + buf2.prop = 1; + + assert.strictEqual(util.isDeepStrictEqual(buf2, buf), false); + + const arr2 = new Uint8Array([120, 121, 122, 10]); + arr2.prop = 5; + assert.strictEqual(util.isDeepStrictEqual(arr, arr2), false); +} + +{ + const date = new Date("2016"); + + const date2 = new MyDate("2016"); + + // deepStrictEqual checks own properties + assert.strictEqual(util.isDeepStrictEqual(date, date2), false); + assert.strictEqual(util.isDeepStrictEqual(date2, date), false); +} + +{ + const re1 = new RegExp("test"); + const re2 = new MyRegExp("test"); + + // deepStrictEqual checks all properties + assert.strictEqual(util.isDeepStrictEqual(re1, re2), false); +} + +{ + // For these cases, deepStrictEqual should throw. + const similar = new Set([ + { 0: "1" }, // Object + { 0: 1 }, // Object + new String("1"), // Object + ["1"], // Array + [1], // Array + new MyDate("2016"), // Date with this[0] = '1' + new MyRegExp("test"), // RegExp with this[0] = '1' + new Int8Array([1]), // Int8Array + new Uint8Array([1]), // Uint8Array + new Int16Array([1]), // Int16Array + new Uint16Array([1]), // Uint16Array + new Int32Array([1]), // Int32Array + new Uint32Array([1]), // Uint32Array + Buffer.from([1]), // Buffer + ]); + + for (const a of similar) { + for (const b of similar) { + if (a !== b) { + assert.strictEqual(util.isDeepStrictEqual(a, b), false); + } + } + } +} + +function utilIsDeepStrict(a, b) { + assert.strictEqual(util.isDeepStrictEqual(a, b), true); + assert.strictEqual(util.isDeepStrictEqual(b, a), true); +} +function notUtilIsDeepStrict(a, b) { + assert.strictEqual(util.isDeepStrictEqual(a, b), false); + assert.strictEqual(util.isDeepStrictEqual(b, a), false); +} + +// es6 Maps and Sets +utilIsDeepStrict(new Set(), new Set()); +utilIsDeepStrict(new Map(), new Map()); + +utilIsDeepStrict(new Set([1, 2, 3]), new Set([1, 2, 3])); +notUtilIsDeepStrict(new Set([1, 2, 3]), new Set([1, 2, 3, 4])); +notUtilIsDeepStrict(new Set([1, 2, 3, 4]), new Set([1, 2, 3])); +utilIsDeepStrict(new Set(["1", "2", "3"]), new Set(["1", "2", "3"])); +utilIsDeepStrict( + new Set([ + [1, 2], + [3, 4], + ]), + new Set([ + [3, 4], + [1, 2], + ]) +); + +{ + const a = [1, 2]; + const b = [3, 4]; + const c = [1, 2]; + const d = [3, 4]; + + utilIsDeepStrict( + { a: a, b: b, s: new Set([a, b]) }, + { a: c, b: d, s: new Set([d, c]) } + ); +} + +utilIsDeepStrict( + new Map([ + [1, 1], + [2, 2], + ]), + new Map([ + [1, 1], + [2, 2], + ]) +); +utilIsDeepStrict( + new Map([ + [1, 1], + [2, 2], + ]), + new Map([ + [2, 2], + [1, 1], + ]) +); +notUtilIsDeepStrict( + new Map([ + [1, 1], + [2, 2], + ]), + new Map([ + [1, 2], + [2, 1], + ]) +); +notUtilIsDeepStrict( + new Map([ + [[1], 1], + [{}, 2], + ]), + new Map([ + [[1], 2], + [{}, 1], + ]) +); + +notUtilIsDeepStrict(new Set([1]), [1]); +notUtilIsDeepStrict(new Set(), []); +notUtilIsDeepStrict(new Set(), {}); + +notUtilIsDeepStrict(new Map([["a", 1]]), { a: 1 }); +notUtilIsDeepStrict(new Map(), []); +notUtilIsDeepStrict(new Map(), {}); + +notUtilIsDeepStrict(new Set(["1"]), new Set([1])); + +notUtilIsDeepStrict(new Map([["1", "a"]]), new Map([[1, "a"]])); +notUtilIsDeepStrict(new Map([["a", "1"]]), new Map([["a", 1]])); +notUtilIsDeepStrict(new Map([["a", "1"]]), new Map([["a", 2]])); + +utilIsDeepStrict(new Set([{}]), new Set([{}])); + +// Ref: https://github.com/nodejs/node/issues/13347 +notUtilIsDeepStrict( + new Set([{ a: 1 }, { a: 1 }]), + new Set([{ a: 1 }, { a: 2 }]) +); +notUtilIsDeepStrict( + new Set([{ a: 1 }, { a: 1 }, { a: 2 }]), + new Set([{ a: 1 }, { a: 2 }, { a: 2 }]) +); +notUtilIsDeepStrict( + new Map([ + [{ x: 1 }, 5], + [{ x: 1 }, 5], + ]), + new Map([ + [{ x: 1 }, 5], + [{ x: 2 }, 5], + ]) +); + +notUtilIsDeepStrict(new Set([3, "3"]), new Set([3, 4])); +notUtilIsDeepStrict( + new Map([ + [3, 0], + ["3", 0], + ]), + new Map([ + [3, 0], + [4, 0], + ]) +); + +notUtilIsDeepStrict( + new Set([{ a: 1 }, { a: 1 }, { a: 2 }]), + new Set([{ a: 1 }, { a: 2 }, { a: 2 }]) +); + +// Mixed primitive and object keys +utilIsDeepStrict( + new Map([ + [1, "a"], + [{}, "a"], + ]), + new Map([ + [1, "a"], + [{}, "a"], + ]) +); +utilIsDeepStrict(new Set([1, "a", [{}, "a"]]), new Set([1, "a", [{}, "a"]])); + +// This is an awful case, where a map contains multiple equivalent keys: +notUtilIsDeepStrict( + new Map([ + [1, "a"], + ["1", "b"], + ]), + new Map([ + ["1", "a"], + [true, "b"], + ]) +); +notUtilIsDeepStrict(new Set(["a"]), new Set(["b"])); +utilIsDeepStrict( + new Map([ + [{}, "a"], + [{}, "b"], + ]), + new Map([ + [{}, "b"], + [{}, "a"], + ]) +); +notUtilIsDeepStrict( + new Map([ + [true, "a"], + ["1", "b"], + [1, "a"], + ]), + new Map([ + ["1", "a"], + [1, "b"], + [true, "a"], + ]) +); +notUtilIsDeepStrict( + new Map([ + [true, "a"], + ["1", "b"], + [1, "c"], + ]), + new Map([ + ["1", "a"], + [1, "b"], + [true, "a"], + ]) +); + +// Similar object keys +notUtilIsDeepStrict(new Set([{}, {}]), new Set([{}, 1])); +notUtilIsDeepStrict( + new Set([ + [{}, 1], + [{}, 1], + ]), + new Set([ + [{}, 1], + [1, 1], + ]) +); +notUtilIsDeepStrict( + new Map([ + [{}, 1], + [{}, 1], + ]), + new Map([ + [{}, 1], + [1, 1], + ]) +); +notUtilIsDeepStrict( + new Map([ + [{}, 1], + [true, 1], + ]), + new Map([ + [{}, 1], + [1, 1], + ]) +); + +// Similar primitive key / values +notUtilIsDeepStrict(new Set([1, true, false]), new Set(["1", 0, "0"])); +notUtilIsDeepStrict( + new Map([ + [1, 5], + [true, 5], + [false, 5], + ]), + new Map([ + ["1", 5], + [0, 5], + ["0", 5], + ]) +); + +// Undefined value in Map +utilIsDeepStrict(new Map([[1, undefined]]), new Map([[1, undefined]])); +notUtilIsDeepStrict(new Map([[1, null]]), new Map([["1", undefined]])); +notUtilIsDeepStrict(new Map([[1, undefined]]), new Map([[2, undefined]])); + +// null as key +utilIsDeepStrict(new Map([[null, 3]]), new Map([[null, 3]])); +notUtilIsDeepStrict(new Map([[null, undefined]]), new Map([[undefined, null]])); +notUtilIsDeepStrict(new Set([null]), new Set([undefined])); + +// GH-6416. Make sure circular refs don't throw. +{ + const b = {}; + b.b = b; + const c = {}; + c.b = c; + + utilIsDeepStrict(b, c); + + const d = {}; + d.a = 1; + d.b = d; + const e = {}; + e.a = 1; + e.b = {}; + + notUtilIsDeepStrict(d, e); +} + +// GH-14441. Circular structures should be consistent +{ + const a = {}; + const b = {}; + a.a = a; + b.a = {}; + b.a.a = a; + utilIsDeepStrict(a, b); +} + +{ + const a = new Set(); + const b = new Set(); + const c = new Set(); + a.add(a); + b.add(b); + c.add(a); + utilIsDeepStrict(b, c); +} + +// GH-7178. Ensure reflexivity of deepEqual with `arguments` objects. +{ + const args = (function () { + return arguments; + })(); + notUtilIsDeepStrict([], args); +} + +// More checking that arguments objects are handled correctly +{ + // eslint-disable-next-line func-style + const returnArguments = function () { + return arguments; + }; + + const someArgs = returnArguments("a"); + const sameArgs = returnArguments("a"); + const diffArgs = returnArguments("b"); + + notUtilIsDeepStrict(someArgs, ["a"]); + notUtilIsDeepStrict(someArgs, { 0: "a" }); + notUtilIsDeepStrict(someArgs, diffArgs); + utilIsDeepStrict(someArgs, sameArgs); +} + +{ + const values = [ + 123, + Infinity, + 0, + null, + undefined, + false, + true, + {}, + [], + () => {}, + ]; + utilIsDeepStrict(new Set(values), new Set(values)); + utilIsDeepStrict(new Set(values), new Set(values.reverse())); + + const mapValues = values.map((v) => [v, { a: 5 }]); + utilIsDeepStrict(new Map(mapValues), new Map(mapValues)); + utilIsDeepStrict(new Map(mapValues), new Map(mapValues.reverse())); +} + +{ + const s1 = new Set(); + const s2 = new Set(); + s1.add(1); + s1.add(2); + s2.add(2); + s2.add(1); + utilIsDeepStrict(s1, s2); +} + +{ + const m1 = new Map(); + const m2 = new Map(); + const obj = { a: 5, b: 6 }; + m1.set(1, obj); + m1.set(2, "hi"); + m1.set(3, [1, 2, 3]); + + m2.set(2, "hi"); // different order + m2.set(1, obj); + m2.set(3, [1, 2, 3]); // Deep equal, but not reference equal. + + utilIsDeepStrict(m1, m2); +} + +{ + const m1 = new Map(); + const m2 = new Map(); + + // m1 contains itself. + m1.set(1, m1); + m2.set(1, new Map()); + + notUtilIsDeepStrict(m1, m2); +} + +{ + const map1 = new Map([[1, 1]]); + const map2 = new Map([[1, "1"]]); + assert.strictEqual(util.isDeepStrictEqual(map1, map2), false); +} + +{ + // Two equivalent sets / maps with different key/values applied shouldn't be + // the same. This is a terrible idea to do in practice, but deepEqual should + // still check for it. + const s1 = new Set(); + const s2 = new Set(); + s1.x = 5; + notUtilIsDeepStrict(s1, s2); + + const m1 = new Map(); + const m2 = new Map(); + m1.x = 5; + notUtilIsDeepStrict(m1, m2); +} + +{ + // Circular references. + const s1 = new Set(); + s1.add(s1); + const s2 = new Set(); + s2.add(s2); + utilIsDeepStrict(s1, s2); + + const m1 = new Map(); + m1.set(2, m1); + const m2 = new Map(); + m2.set(2, m2); + utilIsDeepStrict(m1, m2); + + const m3 = new Map(); + m3.set(m3, 2); + const m4 = new Map(); + m4.set(m4, 2); + utilIsDeepStrict(m3, m4); +} + +// Handle sparse arrays +utilIsDeepStrict([1, , , 3], [1, , , 3]); +notUtilIsDeepStrict([1, , , 3], [1, , , 3, , ,]); + +// Handle different error messages +{ + const err1 = new Error("foo1"); + const err2 = new Error("foo2"); + const err3 = new TypeError("foo1"); + notUtilIsDeepStrict(err1, err2, assert.AssertionError); + notUtilIsDeepStrict(err1, err3, assert.AssertionError); + notUtilIsDeepStrict(err1, {}, assert.AssertionError); +} + +// Handle NaN +assert.strictEqual(util.isDeepStrictEqual(NaN, NaN), true); +assert.strictEqual(util.isDeepStrictEqual({ a: NaN }, { a: NaN }), true); +assert.strictEqual( + util.isDeepStrictEqual([1, 2, NaN, 4], [1, 2, NaN, 4]), + true +); + +// Handle boxed primitives +{ + const boxedString = new String("test"); + const boxedSymbol = Object(Symbol()); + notUtilIsDeepStrict(new Boolean(true), Object(false)); + notUtilIsDeepStrict(Object(true), new Number(1)); + notUtilIsDeepStrict(new Number(2), new Number(1)); + notUtilIsDeepStrict(boxedSymbol, Object(Symbol())); + notUtilIsDeepStrict(boxedSymbol, {}); + utilIsDeepStrict(boxedSymbol, boxedSymbol); + utilIsDeepStrict(Object(true), Object(true)); + utilIsDeepStrict(Object(2), Object(2)); + utilIsDeepStrict(boxedString, Object("test")); + boxedString.slow = true; + notUtilIsDeepStrict(boxedString, Object("test")); + boxedSymbol.slow = true; + notUtilIsDeepStrict(boxedSymbol, {}); + utilIsDeepStrict(Object(BigInt(1)), Object(BigInt(1))); + notUtilIsDeepStrict(Object(BigInt(1)), Object(BigInt(2))); + + const booleanish = new Boolean(true); + Object.defineProperty(booleanish, Symbol.toStringTag, { value: "String" }); + Object.setPrototypeOf(booleanish, String.prototype); + notUtilIsDeepStrict(booleanish, new String("true")); + + const numberish = new Number(42); + Object.defineProperty(numberish, Symbol.toStringTag, { value: "String" }); + Object.setPrototypeOf(numberish, String.prototype); + notUtilIsDeepStrict(numberish, new String("42")); + + const stringish = new String("0"); + Object.defineProperty(stringish, Symbol.toStringTag, { value: "Number" }); + Object.setPrototypeOf(stringish, Number.prototype); + notUtilIsDeepStrict(stringish, new Number(0)); + + const bigintish = new Object(BigInt(42)); + Object.defineProperty(bigintish, Symbol.toStringTag, { value: "String" }); + Object.setPrototypeOf(bigintish, String.prototype); + notUtilIsDeepStrict(bigintish, new String("42")); + + const symbolish = new Object(Symbol("fhqwhgads")); + Object.defineProperty(symbolish, Symbol.toStringTag, { value: "String" }); + Object.setPrototypeOf(symbolish, String.prototype); + notUtilIsDeepStrict(symbolish, new String("fhqwhgads")); +} + +// Minus zero +notUtilIsDeepStrict(0, -0); +utilIsDeepStrict(-0, -0); + +// Handle symbols (enumerable only) +{ + const symbol1 = Symbol(); + const obj1 = { [symbol1]: 1 }; + const obj2 = { [symbol1]: 1 }; + const obj3 = { [Symbol()]: 1 }; + const obj4 = {}; + // Add a non enumerable symbol as well. It is going to be ignored! + Object.defineProperty(obj2, Symbol(), { value: 1 }); + Object.defineProperty(obj4, symbol1, { value: 1 }); + notUtilIsDeepStrict(obj1, obj3); + utilIsDeepStrict(obj1, obj2); + notUtilIsDeepStrict(obj1, obj4); + // TypedArrays have a fast path. Test for this as well. + const a = new Uint8Array(4); + const b = new Uint8Array(4); + a[symbol1] = true; + b[symbol1] = false; + notUtilIsDeepStrict(a, b); + b[symbol1] = true; + utilIsDeepStrict(a, b); + // The same as TypedArrays is valid for boxed primitives + const boxedStringA = new String("test"); + const boxedStringB = new String("test"); + boxedStringA[symbol1] = true; + notUtilIsDeepStrict(boxedStringA, boxedStringB); + boxedStringA[symbol1] = true; + utilIsDeepStrict(a, b); +} diff --git a/cli/tests/node_compat/test/parallel/test-util-promisify.js b/cli/tests/node_compat/test/parallel/test-util-promisify.js new file mode 100644 index 00000000000000..0c5b64349c247e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-promisify.js @@ -0,0 +1,219 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Flags: --expose-internals +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const vm = require('vm'); +const { promisify } = require('util'); +const { customPromisifyArgs } = require('internal/util'); + +const stat = promisify(fs.stat); + +// TODO(wafuwafu13): Fix +// { +// const promise = stat(__filename); +// assert(promise instanceof Promise); +// promise.then(common.mustCall((value) => { +// assert.deepStrictEqual(value, fs.statSync(__filename)); +// })); +// } + +{ + const promise = stat('/dontexist'); + promise.catch(common.mustCall((error) => { + assert(error.message.includes('ENOENT: no such file or directory, stat')); + })); +} + +{ + function fn() {} + + function promisifedFn() {} + fn[promisify.custom] = promisifedFn; + assert.strictEqual(promisify(fn), promisifedFn); + assert.strictEqual(promisify(promisify(fn)), promisifedFn); +} + +{ + function fn() {} + + function promisifiedFn() {} + + // util.promisify.custom is a shared symbol which can be accessed + // as `Symbol.for("nodejs.util.promisify.custom")`. + const kCustomPromisifiedSymbol = Symbol.for('nodejs.util.promisify.custom'); + fn[kCustomPromisifiedSymbol] = promisifiedFn; + + assert.strictEqual(kCustomPromisifiedSymbol, promisify.custom); + assert.strictEqual(promisify(fn), promisifiedFn); + assert.strictEqual(promisify(promisify(fn)), promisifiedFn); +} + +{ + function fn() {} + fn[promisify.custom] = 42; + assert.throws( + () => promisify(fn), + { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' } + ); +} + +// TODO(wafuwafu13): Fix +// { +// const firstValue = 5; +// const secondValue = 17; + +// function fn(callback) { +// callback(null, firstValue, secondValue); +// } + +// fn[customPromisifyArgs] = ['first', 'second']; + +// promisify(fn)().then(common.mustCall((obj) => { +// assert.deepStrictEqual(obj, { first: firstValue, second: secondValue }); +// })); +// } + +// TODO(wafuwafu13): Implement "vm.runInNewContext" +// { +// const fn = vm.runInNewContext('(function() {})'); +// assert.notStrictEqual(Object.getPrototypeOf(promisify(fn)), +// Function.prototype); +// } + +{ + function fn(callback) { + callback(null, 'foo', 'bar'); + } + promisify(fn)().then(common.mustCall((value) => { + assert.deepStrictEqual(value, 'foo'); + })); +} + +{ + function fn(callback) { + callback(null); + } + promisify(fn)().then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + })); +} + +{ + function fn(callback) { + callback(); + } + promisify(fn)().then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + })); +} + +{ + function fn(err, val, callback) { + callback(err, val); + } + promisify(fn)(null, 42).then(common.mustCall((value) => { + assert.strictEqual(value, 42); + })); +} + +{ + function fn(err, val, callback) { + callback(err, val); + } + promisify(fn)(new Error('oops'), null).catch(common.mustCall((err) => { + assert.strictEqual(err.message, 'oops'); + })); +} + +{ + function fn(err, val, callback) { + callback(err, val); + } + + (async () => { + const value = await promisify(fn)(null, 42); + assert.strictEqual(value, 42); + })().then(common.mustCall()); +} + +{ + const o = {}; + const fn = promisify(function(cb) { + + cb(null, this === o); + }); + + o.fn = fn; + + o.fn().then(common.mustCall((val) => assert(val))); +} + +{ + const err = new Error('Should not have called the callback with the error.'); + const stack = err.stack; + + const fn = promisify(function(cb) { + cb(null); + cb(err); + }); + + (async () => { + await fn(); + await Promise.resolve(); + return assert.strictEqual(stack, err.stack); + })().then(common.mustCall()); +} + +{ + function c() { } + const a = promisify(function() { }); + const b = promisify(a); + assert.notStrictEqual(c, a); + assert.strictEqual(a, b); +} + +{ + let errToThrow; + const thrower = promisify(function(a, b, c, cb) { + errToThrow = new Error(); + throw errToThrow; + }); + thrower(1, 2, 3) + .then(assert.fail) + .then(assert.fail, (e) => assert.strictEqual(e, errToThrow)); +} + +{ + const err = new Error(); + + const a = promisify((cb) => cb(err))(); + const b = promisify(() => { throw err; })(); + + Promise.all([ + a.then(assert.fail, function(e) { + assert.strictEqual(err, e); + }), + b.then(assert.fail, function(e) { + assert.strictEqual(err, e); + }), + ]); +} + +[undefined, null, true, 0, 'str', {}, [], Symbol()].forEach((input) => { + assert.throws( + () => promisify(input), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "original" argument must be of type function.' + + common.invalidArgTypeHelper(input) + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-util-types-exists.js b/cli/tests/node_compat/test/parallel/test-util-types-exists.js new file mode 100644 index 00000000000000..805d3e11e5aebd --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-types-exists.js @@ -0,0 +1,13 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('util/types'), require('util').types); diff --git a/cli/tests/node_compat/test/parallel/test-util-types.js b/cli/tests/node_compat/test/parallel/test-util-types.js new file mode 100644 index 00000000000000..00e3d0fd063f84 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util-types.js @@ -0,0 +1,306 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --experimental-vm-modules --expose-internals +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { types, inspect } = require('util'); +// TODO(wafuwafu13): Implement "vm" +// const vm = require('vm'); +// TODO(wafuwafu13): Implement "internalBinding" +// const { internalBinding } = require('internal/test/binding'); +// const { JSStream } = internalBinding('js_stream'); + +// TODO(wafuwafu13): Implement "JSStream" +// const external = (new JSStream())._externalStream; + +for (const [ value, _method ] of [ + // TODO(wafuwafu13): Implement "JSStream" + // [ external, 'isExternal' ], + [ new Date() ], + [ (function() { return arguments; })(), 'isArgumentsObject' ], + [ new Boolean(), 'isBooleanObject' ], + [ new Number(), 'isNumberObject' ], + [ new String(), 'isStringObject' ], + [ Object(Symbol()), 'isSymbolObject' ], + [ Object(BigInt(0)), 'isBigIntObject' ], + [ new Error(), 'isNativeError' ], + [ new RegExp() ], + [ async function() {}, 'isAsyncFunction' ], + [ function*() {}, 'isGeneratorFunction' ], + [ (function*() {})(), 'isGeneratorObject' ], + [ Promise.resolve() ], + [ new Map() ], + [ new Set() ], + [ (new Map())[Symbol.iterator](), 'isMapIterator' ], + [ (new Set())[Symbol.iterator](), 'isSetIterator' ], + [ new WeakMap() ], + [ new WeakSet() ], + [ new ArrayBuffer() ], + [ new Uint8Array() ], + [ new Uint8ClampedArray() ], + [ new Uint16Array() ], + [ new Uint32Array() ], + [ new Int8Array() ], + [ new Int16Array() ], + [ new Int32Array() ], + [ new Float32Array() ], + [ new Float64Array() ], + [ new BigInt64Array() ], + [ new BigUint64Array() ], + // [ Object.defineProperty(new Uint8Array(), + // Symbol.toStringTag, + // { value: 'foo' }) ], + [ new DataView(new ArrayBuffer()) ], + [ new SharedArrayBuffer() ], + [ new Proxy({}, {}), 'isProxy' ], +]) { + const method = _method || `is${value.constructor.name}`; + assert(method in types, `Missing ${method} for ${inspect(value)}`); + assert(types[method](value), `Want ${inspect(value)} to match ${method}`); + + for (const key of Object.keys(types)) { + if ((types.isArrayBufferView(value) || + types.isAnyArrayBuffer(value)) && key.includes('Array') || + key === 'isBoxedPrimitive') { + continue; + } + + assert.strictEqual(types[key](value), + key === method, + `${inspect(value)}: ${key}, ` + + `${method}, ${types[key](value)}`); + } +} + +// Check boxed primitives. +[ + new Boolean(), + new Number(), + new String(), + Object(Symbol()), + Object(BigInt(0)), +].forEach((entry) => assert(types.isBoxedPrimitive(entry))); + +// TODO(wafuwafu13): Fix +// { +// assert(!types.isUint8Array({ [Symbol.toStringTag]: 'Uint8Array' })); +// assert(types.isUint8Array(vm.runInNewContext('new Uint8Array'))); + +// assert(!types.isUint8ClampedArray({ +// [Symbol.toStringTag]: 'Uint8ClampedArray' +// })); +// assert(types.isUint8ClampedArray( +// vm.runInNewContext('new Uint8ClampedArray') +// )); + +// assert(!types.isUint16Array({ [Symbol.toStringTag]: 'Uint16Array' })); +// assert(types.isUint16Array(vm.runInNewContext('new Uint16Array'))); + +// assert(!types.isUint32Array({ [Symbol.toStringTag]: 'Uint32Array' })); +// assert(types.isUint32Array(vm.runInNewContext('new Uint32Array'))); + +// assert(!types.isInt8Array({ [Symbol.toStringTag]: 'Int8Array' })); +// assert(types.isInt8Array(vm.runInNewContext('new Int8Array'))); + +// assert(!types.isInt16Array({ [Symbol.toStringTag]: 'Int16Array' })); +// assert(types.isInt16Array(vm.runInNewContext('new Int16Array'))); + +// assert(!types.isInt32Array({ [Symbol.toStringTag]: 'Int32Array' })); +// assert(types.isInt32Array(vm.runInNewContext('new Int32Array'))); + +// assert(!types.isFloat32Array({ [Symbol.toStringTag]: 'Float32Array' })); +// assert(types.isFloat32Array(vm.runInNewContext('new Float32Array'))); + +// assert(!types.isFloat64Array({ [Symbol.toStringTag]: 'Float64Array' })); +// assert(types.isFloat64Array(vm.runInNewContext('new Float64Array'))); + +// assert(!types.isBigInt64Array({ [Symbol.toStringTag]: 'BigInt64Array' })); +// assert(types.isBigInt64Array(vm.runInNewContext('new BigInt64Array'))); + +// assert(!types.isBigUint64Array({ [Symbol.toStringTag]: 'BigUint64Array' })); +// assert(types.isBigUint64Array(vm.runInNewContext('new BigUint64Array'))); +// } + +{ + const primitive = true; + const arrayBuffer = new ArrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + const dataView = new DataView(arrayBuffer); + const uint8Array = new Uint8Array(arrayBuffer); + const uint8ClampedArray = new Uint8ClampedArray(arrayBuffer); + const uint16Array = new Uint16Array(arrayBuffer); + const uint32Array = new Uint32Array(arrayBuffer); + const int8Array = new Int8Array(arrayBuffer); + const int16Array = new Int16Array(arrayBuffer); + const int32Array = new Int32Array(arrayBuffer); + const float32Array = new Float32Array(arrayBuffer); + const float64Array = new Float64Array(arrayBuffer); + const bigInt64Array = new BigInt64Array(arrayBuffer); + const bigUint64Array = new BigUint64Array(arrayBuffer); + + const fakeBuffer = Object.create(Buffer.prototype); + const fakeDataView = Object.create(DataView.prototype); + const fakeUint8Array = Object.create(Uint8Array.prototype); + const fakeUint8ClampedArray = Object.create(Uint8ClampedArray.prototype); + const fakeUint16Array = Object.create(Uint16Array.prototype); + const fakeUint32Array = Object.create(Uint32Array.prototype); + const fakeInt8Array = Object.create(Int8Array.prototype); + const fakeInt16Array = Object.create(Int16Array.prototype); + const fakeInt32Array = Object.create(Int32Array.prototype); + const fakeFloat32Array = Object.create(Float32Array.prototype); + const fakeFloat64Array = Object.create(Float64Array.prototype); + const fakeBigInt64Array = Object.create(BigInt64Array.prototype); + const fakeBigUint64Array = Object.create(BigUint64Array.prototype); + + const stealthyDataView = + Object.setPrototypeOf(new DataView(arrayBuffer), Uint8Array.prototype); + const stealthyUint8Array = + Object.setPrototypeOf(new Uint8Array(arrayBuffer), ArrayBuffer.prototype); + const stealthyUint8ClampedArray = + Object.setPrototypeOf( + new Uint8ClampedArray(arrayBuffer), ArrayBuffer.prototype + ); + const stealthyUint16Array = + Object.setPrototypeOf(new Uint16Array(arrayBuffer), Uint16Array.prototype); + const stealthyUint32Array = + Object.setPrototypeOf(new Uint32Array(arrayBuffer), Uint32Array.prototype); + const stealthyInt8Array = + Object.setPrototypeOf(new Int8Array(arrayBuffer), Int8Array.prototype); + const stealthyInt16Array = + Object.setPrototypeOf(new Int16Array(arrayBuffer), Int16Array.prototype); + const stealthyInt32Array = + Object.setPrototypeOf(new Int32Array(arrayBuffer), Int32Array.prototype); + const stealthyFloat32Array = + Object.setPrototypeOf( + new Float32Array(arrayBuffer), Float32Array.prototype + ); + const stealthyFloat64Array = + Object.setPrototypeOf( + new Float64Array(arrayBuffer), Float64Array.prototype + ); + const stealthyBigInt64Array = + Object.setPrototypeOf( + new BigInt64Array(arrayBuffer), BigInt64Array.prototype + ); + const stealthyBigUint64Array = + Object.setPrototypeOf( + new BigUint64Array(arrayBuffer), BigUint64Array.prototype + ); + + const all = [ + primitive, arrayBuffer, buffer, fakeBuffer, + dataView, fakeDataView, stealthyDataView, + uint8Array, fakeUint8Array, stealthyUint8Array, + uint8ClampedArray, fakeUint8ClampedArray, stealthyUint8ClampedArray, + uint16Array, fakeUint16Array, stealthyUint16Array, + uint32Array, fakeUint32Array, stealthyUint32Array, + int8Array, fakeInt8Array, stealthyInt8Array, + int16Array, fakeInt16Array, stealthyInt16Array, + int32Array, fakeInt32Array, stealthyInt32Array, + float32Array, fakeFloat32Array, stealthyFloat32Array, + float64Array, fakeFloat64Array, stealthyFloat64Array, + bigInt64Array, fakeBigInt64Array, stealthyBigInt64Array, + bigUint64Array, fakeBigUint64Array, stealthyBigUint64Array, + ]; + + const expected = { + isArrayBufferView: [ + buffer, + dataView, stealthyDataView, + uint8Array, stealthyUint8Array, + uint8ClampedArray, stealthyUint8ClampedArray, + uint16Array, stealthyUint16Array, + uint32Array, stealthyUint32Array, + int8Array, stealthyInt8Array, + int16Array, stealthyInt16Array, + int32Array, stealthyInt32Array, + float32Array, stealthyFloat32Array, + float64Array, stealthyFloat64Array, + bigInt64Array, stealthyBigInt64Array, + bigUint64Array, stealthyBigUint64Array, + ], + isTypedArray: [ + buffer, + uint8Array, stealthyUint8Array, + uint8ClampedArray, stealthyUint8ClampedArray, + uint16Array, stealthyUint16Array, + uint32Array, stealthyUint32Array, + int8Array, stealthyInt8Array, + int16Array, stealthyInt16Array, + int32Array, stealthyInt32Array, + float32Array, stealthyFloat32Array, + float64Array, stealthyFloat64Array, + bigInt64Array, stealthyBigInt64Array, + bigUint64Array, stealthyBigUint64Array, + ], + isUint8Array: [ + buffer, uint8Array, stealthyUint8Array, + ], + isUint8ClampedArray: [ + uint8ClampedArray, stealthyUint8ClampedArray, + ], + isUint16Array: [ + uint16Array, stealthyUint16Array, + ], + isUint32Array: [ + uint32Array, stealthyUint32Array, + ], + isInt8Array: [ + int8Array, stealthyInt8Array, + ], + isInt16Array: [ + int16Array, stealthyInt16Array, + ], + isInt32Array: [ + int32Array, stealthyInt32Array, + ], + isFloat32Array: [ + float32Array, stealthyFloat32Array, + ], + isFloat64Array: [ + float64Array, stealthyFloat64Array, + ], + isBigInt64Array: [ + bigInt64Array, stealthyBigInt64Array, + ], + isBigUint64Array: [ + bigUint64Array, stealthyBigUint64Array, + ] + }; + + for (const testedFunc of Object.keys(expected)) { + const func = types[testedFunc]; + const yup = []; + for (const value of all) { + if (func(value)) { + yup.push(value); + } + } + console.log('Testing', testedFunc); + assert.deepStrictEqual(yup, expected[testedFunc]); + } +} + +// TODO(wafuwafu13): Implement "vm" +// (async () => { +// const m = new vm.SourceTextModule(''); +// await m.link(() => 0); +// await m.evaluate(); +// assert.ok(types.isModuleNamespaceObject(m.namespace)); +// })().then(common.mustCall()); + +{ + // eslint-disable-next-line node-core/crypto-check + if (common.hasCrypto) { + const crypto = require('crypto'); + assert.ok(!types.isKeyObject(crypto.createHash('sha1'))); + } + assert.ok(!types.isCryptoKey()); + assert.ok(!types.isKeyObject()); +} diff --git a/cli/tests/node_compat/test/parallel/test-util.js b/cli/tests/node_compat/test/parallel/test-util.js new file mode 100644 index 00000000000000..5d1ed9b301af00 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-util.js @@ -0,0 +1,203 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Flags: --expose-internals +const common = require('../common'); +const assert = require('assert'); +const util = require('util'); +const errors = require('internal/errors'); +// TODO(wafuwafu13): Enable this when "vm" is ready. +// const context = require('vm').runInNewContext; + +// isArray +assert.strictEqual(util.isArray([]), true); +assert.strictEqual(util.isArray(Array()), true); +assert.strictEqual(util.isArray(new Array()), true); +assert.strictEqual(util.isArray(new Array(5)), true); +assert.strictEqual(util.isArray(new Array('with', 'some', 'entries')), true); +// TODO(wafuwafu13): Enable this when "vm" is ready. +// assert.strictEqual(util.isArray(context('Array')()), true); +assert.strictEqual(util.isArray({}), false); +assert.strictEqual(util.isArray({ push: function() {} }), false); +assert.strictEqual(util.isArray(/regexp/), false); +assert.strictEqual(util.isArray(new Error()), false); +assert.strictEqual(util.isArray(Object.create(Array.prototype)), false); + +// isRegExp +assert.strictEqual(util.isRegExp(/regexp/), true); +assert.strictEqual(util.isRegExp(RegExp(), 'foo'), true); +assert.strictEqual(util.isRegExp(new RegExp()), true); +// TODO(wafuwafu13): Enable this when "vm" is ready. +// assert.strictEqual(util.isRegExp(context('RegExp')()), true); +assert.strictEqual(util.isRegExp({}), false); +assert.strictEqual(util.isRegExp([]), false); +assert.strictEqual(util.isRegExp(new Date()), false); +// TODO(wafuwafu13): Enable this. +assert.strictEqual(util.isRegExp(Object.create(RegExp.prototype)), false); + +// isDate +assert.strictEqual(util.isDate(new Date()), true); +assert.strictEqual(util.isDate(new Date(0), 'foo'), true); +// TODO(wafuwafu13): Enable this when "vm" is ready. +// assert.strictEqual(util.isDate(new (context('Date'))()), true); +assert.strictEqual(util.isDate(Date()), false); +assert.strictEqual(util.isDate({}), false); +assert.strictEqual(util.isDate([]), false); +assert.strictEqual(util.isDate(new Error()), false); +assert.strictEqual(util.isDate(Object.create(Date.prototype)), false); + +// isError +assert.strictEqual(util.isError(new Error()), true); +assert.strictEqual(util.isError(new TypeError()), true); +assert.strictEqual(util.isError(new SyntaxError()), true); +// TODO(wafuwafu13): Enable this when "vm" is ready. +// assert.strictEqual(util.isError(new (context('Error'))()), true); +// assert.strictEqual(util.isError(new (context('TypeError'))()), true); +// assert.strictEqual(util.isError(new (context('SyntaxError'))()), true); +assert.strictEqual(util.isError({}), false); +assert.strictEqual(util.isError({ name: 'Error', message: '' }), false); +assert.strictEqual(util.isError([]), false); +assert.strictEqual(util.isError(Object.create(Error.prototype)), true); + +// isObject +assert.strictEqual(util.isObject({}), true); +assert.strictEqual(util.isObject([]), true); +assert.strictEqual(util.isObject(new Number(3)), true); +assert.strictEqual(util.isObject(Number(4)), false); +assert.strictEqual(util.isObject(1), false); + +// isPrimitive +assert.strictEqual(util.isPrimitive({}), false); +assert.strictEqual(util.isPrimitive(new Error()), false); +assert.strictEqual(util.isPrimitive(new Date()), false); +assert.strictEqual(util.isPrimitive([]), false); +assert.strictEqual(util.isPrimitive(/regexp/), false); +assert.strictEqual(util.isPrimitive(function() {}), false); +assert.strictEqual(util.isPrimitive(new Number(1)), false); +assert.strictEqual(util.isPrimitive(new String('bla')), false); +assert.strictEqual(util.isPrimitive(new Boolean(true)), false); +assert.strictEqual(util.isPrimitive(1), true); +assert.strictEqual(util.isPrimitive('bla'), true); +assert.strictEqual(util.isPrimitive(true), true); +assert.strictEqual(util.isPrimitive(undefined), true); +assert.strictEqual(util.isPrimitive(null), true); +assert.strictEqual(util.isPrimitive(Infinity), true); +assert.strictEqual(util.isPrimitive(NaN), true); +assert.strictEqual(util.isPrimitive(Symbol('symbol')), true); + +// isBuffer +assert.strictEqual(util.isBuffer('foo'), false); +assert.strictEqual(util.isBuffer(Buffer.from('foo')), true); + +// _extend +assert.deepStrictEqual(util._extend({ a: 1 }), { a: 1 }); +assert.deepStrictEqual(util._extend({ a: 1 }, []), { a: 1 }); +assert.deepStrictEqual(util._extend({ a: 1 }, null), { a: 1 }); +assert.deepStrictEqual(util._extend({ a: 1 }, true), { a: 1 }); +assert.deepStrictEqual(util._extend({ a: 1 }, false), { a: 1 }); +assert.deepStrictEqual(util._extend({ a: 1 }, { b: 2 }), { a: 1, b: 2 }); +assert.deepStrictEqual(util._extend({ a: 1, b: 2 }, { b: 3 }), { a: 1, b: 3 }); + +// deprecated +assert.strictEqual(util.isBoolean(true), true); +assert.strictEqual(util.isBoolean(false), true); +assert.strictEqual(util.isBoolean('string'), false); + +assert.strictEqual(util.isNull(null), true); +assert.strictEqual(util.isNull(undefined), false); +assert.strictEqual(util.isNull(), false); +assert.strictEqual(util.isNull('string'), false); + +assert.strictEqual(util.isUndefined(undefined), true); +assert.strictEqual(util.isUndefined(), true); +assert.strictEqual(util.isUndefined(null), false); +assert.strictEqual(util.isUndefined('string'), false); + +assert.strictEqual(util.isNullOrUndefined(null), true); +assert.strictEqual(util.isNullOrUndefined(undefined), true); +assert.strictEqual(util.isNullOrUndefined(), true); +assert.strictEqual(util.isNullOrUndefined('string'), false); + +assert.strictEqual(util.isNumber(42), true); +assert.strictEqual(util.isNumber(), false); +assert.strictEqual(util.isNumber('string'), false); + +assert.strictEqual(util.isString('string'), true); +assert.strictEqual(util.isString(), false); +assert.strictEqual(util.isString(42), false); + +assert.strictEqual(util.isSymbol(Symbol()), true); +assert.strictEqual(util.isSymbol(), false); +assert.strictEqual(util.isSymbol('string'), false); + +assert.strictEqual(util.isFunction(() => {}), true); +assert.strictEqual(util.isFunction(function() {}), true); +assert.strictEqual(util.isFunction(), false); +assert.strictEqual(util.isFunction('string'), false); + +// TODO(wafuwafu13): Enable this when `toUSVString` is ready. +// assert.strictEqual(util.toUSVString('string\ud801'), 'string\ufffd'); + +{ + assert.strictEqual(util.types.isNativeError(new Error()), true); + assert.strictEqual(util.types.isNativeError(new TypeError()), true); + assert.strictEqual(util.types.isNativeError(new SyntaxError()), true); + // TODO(wafuwafu13): Enable this when "vm" is ready. + // assert.strictEqual(util.types.isNativeError(new (context('Error'))()), true); + // assert.strictEqual( + // util.types.isNativeError(new (context('TypeError'))()), + // true + // ); + // assert.strictEqual( + // util.types.isNativeError(new (context('SyntaxError'))()), + // true + // ); + assert.strictEqual(util.types.isNativeError({}), false); + assert.strictEqual( + util.types.isNativeError({ name: 'Error', message: '' }), + false + ); + assert.strictEqual(util.types.isNativeError([]), false); + assert.strictEqual( + util.types.isNativeError(Object.create(Error.prototype)), + false + ); + assert.strictEqual( + util.types.isNativeError(new errors.codes.ERR_IPC_CHANNEL_CLOSED()), + true + ); +} + +assert.throws(() => { + util.stripVTControlCharacters({}); +}, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "str" argument must be of type string.' + + common.invalidArgTypeHelper({}) +}); diff --git a/cli/tests/node_compat/test/parallel/test-vm-static-this.js b/cli/tests/node_compat/test/parallel/test-vm-static-this.js new file mode 100644 index 00000000000000..c2511bc217ba4d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-vm-static-this.js @@ -0,0 +1,72 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-disable strict */ +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// Run a string +const result = vm.runInThisContext('\'passed\';'); +assert.strictEqual(result, 'passed'); + +// thrown error +assert.throws(function() { + vm.runInThisContext('throw new Error(\'test\');'); +}, /test/); + +global.hello = 5; +vm.runInThisContext('hello = 2'); +assert.strictEqual(global.hello, 2); + + +// pass values +const code = 'foo = 1;' + + 'bar = 2;' + + 'if (typeof baz !== \'undefined\')' + + 'throw new Error(\'test fail\');'; +global.foo = 2; +global.obj = { foo: 0, baz: 3 }; +/* eslint-disable no-unused-vars */ +const baz = vm.runInThisContext(code); +/* eslint-enable no-unused-vars */ +assert.strictEqual(global.obj.foo, 0); +assert.strictEqual(global.bar, 2); +assert.strictEqual(global.foo, 1); + +// call a function +global.f = function() { global.foo = 100; }; +vm.runInThisContext('f()'); +assert.strictEqual(global.foo, 100); + +common.allowGlobals( + global.hello, + global.foo, + global.obj, + global.f +); diff --git a/cli/tests/node_compat/test/parallel/test-webcrypto-sign-verify.js b/cli/tests/node_compat/test/parallel/test-webcrypto-sign-verify.js new file mode 100644 index 00000000000000..23df883ee31a6d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-webcrypto-sign-verify.js @@ -0,0 +1,154 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = require('crypto').webcrypto; + +// This is only a partial test. The WebCrypto Web Platform Tests +// will provide much greater coverage. + +// Test Sign/Verify RSASSA-PKCS1-v1_5 +{ + async function test(data) { + const ec = new TextEncoder(); + const { publicKey, privateKey } = await subtle.generateKey({ + name: 'RSASSA-PKCS1-v1_5', + modulusLength: 1024, + publicExponent: new Uint8Array([1, 0, 1]), + hash: 'SHA-256' + }, true, ['sign', 'verify']); + + const signature = await subtle.sign({ + name: 'RSASSA-PKCS1-v1_5' + }, privateKey, ec.encode(data)); + + assert(await subtle.verify({ + name: 'RSASSA-PKCS1-v1_5' + }, publicKey, signature, ec.encode(data))); + } + + test('hello world').then(common.mustCall()); +} + +// Test Sign/Verify RSA-PSS +{ + async function test(data) { + const ec = new TextEncoder(); + const { publicKey, privateKey } = await subtle.generateKey({ + name: 'RSA-PSS', + modulusLength: 4096, + publicExponent: new Uint8Array([1, 0, 1]), + hash: 'SHA-256' + }, true, ['sign', 'verify']); + + const signature = await subtle.sign({ + name: 'RSA-PSS', + saltLength: 256, + }, privateKey, ec.encode(data)); + + assert(await subtle.verify({ + name: 'RSA-PSS', + saltLength: 256, + }, publicKey, signature, ec.encode(data))); + } + + test('hello world').then(common.mustCall()); +} + +// Test Sign/Verify ECDSA +{ + async function test(data) { + const ec = new TextEncoder(); + const { publicKey, privateKey } = await subtle.generateKey({ + name: 'ECDSA', + namedCurve: 'P-384', + }, true, ['sign', 'verify']); + + const signature = await subtle.sign({ + name: 'ECDSA', + hash: 'SHA-384', + }, privateKey, ec.encode(data)); + + assert(await subtle.verify({ + name: 'ECDSA', + hash: 'SHA-384', + }, publicKey, signature, ec.encode(data))); + } + + test('hello world').then(common.mustCall()); +} + +// Test Sign/Verify HMAC +{ + async function test(data) { + const ec = new TextEncoder(); + + const key = await subtle.generateKey({ + name: 'HMAC', + length: 256, + hash: 'SHA-256' + }, true, ['sign', 'verify']); + + const signature = await subtle.sign({ + name: 'HMAC', + }, key, ec.encode(data)); + + assert(await subtle.verify({ + name: 'HMAC', + }, key, signature, ec.encode(data))); + } + + test('hello world').then(common.mustCall()); +} + +// Test Sign/Verify Ed25519 +{ + async function test(data) { + const ec = new TextEncoder(); + const { publicKey, privateKey } = await subtle.generateKey({ + name: 'Ed25519', + }, true, ['sign', 'verify']); + + const signature = await subtle.sign({ + name: 'Ed25519', + }, privateKey, ec.encode(data)); + + assert(await subtle.verify({ + name: 'Ed25519', + }, publicKey, signature, ec.encode(data))); + } + + test('hello world').then(common.mustCall()); +} + +// Test Sign/Verify Ed448 +// TODO(cjihrig): Pending support in Deno core. +// { +// async function test(data) { +// const ec = new TextEncoder(); +// const { publicKey, privateKey } = await subtle.generateKey({ +// name: 'Ed448', +// }, true, ['sign', 'verify']); + +// const signature = await subtle.sign({ +// name: 'Ed448', +// }, privateKey, ec.encode(data)); + +// assert(await subtle.verify({ +// name: 'Ed448', +// }, publicKey, signature, ec.encode(data))); +// } + +// test('hello world').then(common.mustCall()); +// } diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-api-basics.js b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-api-basics.js new file mode 100644 index 00000000000000..5d404285b647cb --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-api-basics.js @@ -0,0 +1,68 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/master/encoding/api-basics.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +function testDecodeSample(encoding, string, bytes) { + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes)), + string); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer), + string); +} + +// `z` (ASCII U+007A), cent (Latin-1 U+00A2), CJK water (BMP U+6C34), +// G-Clef (non-BMP U+1D11E), PUA (BMP U+F8FF), PUA (non-BMP U+10FFFD) +// byte-swapped BOM (non-character U+FFFE) +const sample = 'z\xA2\u6C34\uD834\uDD1E\uF8FF\uDBFF\uDFFD\uFFFE'; + +{ + const encoding = 'utf-8'; + const string = sample; + const bytes = [ + 0x7A, 0xC2, 0xA2, 0xE6, 0xB0, 0xB4, + 0xF0, 0x9D, 0x84, 0x9E, 0xEF, 0xA3, + 0xBF, 0xF4, 0x8F, 0xBF, 0xBD, 0xEF, + 0xBF, 0xBE, + ]; + const encoded = new TextEncoder().encode(string); + assert.deepStrictEqual([].slice.call(encoded), bytes); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes)), + string); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer), + string); +} + +testDecodeSample( + 'utf-16le', + sample, + [ + 0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, + 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xF8, + 0xFF, 0xDB, 0xFD, 0xDF, 0xFE, 0xFF, + ] +); + +testDecodeSample( + 'utf-16', + sample, + [ + 0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, + 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xF8, + 0xFF, 0xDB, 0xFD, 0xDF, 0xFE, 0xFF, + ] +); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js new file mode 100644 index 00000000000000..b3ad5f05859f65 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js @@ -0,0 +1,68 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/d74324b53c/encoding/textdecoder-fatal-streaming.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); +const assert = require('assert'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +{ + [ + { encoding: 'utf-8', sequence: [0xC0] }, + { encoding: 'utf-16le', sequence: [0x00] }, + { encoding: 'utf-16be', sequence: [0x00] }, + ].forEach((testCase) => { + const data = new Uint8Array([testCase.sequence]); + assert.throws( + () => { + const decoder = new TextDecoder(testCase.encoding, { fatal: true }); + decoder.decode(data); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + `The encoded data was not valid for encoding ${testCase.encoding}` + } + ); + }); +} + +{ + const decoder = new TextDecoder('utf-16le', { fatal: true }); + const odd = new Uint8Array([0x00]); + const even = new Uint8Array([0x00, 0x00]); + + assert.throws( + () => { + decoder.decode(even, { stream: true }); + decoder.decode(odd); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + 'The encoded data was not valid for encoding utf-16le' + } + ); + + assert.throws( + () => { + decoder.decode(odd, { stream: true }); + decoder.decode(even); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + 'The encoded data was not valid for encoding utf-16le' + } + ); +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js new file mode 100644 index 00000000000000..3a8aac4003b380 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js @@ -0,0 +1,91 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-fatal.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); + +const bad = [ + { encoding: 'utf-8', input: [0xFF], name: 'invalid code' }, + { encoding: 'utf-8', input: [0xC0], name: 'ends early' }, + { encoding: 'utf-8', input: [0xE0], name: 'ends early 2' }, + { encoding: 'utf-8', input: [0xC0, 0x00], name: 'invalid trail' }, + { encoding: 'utf-8', input: [0xC0, 0xC0], name: 'invalid trail 2' }, + { encoding: 'utf-8', input: [0xE0, 0x00], name: 'invalid trail 3' }, + { encoding: 'utf-8', input: [0xE0, 0xC0], name: 'invalid trail 4' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0x00], name: 'invalid trail 5' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0xC0], name: 'invalid trail 6' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], + name: '> 0x10FFFF' }, + { encoding: 'utf-8', input: [0xFE, 0x80, 0x80, 0x80, 0x80, 0x80], + name: 'obsolete lead byte' }, + // Overlong encodings + { encoding: 'utf-8', input: [0xC0, 0x80], name: 'overlong U+0000 - 2 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0x80], + name: 'overlong U+0000 - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 6 bytes' }, + { encoding: 'utf-8', input: [0xC1, 0xBF], name: 'overlong U+007F - 2 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x81, 0xBF], + name: 'overlong U+007F - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 6 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x9F, 0xBF], + name: 'overlong U+07FF - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 6 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 6 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x84, 0x8F, 0xBF, 0xBF], + name: 'overlong U+10FFFF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x84, 0x8F, 0xBF, 0xBF], + name: 'overlong U+10FFFF - 6 bytes' }, + // UTF-16 surrogates encoded as code points in UTF-8 + { encoding: 'utf-8', input: [0xED, 0xA0, 0x80], name: 'lead surrogate' }, + { encoding: 'utf-8', input: [0xED, 0xB0, 0x80], name: 'trail surrogate' }, + { encoding: 'utf-8', input: [0xED, 0xA0, 0x80, 0xED, 0xB0, 0x80], + name: 'surrogate pair' }, + { encoding: 'utf-16le', input: [0x00], name: 'truncated code unit' }, + // Mismatched UTF-16 surrogates are exercised in utf16-surrogates.html + // FIXME: Add legacy encoding cases +]; + +bad.forEach((t) => { + assert.throws( + () => { + new TextDecoder(t.encoding, { fatal: true }) + .decode(new Uint8Array(t.input)); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js new file mode 100644 index 00000000000000..73469bad530048 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/7f567fa29c/encoding/textdecoder-ignorebom.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +const cases = [ + { + encoding: 'utf-8', + bytes: [0xEF, 0xBB, 0xBF, 0x61, 0x62, 0x63] + }, + { + encoding: 'utf-16le', + bytes: [0xFF, 0xFE, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00] + }, +]; + +cases.forEach((testCase) => { + const BOM = '\uFEFF'; + let decoder = new TextDecoder(testCase.encoding, { ignoreBOM: true }); + const bytes = new Uint8Array(testCase.bytes); + assert.strictEqual(decoder.decode(bytes), `${BOM}abc`); + decoder = new TextDecoder(testCase.encoding, { ignoreBOM: false }); + assert.strictEqual(decoder.decode(bytes), 'abc'); + decoder = new TextDecoder(testCase.encoding); + assert.strictEqual(decoder.decode(bytes), 'abc'); +}); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js new file mode 100644 index 00000000000000..5fc7ece1142db0 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js @@ -0,0 +1,45 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/fa9436d12c/encoding/textdecoder-streaming.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +const string = + '\x00123ABCabc\x80\xFF\u0100\u1000\uFFFD\uD800\uDC00\uDBFF\uDFFF'; +const octets = { + 'utf-8': [ + 0x00, 0x31, 0x32, 0x33, 0x41, 0x42, 0x43, 0x61, 0x62, 0x63, 0xc2, 0x80, + 0xc3, 0xbf, 0xc4, 0x80, 0xe1, 0x80, 0x80, 0xef, 0xbf, 0xbd, 0xf0, 0x90, + 0x80, 0x80, 0xf4, 0x8f, 0xbf, 0xbf], + 'utf-16le': [ + 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x41, 0x00, 0x42, 0x00, + 0x43, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x80, 0x00, 0xFF, 0x00, + 0x00, 0x01, 0x00, 0x10, 0xFD, 0xFF, 0x00, 0xD8, 0x00, 0xDC, 0xFF, 0xDB, + 0xFF, 0xDF] +}; + +Object.keys(octets).forEach((encoding) => { + for (let len = 1; len <= 5; ++len) { + const encoded = octets[encoding]; + const decoder = new TextDecoder(encoding); + let out = ''; + for (let i = 0; i < encoded.length; i += len) { + const sub = []; + for (let j = i; j < encoded.length && j < i + len; ++j) + sub.push(encoded[j]); + out += decoder.decode(new Uint8Array(sub), { stream: true }); + } + out += decoder.decode(); + assert.strictEqual(out, string); + } +}); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js new file mode 100644 index 00000000000000..afe542dfd97929 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js @@ -0,0 +1,63 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-utf16-surrogates.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); + +const bad = [ + { + encoding: 'utf-16le', + input: [0x00, 0xd8], + expected: '\uFFFD', + name: 'lone surrogate lead' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc], + expected: '\uFFFD', + name: 'lone surrogate trail' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xd8, 0x00, 0x00], + expected: '\uFFFD\u0000', + name: 'unmatched surrogate lead' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc, 0x00, 0x00], + expected: '\uFFFD\u0000', + name: 'unmatched surrogate trail' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc, 0x00, 0xd8], + expected: '\uFFFD\uFFFD', + name: 'swapped surrogate pair' + }, +]; + +bad.forEach((t) => { + assert.throws( + () => { + new TextDecoder(t.encoding, { fatal: true }) + .decode(new Uint8Array(t.input)); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError' + } + ); +}); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-passive.js b/cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-passive.js new file mode 100644 index 00000000000000..b6330e8e106313 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-passive.js @@ -0,0 +1,72 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/AddEventListenerOptions-passive.html +// in order to define the `document` ourselves + +const { + fail, + ok, + strictEqual +} = require('assert'); + +{ + const document = new EventTarget(); + let supportsPassive = false; + const query_options = { + get passive() { + supportsPassive = true; + return false; + }, + get dummy() { + fail('dummy value getter invoked'); + return false; + } + }; + + document.addEventListener('test_event', null, query_options); + ok(supportsPassive); + + supportsPassive = false; + document.removeEventListener('test_event', null, query_options); + strictEqual(supportsPassive, false); +} +{ + function testPassiveValue(optionsValue, expectedDefaultPrevented) { + const document = new EventTarget(); + let defaultPrevented; + function handler(e) { + if (e.defaultPrevented) { + fail('Event prematurely marked defaultPrevented'); + } + e.preventDefault(); + defaultPrevented = e.defaultPrevented; + } + document.addEventListener('test', handler, optionsValue); + // TODO the WHATWG test is more extensive here and tests dispatching on + // document.body, if we ever support getParent we should amend this + const ev = new Event('test', { bubbles: true, cancelable: true }); + const uncanceled = document.dispatchEvent(ev); + + strictEqual(defaultPrevented, expectedDefaultPrevented); + strictEqual(uncanceled, !expectedDefaultPrevented); + + document.removeEventListener('test', handler, optionsValue); + } + testPassiveValue(undefined, true); + testPassiveValue({}, true); + testPassiveValue({ passive: false }, true); + + common.skip('TODO: passive listeners is still broken'); + testPassiveValue({ passive: 1 }, false); + testPassiveValue({ passive: true }, false); + testPassiveValue({ passive: 0 }, true); +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-signal.js b/cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-signal.js new file mode 100644 index 00000000000000..a512facb57d837 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-events-add-event-listener-options-signal.js @@ -0,0 +1,166 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +const { + strictEqual, +} = require('assert'); + +// Manually ported from: wpt@dom/events/AddEventListenerOptions-signal.any.js + +{ + // Passing an AbortSignal to addEventListener does not prevent + // removeEventListener + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', handler, { signal: controller.signal }); + et.dispatchEvent(new Event('test')); + strictEqual(count, 1, 'Adding a signal still adds a listener'); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'The listener was not added with the once flag'); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'Aborting on the controller removes the listener'); + // See: https://github.com/nodejs/node/pull/37696 , adding an event listener + // should always return undefined. + strictEqual( + et.addEventListener('test', handler, { signal: controller.signal }), + undefined); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'Passing an aborted signal never adds the handler'); +} + +{ + // Passing an AbortSignal to addEventListener works with the once flag + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', handler, { signal: controller.signal }); + et.removeEventListener('test', handler); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Removing a once listener works with a passed signal + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('test', handler, options); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('test', handler, options); + et.removeEventListener('test', handler); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Passing an AbortSignal to multiple listeners + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('first', handler, options); + et.addEventListener('second', handler, options); + controller.abort(); + et.dispatchEvent(new Event('first')); + et.dispatchEvent(new Event('second')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Passing an AbortSignal to addEventListener works with the capture flag + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, capture: true }; + et.addEventListener('test', handler, options); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Aborting from a listener does not call future listeners + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal }; + et.addEventListener('test', () => { + controller.abort(); + }, options); + et.addEventListener('test', handler, options); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Adding then aborting a listener in another listener does not call it + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', () => { + et.addEventListener('test', handler, { signal: controller.signal }); + controller.abort(); + }, { signal: controller.signal }); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Aborting from a nested listener should remove it + const et = new EventTarget(); + const ac = new AbortController(); + let count = 0; + et.addEventListener('foo', () => { + et.addEventListener('foo', () => { + count++; + if (count > 5) ac.abort(); + et.dispatchEvent(new Event('foo')); + }, { signal: ac.signal }); + et.dispatchEvent(new Event('foo')); + }, { once: true }); + et.dispatchEvent(new Event('foo')); +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-events-customevent.js b/cli/tests/node_compat/test/parallel/test-whatwg-events-customevent.js new file mode 100644 index 00000000000000..9c6fbca6250bb5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-events-customevent.js @@ -0,0 +1,40 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const { strictEqual, throws, equal } = require('assert'); + +// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/CustomEvent.html +// in order to define the `document` ourselves + +{ + const type = 'foo'; + const target = new EventTarget(); + + target.addEventListener(type, common.mustCall((evt) => { + strictEqual(evt.type, type); + })); + + target.dispatchEvent(new Event(type)); +} + +{ + throws(() => { + new Event(); + }, TypeError); +} + +{ + const event = new Event('foo'); + equal(event.type, 'foo'); + equal(event.bubbles, false); + equal(event.cancelable, false); + equal(event.detail, null); +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-deepequal.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-deepequal.js new file mode 100644 index 00000000000000..f851699a8d2569 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-deepequal.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// This tests that the internal flags in URL objects are consistent, as manifest +// through assert libraries. +// See https://github.com/nodejs/node/issues/24211 + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); + +assert.deepStrictEqual( + new URL('./foo', 'https://example.com/'), + new URL('https://example.com/foo') +); +assert.deepStrictEqual( + new URL('./foo', 'https://user:pass@example.com/'), + new URL('https://user:pass@example.com/foo') +); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-domainto.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-domainto.js new file mode 100644 index 00000000000000..225f8a05c9fd0e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-domainto.js @@ -0,0 +1,64 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const { domainToASCII, domainToUnicode } = require('url'); + +const tests = require('../fixtures/url-idna'); +const fixtures = require('../common/fixtures'); +const wptToASCIITests = require( + fixtures.path('wpt', 'url', 'resources', 'toascii.json') +); + +{ + const expectedError = { code: 'ERR_MISSING_ARGS', name: 'TypeError' }; + assert.throws(() => domainToASCII(), expectedError); + assert.throws(() => domainToUnicode(), expectedError); + assert.strictEqual(domainToASCII(undefined), 'undefined'); + assert.strictEqual(domainToUnicode(undefined), 'undefined'); +} + +{ + for (const [i, { ascii, unicode }] of tests.entries()) { + assert.strictEqual(ascii, domainToASCII(unicode), + `domainToASCII(${i + 1})`); + assert.strictEqual(unicode, domainToUnicode(ascii), + `domainToUnicode(${i + 1})`); + assert.strictEqual(ascii, domainToASCII(domainToUnicode(ascii)), + `domainToASCII(domainToUnicode(${i + 1}))`); + assert.strictEqual(unicode, domainToUnicode(domainToASCII(unicode)), + `domainToUnicode(domainToASCII(${i + 1}))`); + } +} + +{ + for (const [i, test] of wptToASCIITests.entries()) { + if (typeof test === 'string') + continue; // skip comments + const { comment, input, output } = test; + let caseComment = `Case ${i + 1}`; + if (comment) + caseComment += ` (${comment})`; + if (output === null) { + assert.strictEqual(domainToASCII(input), '', caseComment); + assert.strictEqual(domainToUnicode(input), '', caseComment); + } else { + assert.strictEqual(domainToASCII(input), output, caseComment); + const roundtripped = domainToASCII(domainToUnicode(input)); + assert.strictEqual(roundtripped, output, caseComment); + } + } +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-global.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-global.js new file mode 100644 index 00000000000000..0a6a8e40399960 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-global.js @@ -0,0 +1,33 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); + +assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(global, 'URL'), + { + value: URL, + writable: true, + configurable: true, + enumerable: false + } +); + +assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(global, 'URLSearchParams'), + { + value: URLSearchParams, + writable: true, + configurable: true, + enumerable: false + } +); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-href-side-effect.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-href-side-effect.js new file mode 100644 index 00000000000000..12c3ef002e500d --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-href-side-effect.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests below are not from WPT. +require('../common'); +const assert = require('assert'); + +const ref = new URL('http://example.com/path'); +const url = new URL('http://example.com/path'); +assert.throws(() => { + url.href = ''; +}, { + name: 'TypeError' +}); + +assert.deepStrictEqual(url, ref); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-inspect.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-inspect.js new file mode 100644 index 00000000000000..7a92d5ea3e57c4 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-inspect.js @@ -0,0 +1,75 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const util = require('util'); +const assert = require('assert'); + +const url = new URL('https://username:password@host.name:8080/path/name/?que=ry#hash'); + +assert.strictEqual( + util.inspect(url), + `URL { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + origin: 'https://host.name:8080', + protocol: 'https:', + username: 'username', + password: 'password', + host: 'host.name:8080', + hostname: 'host.name', + port: '8080', + pathname: '/path/name/', + search: '?que=ry', + searchParams: URLSearchParams { 'que' => 'ry' }, + hash: '#hash' +}`); + +assert.strictEqual( + util.inspect(url, { showHidden: true }), + `URL { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + origin: 'https://host.name:8080', + protocol: 'https:', + username: 'username', + password: 'password', + host: 'host.name:8080', + hostname: 'host.name', + port: '8080', + pathname: '/path/name/', + search: '?que=ry', + searchParams: URLSearchParams { 'que' => 'ry' }, + hash: '#hash', + cannotBeBase: false, + special: true, + [Symbol(context)]: URLContext { + flags: 2032, + scheme: 'https:', + username: 'username', + password: 'password', + host: 'host.name', + port: 8080, + path: [ 'path', 'name', '', [length]: 3 ], + query: 'que=ry', + fragment: 'hash' + } +}`); + +assert.strictEqual( + util.inspect({ a: url }, { depth: 0 }), + '{ a: [URL] }'); + +class MyURL extends URL {} +assert(util.inspect(new MyURL(url.href)).startsWith('MyURL {')); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-parsing.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-parsing.js new file mode 100644 index 00000000000000..7af0759566b611 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-parsing.js @@ -0,0 +1,87 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const tests = require( + fixtures.path('wpt', 'url', 'resources', 'urltestdata.json') +); + +const originalFailures = tests.filter((test) => test.failure); + +const typeFailures = [ + { input: '' }, + { input: 'test' }, + { input: undefined }, + { input: 0 }, + { input: true }, + { input: false }, + { input: null }, + { input: new Date() }, + { input: new RegExp() }, + { input: 'test', base: null }, + { input: 'http://nodejs.org', base: null }, + { input: () => {} }, +]; + +// See https://github.com/w3c/web-platform-tests/pull/10955 +// > If `failure` is true, parsing `about:blank` against `base` +// > must give failure. This tests that the logic for converting +// > base URLs into strings properly fails the whole parsing +// > algorithm if the base URL cannot be parsed. +const aboutBlankFailures = originalFailures + .map((test) => ({ + input: 'about:blank', + base: test.input, + failure: true + })); + +const failureTests = originalFailures + .concat(typeFailures) + .concat(aboutBlankFailures); + +const expectedError = { code: 'ERR_INVALID_URL', name: 'TypeError' }; + +for (const test of failureTests) { + assert.throws( + () => new URL(test.input, test.base), + (error) => { + assert.throws(() => { throw error; }, expectedError); + assert.strictEqual(`${error}`, 'TypeError [ERR_INVALID_URL]: Invalid URL'); + assert.strictEqual(error.message, 'Invalid URL'); + return true; + }); +} + +const additional_tests = + require(fixtures.path('url-tests-additional.js')); + +for (const test of additional_tests) { + const url = new URL(test.url); + if (test.href) assert.strictEqual(url.href, test.href); + if (test.origin) assert.strictEqual(url.origin, test.origin); + if (test.protocol) assert.strictEqual(url.protocol, test.protocol); + if (test.username) assert.strictEqual(url.username, test.username); + if (test.password) assert.strictEqual(url.password, test.password); + if (test.hostname) assert.strictEqual(url.hostname, test.hostname); + if (test.host) assert.strictEqual(url.host, test.host); + if (test.port !== undefined) assert.strictEqual(url.port, test.port); + if (test.pathname) assert.strictEqual(url.pathname, test.pathname); + if (test.search) assert.strictEqual(url.search, test.search); + if (test.hash) assert.strictEqual(url.hash, test.hash); +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-setters.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-setters.js new file mode 100644 index 00000000000000..c0b4e41bdc3ee5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-setters.js @@ -0,0 +1,67 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const assert = require('assert'); +const { test, assert_equals } = require('../common/wpt').harness; +const fixtures = require('../common/fixtures'); + +// TODO(joyeecheung): we should submit these to the upstream +const additionalTestCases = + require(fixtures.path('url-setter-tests-additional.js')); + +{ + for (const attributeToBeSet in additionalTestCases) { + if (attributeToBeSet === 'comment') { + continue; + } + const testCases = additionalTestCases[attributeToBeSet]; + for (const testCase of testCases) { + let name = `Setting <${testCase.href}>.${attributeToBeSet}` + + ` = "${testCase.new_value}"`; + if ('comment' in testCase) { + name += ` ${testCase.comment}`; + } + test(function() { + const url = new URL(testCase.href); + url[attributeToBeSet] = testCase.new_value; + for (const attribute in testCase.expected) { + assert_equals(url[attribute], testCase.expected[attribute]); + } + }, `URL: ${name}`); + } + } +} + +{ + const url = new URL('http://example.com/'); + const obj = { + toString() { throw new Error('toString'); }, + valueOf() { throw new Error('valueOf'); } + }; + const sym = Symbol(); + const props = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(url)); + for (const [name, { set }] of Object.entries(props)) { + if (set) { + assert.throws(() => url[name] = obj, + /^Error: toString$/, + `url.${name} = { toString() { throw ... } }`); + assert.throws(() => url[name] = sym, + /^TypeError: Cannot convert a Symbol value to a string$/, + `url.${name} = ${String(sym)}`); + } + } +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-tostringtag.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-tostringtag.js new file mode 100644 index 00000000000000..7455add352c9f5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-custom-tostringtag.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); + +const toString = Object.prototype.toString; + +const url = new URL('http://example.org'); +const sp = url.searchParams; +const spIterator = sp.entries(); + +const test = [ + [url, 'URL'], + [sp, 'URLSearchParams'], + [spIterator, 'URLSearchParams Iterator'], + // Web IDL spec says we have to return 'URLPrototype', but it is too + // expensive to implement; therefore, use Chrome's behavior for now, until + // spec is changed. + [Object.getPrototypeOf(url), 'URL'], + [Object.getPrototypeOf(sp), 'URLSearchParams'], + [Object.getPrototypeOf(spIterator), 'URLSearchParams Iterator'], +]; + +test.forEach(([obj, expected]) => { + assert.strictEqual(obj[Symbol.toStringTag], expected, + `${obj[Symbol.toStringTag]} !== ${expected}`); + const str = toString.call(obj); + assert.strictEqual(str, `[object ${expected}]`, + `${str} !== [object ${expected}]`); +}); diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-override-hostname.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-override-hostname.js new file mode 100644 index 00000000000000..b60ffba564c2f5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-override-hostname.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); + +{ + const url = new (class extends URL { get hostname() { return 'bar.com'; } })('http://foo.com/'); + assert.strictEqual(url.href, 'http://foo.com/'); + assert.strictEqual(url.toString(), 'http://foo.com/'); + assert.strictEqual(url.toJSON(), 'http://foo.com/'); + assert.strictEqual(url.hash, ''); + assert.strictEqual(url.host, 'foo.com'); + assert.strictEqual(url.hostname, 'bar.com'); + assert.strictEqual(url.origin, 'http://foo.com'); + assert.strictEqual(url.password, ''); + assert.strictEqual(url.protocol, 'http:'); + assert.strictEqual(url.username, ''); + assert.strictEqual(url.search, ''); + assert.strictEqual(url.searchParams.toString(), ''); +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-properties.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-properties.js new file mode 100644 index 00000000000000..8a4f4e57b86b75 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-properties.js @@ -0,0 +1,111 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const { URL, URLSearchParams } = require('url'); + +[ + { name: 'toString' }, + { name: 'toJSON' }, + // Deno's URL doesn't support nodejs custom inspect + // { name: Symbol.for('nodejs.util.inspect.custom') }, +].forEach(({ name }) => { + testMethod(URL.prototype, name); +}); + +[ + { name: 'href' }, + { name: 'protocol' }, + { name: 'username' }, + { name: 'password' }, + { name: 'host' }, + { name: 'hostname' }, + { name: 'port' }, + { name: 'pathname' }, + { name: 'search' }, + { name: 'hash' }, + { name: 'origin', readonly: true }, + { name: 'searchParams', readonly: true }, +].forEach(({ name, readonly = false }) => { + testAccessor(URL.prototype, name, readonly); +}); + +[ + { name: 'append' }, + { name: 'delete' }, + { name: 'get' }, + { name: 'getAll' }, + { name: 'has' }, + { name: 'set' }, + { name: 'sort' }, + { name: 'entries' }, + { name: 'forEach' }, + { name: 'keys' }, + { name: 'values' }, + { name: 'toString' }, + { name: Symbol.iterator, methodName: 'entries' }, + // Deno's URL doesn't support nodejs custom inspect + // { name: Symbol.for('nodejs.util.inspect.custom') }, +].forEach(({ name, methodName }) => { + testMethod(URLSearchParams.prototype, name, methodName); +}); + +function stringifyName(name) { + if (typeof name === 'symbol') { + const { description } = name; + if (description === undefined) { + return ''; + } + return `[${description}]`; + } + + return name; +} + +function testMethod(target, name, methodName = stringifyName(name)) { + const desc = Object.getOwnPropertyDescriptor(target, name); + assert.notStrictEqual(desc, undefined); + assert.strictEqual(desc.enumerable, typeof name === 'string'); + + const { value } = desc; + assert.strictEqual(typeof value, 'function'); + assert.strictEqual(value.name, methodName); + /* This can't pass with Deno's URL/URLSearchParams + assert.strictEqual( + Object.prototype.hasOwnProperty.call(value, 'prototype'), + false, + ); + */ +} + +function testAccessor(target, name, readonly = false) { + const desc = Object.getOwnPropertyDescriptor(target, name); + assert.notStrictEqual(desc, undefined); + assert.strictEqual(desc.enumerable, typeof name === 'string'); + + const methodName = stringifyName(name); + const { get, set } = desc; + assert.strictEqual(typeof get, 'function'); + assert.strictEqual(get.name, `get ${methodName}`); + assert.strictEqual( + Object.prototype.hasOwnProperty.call(get, 'prototype'), + false, + ); + + if (readonly) { + assert.strictEqual(set, undefined); + } else { + assert.strictEqual(typeof set, 'function'); + assert.strictEqual(set.name, `set ${methodName}`); + assert.strictEqual( + Object.prototype.hasOwnProperty.call(set, 'prototype'), + false, + ); + } +} diff --git a/cli/tests/node_compat/test/parallel/test-whatwg-url-toascii.js b/cli/tests/node_compat/test/parallel/test-whatwg-url-toascii.js new file mode 100644 index 00000000000000..82ac527f1f964a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-whatwg-url-toascii.js @@ -0,0 +1,93 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const fixtures = require('../common/fixtures'); +const { test, assert_equals, assert_throws } = require('../common/wpt').harness; + +const request = { + response: require( + fixtures.path('wpt', 'url', 'resources', 'toascii.json') + ) +}; + +// The following tests are copied from WPT. Modifications to them should be +// upstreamed first. +// Refs: https://github.com/w3c/web-platform-tests/blob/4839a0a804/url/toascii.window.js +// License: http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html + +/* eslint-disable */ +// async_test(t => { +// const request = new XMLHttpRequest() +// request.open("GET", "toascii.json") +// request.send() +// request.responseType = "json" +// request.onload = t.step_func_done(() => { + runTests(request.response) +// }) +// }, "Loading data…") + +function makeURL(type, input) { + input = "https://" + input + "/x" + if(type === "url") { + return new URL(input) + } else { + const url = document.createElement(type) + url.href = input + return url + } +} + +function runTests(tests) { + for(var i = 0, l = tests.length; i < l; i++) { + let hostTest = tests[i] + if (typeof hostTest === "string") { + continue // skip comments + } + const typeName = { "url": "URL", "a": "", "area": "" } + // ;["url", "a", "area"].forEach((type) => { + ;["url"].forEach((type) => { + test(() => { + if(hostTest.output !== null) { + const url = makeURL("url", hostTest.input) + assert_equals(url.host, hostTest.output) + assert_equals(url.hostname, hostTest.output) + assert_equals(url.pathname, "/x") + assert_equals(url.href, "https://" + hostTest.output + "/x") + } else { + if(type === "url") { + assert_throws(new TypeError, () => makeURL("url", hostTest.input)) + } else { + const url = makeURL(type, hostTest.input) + assert_equals(url.host, "") + assert_equals(url.hostname, "") + assert_equals(url.pathname, "") + assert_equals(url.href, "https://" + hostTest.input + "/x") + } + } + }, hostTest.input + " (using " + typeName[type] + ")") + ;["host", "hostname"].forEach((val) => { + test(() => { + const url = makeURL(type, "x") + url[val] = hostTest.input + if(hostTest.output !== null) { + assert_equals(url[val], hostTest.output) + } else { + assert_equals(url[val], "x") + } + }, hostTest.input + " (using " + typeName[type] + "." + val + ")") + }) + }) + } +} +/* eslint-enable */ diff --git a/cli/tests/node_compat/test/parallel/test-zlib-close-after-error.js b/cli/tests/node_compat/test/parallel/test-zlib-close-after-error.js new file mode 100644 index 00000000000000..c4de28ae1d0862 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-close-after-error.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// https://github.com/nodejs/node/issues/6034 + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const decompress = zlib.createGunzip(15); + +decompress.on('error', common.mustCall((err) => { + assert.strictEqual(decompress._closed, true); + decompress.close(); +})); + +assert.strictEqual(decompress._closed, false); +decompress.write('something invalid'); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-close-after-write.js b/cli/tests/node_compat/test/parallel/test-zlib-close-after-write.js new file mode 100644 index 00000000000000..26c07c266e7ed2 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-close-after-write.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +zlib.gzip('hello', common.mustCall((err, out) => { + const unzip = zlib.createGunzip(); + unzip.write(out); + unzip.close(common.mustCall()); +})); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-convenience-methods.js b/cli/tests/node_compat/test/parallel/test-zlib-convenience-methods.js new file mode 100644 index 00000000000000..cf6694b1fc5909 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-convenience-methods.js @@ -0,0 +1,144 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test convenience methods with and without options supplied + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Must be a multiple of 4 characters in total to test all ArrayBufferView +// types. +const expectStr = 'blah'.repeat(8); +const expectBuf = Buffer.from(expectStr); + +const opts = { + level: 9, + chunkSize: 1024, +}; + +const optsInfo = { + info: true +}; + +for (const [type, expect] of [ + ['string', expectStr], + ['Buffer', expectBuf], + // FIXME(bartlomieju): + // ...common.getBufferSources(expectBuf).map((obj) => + // [obj[Symbol.toStringTag], obj] + // ), +]) { + for (const method of [ + ['gzip', 'gunzip', 'Gzip', 'Gunzip'], + ['gzip', 'unzip', 'Gzip', 'Unzip'], + ['deflate', 'inflate', 'Deflate', 'Inflate'], + ['deflateRaw', 'inflateRaw', 'DeflateRaw', 'InflateRaw'], + // FIXME(bartlomieju): + // ['brotliCompress', 'brotliDecompress', + // 'BrotliCompress', 'BrotliDecompress'], + ]) { + zlib[method[0]](expect, opts, common.mustCall((err, result) => { + zlib[method[1]](result, opts, common.mustCall((err, result) => { + assert.strictEqual(result.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} with options.`); + })); + })); + + zlib[method[0]](expect, common.mustCall((err, result) => { + zlib[method[1]](result, common.mustCall((err, result) => { + assert.strictEqual(result.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} without options.`); + })); + })); + + // FIXME(bartlomieju): + // zlib[method[0]](expect, optsInfo, common.mustCall((err, result) => { + // assert.ok(result.engine instanceof zlib[method[2]], + // `Should get engine ${method[2]} after ${method[0]} ` + + // `${type} with info option.`); + + // const compressed = result.buffer; + // zlib[method[1]](compressed, optsInfo, common.mustCall((err, result) => { + // assert.strictEqual(result.buffer.toString(), expectStr, + // `Should get original string after ${method[0]}/` + + // `${method[1]} ${type} with info option.`); + // assert.ok(result.engine instanceof zlib[method[3]], + // `Should get engine ${method[3]} after ${method[0]} ` + + // `${type} with info option.`); + // })); + // })); + + { + const compressed = zlib[`${method[0]}Sync`](expect, opts); + const decompressed = zlib[`${method[1]}Sync`](compressed, opts); + assert.strictEqual(decompressed.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} with options.`); + } + + + { + const compressed = zlib[`${method[0]}Sync`](expect); + const decompressed = zlib[`${method[1]}Sync`](compressed); + assert.strictEqual(decompressed.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} without options.`); + } + + // FIXME(bartlomieju): + // { + // const compressed = zlib[`${method[0]}Sync`](expect, optsInfo); + // assert.ok(compressed.engine instanceof zlib[method[2]], + // `Should get engine ${method[2]} after ${method[0]} ` + + // `${type} with info option.`); + // const decompressed = zlib[`${method[1]}Sync`](compressed.buffer, + // optsInfo); + // assert.strictEqual(decompressed.buffer.toString(), expectStr, + // `Should get original string after ${method[0]}Sync/` + + // `${method[1]}Sync ${type} without options.`); + // assert.ok(decompressed.engine instanceof zlib[method[3]], + // `Should get engine ${method[3]} after ${method[0]} ` + + // `${type} with info option.`); + // } + } +} + +// FIXME(bartlomieju): +// assert.throws( +// () => zlib.gzip('abc'), +// { +// code: 'ERR_INVALID_ARG_TYPE', +// name: 'TypeError', +// message: 'The "callback" argument must be of type function. ' + +// 'Received undefined' +// } +// ); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-deflate-raw-inherits.js b/cli/tests/node_compat/test/parallel/test-zlib-deflate-raw-inherits.js new file mode 100644 index 00000000000000..5af18a1b4ce749 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-deflate-raw-inherits.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const { DeflateRaw } = require('zlib'); +const { Readable } = require('stream'); + +// Validates that zlib.DeflateRaw can be inherited +// with Object.setPrototypeOf + +function NotInitialized(options) { + DeflateRaw.call(this, options); + this.prop = true; +} +Object.setPrototypeOf(NotInitialized.prototype, DeflateRaw.prototype); +Object.setPrototypeOf(NotInitialized, DeflateRaw); + +const dest = new NotInitialized(); + +const read = new Readable({ + read() { + this.push(Buffer.from('a test string')); + this.push(null); + } +}); + +read.pipe(dest); +dest.resume(); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-destroy-pipe.js b/cli/tests/node_compat/test/parallel/test-zlib-destroy-pipe.js new file mode 100644 index 00000000000000..d4fa85a924c3db --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-destroy-pipe.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const zlib = require('zlib'); +const { Writable } = require('stream'); + +// Verify that the zlib transform does not error in case +// it is destroyed with data still in flight + +const ts = zlib.createGzip(); + +const ws = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + setImmediate(cb); + ts.destroy(); + }) +}); + +const buf = Buffer.allocUnsafe(1024 * 1024 * 20); +ts.end(buf); +ts.pipe(ws); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-empty-buffer.js b/cli/tests/node_compat/test/parallel/test-zlib-empty-buffer.js new file mode 100644 index 00000000000000..2281ba88e5574b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-empty-buffer.js @@ -0,0 +1,35 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const { inspect, promisify } = require('util'); +const assert = require('assert'); +const emptyBuffer = Buffer.alloc(0); + +(async function() { + for (const [ compress, decompress, method ] of [ + [ zlib.deflateRawSync, zlib.inflateRawSync, 'raw sync' ], + [ zlib.deflateSync, zlib.inflateSync, 'deflate sync' ], + [ zlib.gzipSync, zlib.gunzipSync, 'gzip sync' ], + // FIXME(bartlomieju): + // [ zlib.brotliCompressSync, zlib.brotliDecompressSync, 'br sync' ], + [ promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), 'raw' ], + [ promisify(zlib.deflate), promisify(zlib.inflate), 'deflate' ], + [ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ], + // FIXME(bartlomieju): + // [ promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), 'br' ], + ]) { + const compressed = await compress(emptyBuffer); + const decompressed = await decompress(compressed); + assert.deepStrictEqual( + emptyBuffer, decompressed, + `Expected ${inspect(compressed)} to match ${inspect(decompressed)} ` + + `to match for ${method}`); + } +})().then(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-from-string.js b/cli/tests/node_compat/test/parallel/test-zlib-from-string.js new file mode 100644 index 00000000000000..e1003f214f5929 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-from-string.js @@ -0,0 +1,90 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test compressing and uncompressing a string with zlib + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; +const expectedBase64Deflate = 'eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpmX' + + 'AKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkD' + + 'lKWLJWkncJG5403HQXAkT3Jw29B9uIEmToMukglZ0vS6oc' + + 'iBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491LofoA' + + 'bWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+' + + 'RSQsGoC7dn2t/xjhduTA1NWyQIZR0pbHwMDatnD+crPqKS' + + 'qGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqqq' + + 'fKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOH' + + 'aFE4RdpnGavKtrB5xzfO/Ll9'; +const expectedBase64Gzip = 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4' + + '96pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajN' + + 'Pk6QOUpYslaSdwkbnjTcdBcCRPcnDb0H24gSZOgy6SCVnS9Lq' + + 'hyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYrLj3Uuh+gBt' + + 'aHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCw' + + 'agLt2fa3/GOF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+' + + 'eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9hN9ory+qqp8oG00yF7c5' + + 'mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2' + + 'sHnHNzRtagj5AQAA'; + +zlib.deflate(inputString, common.mustCall((err, buffer) => { + zlib.inflate(buffer, common.mustCall((err, inflated) => { + assert.strictEqual(inflated.toString(), inputString); + })); +})); + +zlib.gzip(inputString, common.mustCall((err, buffer) => { + // Can't actually guarantee that we'll get exactly the same + // deflated bytes when we compress a string, since the header + // depends on stuff other than the input string itself. + // However, decrypting it should definitely yield the same + // result that we're expecting, and this should match what we get + // from inflating the known valid deflate data. + zlib.gunzip(buffer, common.mustCall((err, gunzipped) => { + assert.strictEqual(gunzipped.toString(), inputString); + })); +})); + +let buffer = Buffer.from(expectedBase64Deflate, 'base64'); +zlib.unzip(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); + +buffer = Buffer.from(expectedBase64Gzip, 'base64'); +zlib.unzip(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-invalid-input.js b/cli/tests/node_compat/test/parallel/test-zlib-invalid-input.js new file mode 100644 index 00000000000000..d8ecae521cae2a --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-invalid-input.js @@ -0,0 +1,68 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test uncompressing invalid input + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const nonStringInputs = [ + 1, + true, + { a: 1 }, + ['a'], +]; + +// zlib.Unzip classes need to get valid data, or else they'll throw. +const unzips = [ + zlib.Unzip(), + zlib.Gunzip(), + zlib.Inflate(), + zlib.InflateRaw(), + // FIXME(bartlomieju): + // zlib.BrotliDecompress(), +]; + +nonStringInputs.forEach(common.mustCall((input) => { + assert.throws(() => { + zlib.gunzip(input); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' + }); +}, nonStringInputs.length)); + +unzips.forEach(common.mustCall((uz, i) => { + uz.on('error', common.mustCall()); + uz.on('end', common.mustNotCall); + + // This will trigger error event + uz.write('this is not valid compressed data.'); +}, unzips.length)); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-no-stream.js b/cli/tests/node_compat/test/parallel/test-zlib-no-stream.js new file mode 100644 index 00000000000000..9e5a6b1a2ee1a5 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-no-stream.js @@ -0,0 +1,21 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +/* eslint-disable node-core/required-modules */ +/* eslint-disable node-core/require-common-first */ + +'use strict'; + +// We are not loading common because it will load the stream module, +// defeating the purpose of this test. + +const { gzipSync } = require('zlib'); + +// Avoid regressions such as https://github.com/nodejs/node/issues/36615 + +// This must not throw +gzipSync('fooobar'); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-random-byte-pipes.js b/cli/tests/node_compat/test/parallel/test-zlib-random-byte-pipes.js new file mode 100644 index 00000000000000..56409d4117766f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-random-byte-pipes.js @@ -0,0 +1,166 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); +const stream = require('stream'); +const zlib = require('zlib'); + +const Stream = stream.Stream; + +// Emit random bytes, and keep a shasum +class RandomReadStream extends Stream { + constructor(opt) { + super(); + + this.readable = true; + this._paused = false; + this._processing = false; + + this._hasher = crypto.createHash('sha1'); + opt = opt || {}; + + // base block size. + opt.block = opt.block || 256 * 1024; + + // Total number of bytes to emit + opt.total = opt.total || 256 * 1024 * 1024; + this._remaining = opt.total; + + // How variable to make the block sizes + opt.jitter = opt.jitter || 1024; + + this._opt = opt; + + this._process = this._process.bind(this); + + process.nextTick(this._process); + } + + pause() { + this._paused = true; + this.emit('pause'); + } + + resume() { + // console.error("rrs resume"); + this._paused = false; + this.emit('resume'); + this._process(); + } + + _process() { + if (this._processing) return; + if (this._paused) return; + + this._processing = true; + + if (!this._remaining) { + this._hash = this._hasher.digest('hex').toLowerCase().trim(); + this._processing = false; + + this.emit('end'); + return; + } + + // Figure out how many bytes to output + // if finished, then just emit end. + let block = this._opt.block; + const jitter = this._opt.jitter; + if (jitter) { + block += Math.ceil(Math.random() * jitter - (jitter / 2)); + } + block = Math.min(block, this._remaining); + const buf = Buffer.allocUnsafe(block); + for (let i = 0; i < block; i++) { + buf[i] = Math.random() * 256; + } + + this._hasher.update(buf); + + this._remaining -= block; + + this._processing = false; + + this.emit('data', buf); + process.nextTick(this._process); + } +} + +// A filter that just verifies a shasum +class HashStream extends Stream { + constructor() { + super(); + this.readable = this.writable = true; + this._hasher = crypto.createHash('sha1'); + } + + write(c) { + // Simulate the way that an fs.ReadStream returns false + // on *every* write, only to resume a moment later. + this._hasher.update(c); + process.nextTick(() => this.resume()); + return false; + } + + resume() { + this.emit('resume'); + process.nextTick(() => this.emit('drain')); + } + + end(c) { + if (c) { + this.write(c); + } + this._hash = this._hasher.digest('hex').toLowerCase().trim(); + this.emit('data', this._hash); + this.emit('end'); + } +} + +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + // TODO(kt3k): Enable this when we support brotli in zlib + // [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); + const out = new HashStream(); + const gzip = createCompress(); + const gunz = createDecompress(); + + inp.pipe(gzip).pipe(gunz).pipe(out); + + out.on('data', common.mustCall((c) => { + assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`); + })); +} diff --git a/cli/tests/node_compat/test/parallel/test-zlib-sync-no-event.js b/cli/tests/node_compat/test/parallel/test-zlib-sync-no-event.js new file mode 100644 index 00000000000000..eaf058a9ca09a3 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-sync-no-event.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const assert = require('assert'); + +const message = 'Come on, Fhqwhgads.'; +const buffer = Buffer.from(message); + +const zipper = new zlib.Gzip(); +zipper.on('close', common.mustNotCall()); + +const zipped = zipper._processChunk(buffer, zlib.constants.Z_FINISH); + +const unzipper = new zlib.Gunzip(); +unzipper.on('close', common.mustNotCall()); + +const unzipped = unzipper._processChunk(zipped, zlib.constants.Z_FINISH); +assert.notStrictEqual(zipped.toString(), message); +assert.strictEqual(unzipped.toString(), message); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-truncated.js b/cli/tests/node_compat/test/parallel/test-zlib-truncated.js new file mode 100644 index 00000000000000..6d9f95b0c3a157 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-truncated.js @@ -0,0 +1,71 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Tests zlib streams with truncated compressed input + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; + +const errMessage = /unexpected end of file/; + +[ + { comp: 'gzip', decomp: 'gunzip', decompSync: 'gunzipSync' }, + { comp: 'gzip', decomp: 'unzip', decompSync: 'unzipSync' }, + { comp: 'deflate', decomp: 'inflate', decompSync: 'inflateSync' }, + { comp: 'deflateRaw', decomp: 'inflateRaw', decompSync: 'inflateRawSync' }, +].forEach(function(methods) { + zlib[methods.comp](inputString, function(err, compressed) { + assert.ifError(err); + const truncated = compressed.slice(0, compressed.length / 2); + const toUTF8 = (buffer) => buffer.toString('utf-8'); + + // sync sanity + const decompressed = zlib[methods.decompSync](compressed); + assert.strictEqual(toUTF8(decompressed), inputString); + + // async sanity + zlib[methods.decomp](compressed, function(err, result) { + assert.ifError(err); + assert.strictEqual(toUTF8(result), inputString); + }); + + // Sync truncated input test + assert.throws(function() { + zlib[methods.decompSync](truncated); + }, errMessage); + + // Async truncated input test + zlib[methods.decomp](truncated, function(err, result) { + assert.match(err.message, errMessage); + }); + + const syncFlushOpt = { finishFlush: zlib.constants.Z_SYNC_FLUSH }; + + // Sync truncated input test, finishFlush = Z_SYNC_FLUSH + const result = toUTF8(zlib[methods.decompSync](truncated, syncFlushOpt)); + assert.strictEqual(result, inputString.substr(0, result.length)); + + // Async truncated input test, finishFlush = Z_SYNC_FLUSH + zlib[methods.decomp](truncated, syncFlushOpt, function(err, decompressed) { + assert.ifError(err); + const result = toUTF8(decompressed); + assert.strictEqual(result, inputString.substr(0, result.length)); + }); + }); +}); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-unzip-one-byte-chunks.js b/cli/tests/node_compat/test/parallel/test-zlib-unzip-one-byte-chunks.js new file mode 100644 index 00000000000000..bc66b6696b01b9 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-unzip-one-byte-chunks.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const data = Buffer.concat([ + zlib.gzipSync('abc'), + zlib.gzipSync('def'), +]); + +const resultBuffers = []; + +const unzip = zlib.createUnzip() + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (data) => resultBuffers.push(data)) + .on('finish', common.mustCall(() => { + const unzipped = Buffer.concat(resultBuffers).toString(); + assert.strictEqual(unzipped, 'abcdef', + `'${unzipped}' should match 'abcdef' after zipping ` + + 'and unzipping'); + })); + +for (let i = 0; i < data.length; i++) { + // Write each single byte individually. + unzip.write(Buffer.from([data[i]])); +} + +unzip.end(); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-write-after-end.js b/cli/tests/node_compat/test/parallel/test-zlib-write-after-end.js new file mode 100644 index 00000000000000..8a9e9fbaa4e25b --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-write-after-end.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +// Regression test for https://github.com/nodejs/node/issues/30976 +// Writes to a stream should finish even after the readable side has been ended. + +const data = zlib.deflateRawSync('Welcome'); + +const inflate = zlib.createInflateRaw(); + +inflate.resume(); +inflate.write(data, common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.flush(common.mustCall()); diff --git a/cli/tests/node_compat/test/parallel/test-zlib-write-after-flush.js b/cli/tests/node_compat/test/parallel/test-zlib-write-after-flush.js new file mode 100644 index 00000000000000..6f33668c75e466 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-write-after-flush.js @@ -0,0 +1,57 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + // FIXME(bartlomieju): + // [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const gzip = createCompress(); + const gunz = createDecompress(); + + gzip.pipe(gunz); + + let output = ''; + const input = 'A line of data\n'; + gunz.setEncoding('utf8'); + gunz.on('data', (c) => output += c); + gunz.on('end', common.mustCall(() => { + assert.strictEqual(output, input); + })); + + // Make sure that flush/write doesn't trigger an assert failure + gzip.flush(); + gzip.write(input); + gzip.end(); + gunz.read(0); +} diff --git a/cli/tests/node_compat/test/parallel/test-zlib-zero-byte.js b/cli/tests/node_compat/test/parallel/test-zlib-zero-byte.js new file mode 100644 index 00000000000000..fb12b22803524e --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-zero-byte.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +for (const Compressor of [ zlib.Gzip, + // FIXME(bartlomieju): + // zlib.BrotliCompress +]) { + const gz = Compressor(); + const emptyBuffer = Buffer.alloc(0); + let received = 0; + gz.on('data', function(c) { + received += c.length; + }); + + gz.on('end', common.mustCall(function() { + const expected = Compressor === zlib.Gzip ? 20 : 1; + assert.strictEqual(received, expected, + `${received}, ${expected}, ${Compressor.name}`); + })); + gz.on('finish', common.mustCall()); + gz.write(emptyBuffer); + gz.end(); +} diff --git a/cli/tests/node_compat/test/parallel/test-zlib-zero-windowBits.js b/cli/tests/node_compat/test/parallel/test-zlib-zero-windowBits.js new file mode 100644 index 00000000000000..fe74fe6d88b840 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-zlib-zero-windowBits.js @@ -0,0 +1,41 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + + +// windowBits is a special case in zlib. On the compression side, 0 is invalid. +// On the decompression side, it indicates that zlib should use the value from +// the header of the compressed stream. +{ + const inflate = zlib.createInflate({ windowBits: 0 }); + assert(inflate instanceof zlib.Inflate); +} + +{ + const gunzip = zlib.createGunzip({ windowBits: 0 }); + assert(gunzip instanceof zlib.Gunzip); +} + +{ + const unzip = zlib.createUnzip({ windowBits: 0 }); + assert(unzip instanceof zlib.Unzip); +} + +// FIXME(bartlomieju): +// { +// assert.throws(() => zlib.createGzip({ windowBits: 0 }), { +// code: 'ERR_OUT_OF_RANGE', +// name: 'RangeError', +// message: 'The value of "options.windowBits" is out of range. ' + +// 'It must be >= 9 and <= 15. Received 0' +// }); +// } diff --git a/cli/tests/node_compat/test/pseudo-tty/console-dumb-tty.js b/cli/tests/node_compat/test/pseudo-tty/console-dumb-tty.js new file mode 100644 index 00000000000000..8ce0268ea0e256 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/console-dumb-tty.js @@ -0,0 +1,16 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +process.env.TERM = 'dumb'; + +console.log({ foo: 'bar' }); +console.dir({ foo: 'bar' }); +console.log('%s q', 'string'); +console.log('%o with object format param', { foo: 'bar' }); diff --git a/cli/tests/node_compat/test/pseudo-tty/console_colors.js b/cli/tests/node_compat/test/pseudo-tty/console_colors.js new file mode 100644 index 00000000000000..bfb718dbbb64b0 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/console_colors.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const vm = require('vm'); +// Make this test OS-independent by overriding stdio getColorDepth(). +process.stdout.getColorDepth = () => 8; +process.stderr.getColorDepth = () => 8; + +console.log({ foo: 'bar' }); +console.log('%s q', 'string'); +console.log('%o with object format param', { foo: 'bar' }); + +console.log( + new Error('test\n at abc (../fixtures/node_modules/bar.js:4:4)\nfoobar') +); + +try { + require('../fixtures/node_modules/node_modules/bar.js'); +} catch (err) { + console.log(err); +} + +vm.runInThisContext('console.log(new Error())'); diff --git a/cli/tests/node_compat/test/pseudo-tty/no_dropped_stdio.js b/cli/tests/node_compat/test/pseudo-tty/no_dropped_stdio.js new file mode 100644 index 00000000000000..7772d8254b0119 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/no_dropped_stdio.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// https://github.com/nodejs/node/issues/6456#issuecomment-219320599 +// https://gist.github.com/isaacs/1495b91ec66b21d30b10572d72ad2cdd +'use strict'; +const common = require('../common'); + +// 1000 bytes wrapped at 50 columns +// \n turns into a double-byte character +// (48 + {2}) * 20 = 1000 +let out = `${'o'.repeat(48)}\n`.repeat(20); +// Add the remaining 24 bytes and terminate with an 'O'. +// This results in 1025 bytes, just enough to overflow the 1kb OS X TTY buffer. +out += `${'o'.repeat(24)}O`; + +// In AIX, the child exits even before the python parent +// can setup the readloop. Provide a reasonable delay. +setTimeout(function() { + process.stdout.write(out); + process.exit(0); +}, common.isAIX ? 200 : 0); diff --git a/cli/tests/node_compat/test/pseudo-tty/no_interleaved_stdio.js b/cli/tests/node_compat/test/pseudo-tty/no_interleaved_stdio.js new file mode 100644 index 00000000000000..fe5f046b6ae5f5 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/no_interleaved_stdio.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// https://github.com/nodejs/node/issues/6456#issuecomment-219320599 +// https://gist.github.com/isaacs/1495b91ec66b21d30b10572d72ad2cdd +'use strict'; +const common = require('../common'); + +// 1000 bytes wrapped at 50 columns +// \n turns into a double-byte character +// (48 + {2}) * 20 = 1000 +let out = `${'o'.repeat(48)}\n`.repeat(20); +// Add the remaining 24 bytes and terminate with an 'O'. +// This results in 1025 bytes, just enough to overflow the 1kb OS X TTY buffer. +out += `${'o'.repeat(24)}O`; + +const err = '__This is some stderr__'; + +// In AIX, the child exits even before the python parent +// can setup the readloop. Provide a reasonable delay. +setTimeout(function() { + process.stdout.write(out); + process.stderr.write(err); +}, common.isAIX ? 200 : 0); diff --git a/cli/tests/node_compat/test/pseudo-tty/package.json b/cli/tests/node_compat/test/pseudo-tty/package.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/package.json @@ -0,0 +1 @@ +{} diff --git a/cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning-2.js b/cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning-2.js new file mode 100644 index 00000000000000..4f865e9ee4c857 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning-2.js @@ -0,0 +1,15 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +process.env.NODE_DISABLE_COLORS = '1'; +process.env.FORCE_COLOR = '3'; + +console.log(); diff --git a/cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning.js b/cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning.js new file mode 100644 index 00000000000000..effcdb2b10ab75 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/test-tty-color-support-warning.js @@ -0,0 +1,16 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); + +process.env.NO_COLOR = '1'; +process.env.NODE_DISABLE_COLORS = '1'; +process.env.FORCE_COLOR = '3'; + +console.log(); diff --git a/cli/tests/node_compat/test/pseudo-tty/test-tty-stdin-end.js b/cli/tests/node_compat/test/pseudo-tty/test-tty-stdin-end.js new file mode 100644 index 00000000000000..2acf2f223fd0ab --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/test-tty-stdin-end.js @@ -0,0 +1,14 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test ensures that Node.js doesn't crash on `process.stdin.emit("end")`. +// https://github.com/nodejs/node/issues/1068 + +process.stdin.emit('end'); diff --git a/cli/tests/node_compat/test/pseudo-tty/test-tty-stdout-end.js b/cli/tests/node_compat/test/pseudo-tty/test-tty-stdout-end.js new file mode 100644 index 00000000000000..a75fc9a4486031 --- /dev/null +++ b/cli/tests/node_compat/test/pseudo-tty/test-tty-stdout-end.js @@ -0,0 +1,11 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +process.stdout.end(); diff --git a/cli/tests/node_compat/test/pummel/package.json b/cli/tests/node_compat/test/pummel/package.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/cli/tests/node_compat/test/pummel/package.json @@ -0,0 +1 @@ +{} diff --git a/cli/tests/node_compat/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js b/cli/tests/node_compat/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js new file mode 100644 index 00000000000000..950fc74ec014d9 --- /dev/null +++ b/cli/tests/node_compat/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-gc +'use strict'; + +const common = require('../common'); + +if (process.config.variables.asan) { + common.skip('ASAN messes with memory measurements'); +} + +if (process.config.variables.arm_version === '7') { + common.skip('Too slow for armv7 bots'); +} + +const assert = require('assert'); +const net = require('net'); + +// Tests that, when receiving small chunks, we do not keep the full length +// of the original allocation for the libuv read call in memory. + +let client; +let baseRSS; +const receivedChunks = []; +const N = 250000; + +const server = net.createServer(common.mustCall((socket) => { + baseRSS = process.memoryUsage.rss(); + + socket.setNoDelay(true); + socket.on('data', (chunk) => { + receivedChunks.push(chunk); + if (receivedChunks.length < N) { + client.write('a'); + } else { + client.end(); + server.close(); + } + }); +})).listen(0, common.mustCall(() => { + client = net.connect(server.address().port); + client.setNoDelay(true); + client.write('hello!'); +})); + +process.on('exit', () => { + // TODO: support global.gc() compat + // global.gc(); + const bytesPerChunk = + (process.memoryUsage.rss() - baseRSS) / receivedChunks.length; + // We should always have less than one page (usually ~ 4 kB) per chunk. + assert(bytesPerChunk < 650, `measured ${bytesPerChunk} bytes per chunk`); +}); diff --git a/cli/tests/node_compat/test/pummel/test-net-pingpong-delay.js b/cli/tests/node_compat/test/pummel/test-net-pingpong-delay.js new file mode 100644 index 00000000000000..50f509b8e40541 --- /dev/null +++ b/cli/tests/node_compat/test/pummel/test-net-pingpong-delay.js @@ -0,0 +1,114 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +function pingPongTest(host, on_complete) { + const N = 100; + const DELAY = 1; + let count = 0; + let client_ended = false; + + const server = net.createServer({ allowHalfOpen: true }, function(socket) { + socket.setEncoding('utf8'); + + socket.on('data', function(data) { + console.log(data); + assert.strictEqual(data, 'PING'); + assert.strictEqual(socket.readyState, 'open'); + assert.strictEqual(count <= N, true); + setTimeout(function() { + assert.strictEqual(socket.readyState, 'open'); + socket.write('PONG'); + }, DELAY); + }); + + socket.on('timeout', function() { + console.error('server-side timeout!!'); + assert.strictEqual(false, true); + }); + + socket.on('end', function() { + console.log('server-side socket EOF'); + assert.strictEqual(socket.readyState, 'writeOnly'); + socket.end(); + }); + + socket.on('close', function(had_error) { + console.log('server-side socket.end'); + assert.strictEqual(had_error, false); + assert.strictEqual(socket.readyState, 'closed'); + socket.server.close(); + }); + }); + + server.listen(0, host, common.mustCall(function() { + const client = net.createConnection(server.address().port, host); + + client.setEncoding('utf8'); + + client.on('connect', function() { + assert.strictEqual(client.readyState, 'open'); + client.write('PING'); + }); + + client.on('data', function(data) { + console.log(data); + assert.strictEqual(data, 'PONG'); + assert.strictEqual(client.readyState, 'open'); + + setTimeout(function() { + assert.strictEqual(client.readyState, 'open'); + if (count++ < N) { + client.write('PING'); + } else { + console.log('closing client'); + client.end(); + client_ended = true; + } + }, DELAY); + }); + + client.on('timeout', function() { + console.error('client-side timeout!!'); + assert.strictEqual(false, true); + }); + + client.on('close', common.mustCall(function() { + console.log('client.end'); + assert.strictEqual(count, N + 1); + assert.ok(client_ended); + if (on_complete) on_complete(); + })); + })); +} + +pingPongTest(); diff --git a/cli/tests/node_compat/test/pummel/test-net-write-callbacks.js b/cli/tests/node_compat/test/pummel/test-net-write-callbacks.js new file mode 100644 index 00000000000000..7f6528107705ea --- /dev/null +++ b/cli/tests/node_compat/test/pummel/test-net-write-callbacks.js @@ -0,0 +1,80 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const net = require('net'); +const assert = require('assert'); + +let cbcount = 0; +const N = 500000; + +// TODO: support net.Server() without new + +const server = new net.Server(function(socket) { + socket.on('data', function(d) { + console.error(`got ${d.length} bytes`); + }); + + socket.on('end', function() { + console.error('end'); + socket.destroy(); + server.close(); + }); +}); + +let lastCalled = -1; +function makeCallback(c) { + let called = false; + return function() { + if (called) + throw new Error(`called callback #${c} more than once`); + called = true; + if (c < lastCalled) { + throw new Error( + `callbacks out of order. last=${lastCalled} current=${c}`); + } + lastCalled = c; + cbcount++; + }; +} + +server.listen(0, function() { + const client = net.createConnection(server.address().port); + + client.on('connect', function() { + for (let i = 0; i < N; i++) { + client.write('hello world', makeCallback(i)); + } + client.end(); + }); +}); + +process.on('exit', function() { + assert.strictEqual(cbcount, N); +}); diff --git a/cli/tests/node_compat/test/sequential/package.json b/cli/tests/node_compat/test/sequential/package.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/cli/tests/node_compat/test/sequential/package.json @@ -0,0 +1 @@ +{} diff --git a/cli/tests/node_compat/test/sequential/test-child-process-exit.js b/cli/tests/node_compat/test/sequential/test-child-process-exit.js new file mode 100644 index 00000000000000..c8930b05917e13 --- /dev/null +++ b/cli/tests/node_compat/test/sequential/test-child-process-exit.js @@ -0,0 +1,69 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// TODO(PolarETech): The process.argv[3] to be assigned to gen should be argv[2], +// and the arguments array passed to spawn() should not need to include "require.ts". + +'use strict'; +require('../common'); + +// Open a chain of five Node processes each a child of the next. The final +// process exits immediately. Each process in the chain is instructed to exit +// when its child exits. +// https://github.com/joyent/node/issues/1726 + +const assert = require('assert'); +const ch = require('child_process'); + +const gen = +(process.argv[3] || 0); +const maxGen = 5; + + +if (gen === maxGen) { + console.error('hit maxGen, exiting', maxGen); + return; +} + +const child = ch.spawn(process.execPath, ['require.ts', __filename, gen + 1], { + stdio: [ 'ignore', 'pipe', 'ignore' ] +}); +assert.ok(!child.stdin); +assert.ok(child.stdout); +assert.ok(!child.stderr); + +console.error('gen=%d, pid=%d', gen, process.pid); + +child.on('exit', function(code) { + console.error('exit %d from gen %d', code, gen + 1); +}); + +child.stdout.pipe(process.stdout); + +child.stdout.on('close', function() { + console.error('child.stdout close gen=%d', gen); +});