From 612226de8e2fe3068d981866242bacedfceb9734 Mon Sep 17 00:00:00 2001 From: Matt Mastracci Date: Mon, 22 May 2023 13:35:59 -0600 Subject: [PATCH] chore(cli): One Rust test per JS and Node unit test file (#19199) This runs our `js_unit_tests` and `node_unit_tests` in parallel, one rust test per JS unit test file. Some of our JS tests don't like running in parallel due to port requirements, so this also makes those use a specific port-per-file. This does not attempt to make the node-compat tests work. --- Cargo.lock | 1 + Cargo.toml | 1 + cli/tests/integration/js_unit_tests.rs | 177 ++++++++++++++++-- cli/tests/integration/node_unit_tests.rs | 150 ++++++++++++++- cli/tests/unit/fetch_test.ts | 26 +-- cli/tests/unit/net_test.ts | 100 +++++----- cli/tests/unit/resources_test.ts | 6 +- cli/tests/unit/serve_test.ts | 166 ++++++++-------- cli/tests/unit/streams_deprecated.ts | 16 -- .../{ => crypto}/crypto_cipher_test.ts | 6 +- .../crypto_hash_test.ts} | 2 +- .../crypto_key_test.ts} | 2 +- .../{ => crypto}/crypto_sign_test.ts | 6 +- test_util/Cargo.toml | 1 + test_util/src/factory.rs | 99 ++++++++++ test_util/src/lib.rs | 1 + 16 files changed, 564 insertions(+), 196 deletions(-) delete mode 100644 cli/tests/unit/streams_deprecated.ts rename cli/tests/unit_node/{ => crypto}/crypto_cipher_test.ts (96%) rename cli/tests/unit_node/{crypto_hash.ts => crypto/crypto_hash_test.ts} (88%) rename cli/tests/unit_node/{crypto_key.ts => crypto/crypto_key_test.ts} (98%) rename cli/tests/unit_node/{ => crypto}/crypto_sign_test.ts (95%) create mode 100644 test_util/src/factory.rs diff --git a/Cargo.lock b/Cargo.lock index 483ed55fb4..57e089f9b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5087,6 +5087,7 @@ dependencies = [ "fastwebsockets", "flate2", "futures", + "glob", "hyper 0.14.26", "lazy-regex", "lsp-types", diff --git a/Cargo.toml b/Cargo.toml index 916248d9af..dbc17d17f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ filetime = "0.2.16" flate2 = "=1.0.24" fs3 = "0.5.0" futures = "0.3.21" +glob = "0.3.1" hex = "0.4" http = "0.2.9" httparse = "1.8.0" diff --git a/cli/tests/integration/js_unit_tests.rs b/cli/tests/integration/js_unit_tests.rs index 793f66b1e1..4d6ef3675d 100644 --- a/cli/tests/integration/js_unit_tests.rs +++ b/cli/tests/integration/js_unit_tests.rs @@ -1,27 +1,113 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - +use std::io::BufRead; +use std::io::BufReader; +use std::process::Stdio; +use std::time::Duration; +use std::time::Instant; use test_util as util; -#[test] -fn js_unit_tests_lint() { - let status = util::deno_cmd() - .arg("lint") - .arg("--unstable") - .arg(util::tests_path().join("unit")) - .spawn() - .unwrap() - .wait() - .unwrap(); - assert!(status.success()); -} +util::unit_test_factory!( + js_unit_test, + "tests/unit", + "*.ts", + [ + abort_controller_test, + blob_test, + body_test, + broadcast_channel_test, + buffer_test, + build_test, + cache_api_test, + chmod_test, + chown_test, + command_test, + console_test, + copy_file_test, + custom_event_test, + dir_test, + dom_exception_test, + error_stack_test, + error_test, + esnext_test, + event_target_test, + event_test, + fetch_test, + ffi_test, + file_test, + filereader_test, + files_test, + flock_test, + fs_events_test, + get_random_values_test, + globals_test, + headers_test, + http_test, + internals_test, + intl_test, + io_test, + kv_test, + link_test, + make_temp_test, + message_channel_test, + metrics_test, + mkdir_test, + navigator_test, + net_test, + network_interfaces_test, + opcall_test, + os_test, + path_from_url_test, + performance_test, + permissions_test, + process_test, + progressevent_test, + promise_hooks_test, + read_dir_test, + read_file_test, + read_link_test, + read_text_file_test, + real_path_test, + ref_unref_test, + remove_test, + rename_test, + request_test, + resources_test, + response_test, + serve_test, + signal_test, + stat_test, + stdio_test, + structured_clone_test, + symlink_test, + sync_test, + test_util, + testing_test, + text_encoding_test, + timers_test, + tls_test, + truncate_test, + tty_color_test, + tty_test, + umask_test, + url_search_params_test, + url_test, + urlpattern_test, + utime_test, + version_test, + wasm_test, + webcrypto_test, + websocket_test, + webstorage_test, + worker_permissions_test, + worker_types, + write_file_test, + write_text_file_test, + ] +); -#[test] -fn js_unit_tests() { +fn js_unit_test(test: String) { let _g = util::http_server(); - // Note that the unit tests are not safe for concurrency and must be run with a concurrency limit - // of one because there are some chdir tests in there. - // TODO(caspervonb) split these tests into two groups: parallel and serial. let mut deno = util::deno_cmd() .current_dir(util::root_path()) .arg("test") @@ -29,11 +115,58 @@ fn js_unit_tests() { .arg("--location=http://js-unit-tests/foo/bar") .arg("--no-prompt") .arg("-A") - .arg(util::tests_path().join("unit")) + .arg(util::tests_path().join("unit").join(format!("{test}.ts"))) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) .spawn() .expect("failed to spawn script"); - let status = deno.wait().expect("failed to wait for the child process"); + let now = Instant::now(); + let stdout = deno.stdout.take().unwrap(); + let test_name = test.clone(); + let stdout = std::thread::spawn(move || { + let reader = BufReader::new(stdout); + for line in reader.lines() { + if let Ok(line) = line { + println!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); + } else { + break; + } + } + }); + + let now = Instant::now(); + let stderr = deno.stderr.take().unwrap(); + let test_name = test.clone(); + let stderr = std::thread::spawn(move || { + let reader = BufReader::new(stderr); + for line in reader.lines() { + if let Ok(line) = line { + eprintln!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); + } else { + break; + } + } + }); + + const PER_TEST_TIMEOUT: Duration = Duration::from_secs(2 * 60); + + let now = Instant::now(); + let status = loop { + if now.elapsed() > PER_TEST_TIMEOUT { + // Last-ditch kill + _ = deno.kill(); + panic!("Test {test} failed to complete in time"); + } + if let Some(status) = deno + .try_wait() + .expect("failed to wait for the child process") + { + break status; + } + std::thread::sleep(Duration::from_millis(100)); + }; + #[cfg(unix)] assert_eq!( std::os::unix::process::ExitStatusExt::signal(&status), @@ -41,5 +174,9 @@ fn js_unit_tests() { "Deno should not have died with a signal" ); assert_eq!(Some(0), status.code(), "Deno should have exited cleanly"); + + stdout.join().unwrap(); + stderr.join().unwrap(); + assert!(status.success()); } diff --git a/cli/tests/integration/node_unit_tests.rs b/cli/tests/integration/node_unit_tests.rs index d2a6f6ec8e..2cde515528 100644 --- a/cli/tests/integration/node_unit_tests.rs +++ b/cli/tests/integration/node_unit_tests.rs @@ -1,12 +1,80 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - +use std::io::BufRead; +use std::io::BufReader; +use std::process::Stdio; +use std::time::Duration; +use std::time::Instant; use test_util as util; -#[test] -fn node_unit_tests() { +util::unit_test_factory!( + node_unit_test, + "tests/unit_node", + "**/*_test.ts", + [ + _fs_access_test = _fs / _fs_access_test, + _fs_appendFile_test = _fs / _fs_appendFile_test, + _fs_chmod_test = _fs / _fs_chmod_test, + _fs_chown_test = _fs / _fs_chown_test, + _fs_close_test = _fs / _fs_close_test, + _fs_copy_test = _fs / _fs_copy_test, + _fs_dir_test = _fs / _fs_dir_test, + _fs_exists_test = _fs / _fs_exists_test, + _fs_fdatasync_test = _fs / _fs_fdatasync_test, + _fs_fstat_test = _fs / _fs_fstat_test, + _fs_fsync_test = _fs / _fs_fsync_test, + _fs_ftruncate_test = _fs / _fs_ftruncate_test, + _fs_futimes_test = _fs / _fs_futimes_test, + _fs_link_test = _fs / _fs_link_test, + _fs_lstat_test = _fs / _fs_lstat_test, + _fs_mkdir_test = _fs / _fs_mkdir_test, + _fs_mkdtemp_test = _fs / _fs_mkdtemp_test, + _fs_opendir_test = _fs / _fs_opendir_test, + _fs_readFile_test = _fs / _fs_readFile_test, + _fs_readdir_test = _fs / _fs_readdir_test, + _fs_readlink_test = _fs / _fs_readlink_test, + _fs_realpath_test = _fs / _fs_realpath_test, + _fs_rename_test = _fs / _fs_rename_test, + _fs_rm_test = _fs / _fs_rm_test, + _fs_rmdir_test = _fs / _fs_rmdir_test, + _fs_stat_test = _fs / _fs_stat_test, + _fs_symlink_test = _fs / _fs_symlink_test, + _fs_truncate_test = _fs / _fs_truncate_test, + _fs_unlink_test = _fs / _fs_unlink_test, + _fs_utimes_test = _fs / _fs_utimes_test, + _fs_watch_test = _fs / _fs_watch_test, + _fs_write_test = _fs / _fs_write_test, + async_hooks_test, + child_process_test, + crypto_cipher_test = crypto / crypto_cipher_test, + crypto_hash_test = crypto / crypto_hash_test, + crypto_key_test = crypto / crypto_key_test, + crypto_sign_test = crypto / crypto_sign_test, + fs_test, + http_test, + _randomBytes_test = internal / _randomBytes_test, + _randomFill_test = internal / _randomFill_test, + _randomInt_test = internal / _randomInt_test, + pbkdf2_test = internal / pbkdf2_test, + scrypt_test = internal / scrypt_test, + module_test, + process_test, + querystring_test, + readline_test, + string_decoder_test, + timers_test, + tls_test, + tty_test, + util_test, + v8_test, + worker_threads_test + ] +); + +fn node_unit_test(test: String) { let _g = util::http_server(); - let mut deno = util::deno_cmd() + let mut deno = util::deno_cmd(); + let mut deno = deno .current_dir(util::root_path()) .arg("test") .arg("--unstable") @@ -14,12 +82,78 @@ fn node_unit_tests() { // but this shouldn't be necessary. tls.connect currently doesn't // pass hostname option correctly and it causes cert errors. .arg("--unsafely-ignore-certificate-errors") - .arg("-A") - .arg(util::tests_path().join("unit_node")) + .arg("-A"); + // Parallel tests for crypto + if test.starts_with("crypto/") { + deno = deno.arg("--parallel"); + } + let mut deno = deno + .arg( + util::tests_path() + .join("unit_node") + .join(format!("{test}.ts")), + ) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) .spawn() .expect("failed to spawn script"); - let status = deno.wait().expect("failed to wait for the child process"); - assert_eq!(Some(0), status.code()); + let now = Instant::now(); + let stdout = deno.stdout.take().unwrap(); + let test_name = test.clone(); + let stdout = std::thread::spawn(move || { + let reader = BufReader::new(stdout); + for line in reader.lines() { + if let Ok(line) = line { + println!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); + } else { + break; + } + } + }); + + let now = Instant::now(); + let stderr = deno.stderr.take().unwrap(); + let test_name = test.clone(); + let stderr = std::thread::spawn(move || { + let reader = BufReader::new(stderr); + for line in reader.lines() { + if let Ok(line) = line { + eprintln!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); + } else { + break; + } + } + }); + + const PER_TEST_TIMEOUT: Duration = Duration::from_secs(5 * 60); + + let now = Instant::now(); + let status = loop { + if now.elapsed() > PER_TEST_TIMEOUT { + // Last-ditch kill + _ = deno.kill(); + panic!("Test {test} failed to complete in time"); + } + if let Some(status) = deno + .try_wait() + .expect("failed to wait for the child process") + { + break status; + } + std::thread::sleep(Duration::from_millis(100)); + }; + + #[cfg(unix)] + assert_eq!( + std::os::unix::process::ExitStatusExt::signal(&status), + None, + "Deno should not have died with a signal" + ); + assert_eq!(Some(0), status.code(), "Deno should have exited cleanly"); + + stdout.join().unwrap(); + stderr.join().unwrap(); + assert!(status.success()); } diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index 08689a2a0a..db553f14d4 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -10,6 +10,8 @@ import { } from "./test_util.ts"; import { Buffer } from "../../../test_util/std/io/buffer.ts"; +const listenPort = 4504; + Deno.test( { permissions: { net: true } }, async function fetchRequiresOneArgument() { @@ -639,7 +641,7 @@ Deno.test( permissions: { net: true }, }, async function fetchRequest() { - const addr = "127.0.0.1:4501"; + const addr = `127.0.0.1:${listenPort}`; const bufPromise = bufferServer(addr); const response = await fetch(`http://${addr}/blah`, { method: "POST", @@ -673,7 +675,7 @@ Deno.test( permissions: { net: true }, }, async function fetchRequestAcceptHeaders() { - const addr = "127.0.0.1:4501"; + const addr = `127.0.0.1:${listenPort}`; const bufPromise = bufferServer(addr); const response = await fetch(`http://${addr}/blah`, { method: "POST", @@ -705,7 +707,7 @@ Deno.test( permissions: { net: true }, }, async function fetchPostBodyString() { - const addr = "127.0.0.1:4511"; + const addr = `127.0.0.1:${listenPort}`; const bufPromise = bufferServer(addr); const body = "hello world"; const response = await fetch(`http://${addr}/blah`, { @@ -743,7 +745,7 @@ Deno.test( permissions: { net: true }, }, async function fetchPostBodyTypedArray() { - const addr = "127.0.0.1:4503"; + const addr = `127.0.0.1:${listenPort}`; const bufPromise = bufferServer(addr); const bodyStr = "hello world"; const body = new TextEncoder().encode(bodyStr); @@ -781,7 +783,7 @@ Deno.test( permissions: { net: true }, }, async function fetchUserSetContentLength() { - const addr = "127.0.0.1:4501"; + const addr = `127.0.0.1:${listenPort}`; const bufPromise = bufferServer(addr); const response = await fetch(`http://${addr}/blah`, { method: "POST", @@ -812,7 +814,7 @@ Deno.test( permissions: { net: true }, }, async function fetchUserSetTransferEncoding() { - const addr = "127.0.0.1:4501"; + const addr = `127.0.0.1:${listenPort}`; const bufPromise = bufferServer(addr); const response = await fetch(`http://${addr}/blah`, { method: "POST", @@ -1158,7 +1160,7 @@ Deno.test( permissions: { net: true }, }, async function fetchPostBodyReadableStream() { - const addr = "127.0.0.1:4511"; + const addr = `127.0.0.1:${listenPort}`; const bufPromise = bufferServer(addr); const stream = new TransformStream(); const writer = stream.writable.getWriter(); @@ -1217,7 +1219,7 @@ Deno.test( async function fetchFilterOutCustomHostHeader(): Promise< void > { - const addr = "127.0.0.1:4511"; + const addr = `127.0.0.1:${listenPort}`; const [hostname, port] = addr.split(":"); const listener = Deno.listen({ hostname, @@ -1717,7 +1719,7 @@ Deno.test( async function fetchWithInvalidContentLengthAndTransferEncoding(): Promise< void > { - const addr = "127.0.0.1:4516"; + const addr = `127.0.0.1:${listenPort}`; const data = "a".repeat(10 << 10); const body = new TextEncoder().encode( @@ -1749,7 +1751,7 @@ Deno.test( async function fetchWithInvalidContentLength(): Promise< void > { - const addr = "127.0.0.1:4517"; + const addr = `127.0.0.1:${listenPort}`; const data = "a".repeat(10 << 10); const body = new TextEncoder().encode( @@ -1777,7 +1779,7 @@ Deno.test( async function fetchWithInvalidContentLength(): Promise< void > { - const addr = "127.0.0.1:4518"; + const addr = `127.0.0.1:${listenPort}`; const data = "a".repeat(10 << 10); const contentLength = data.length / 2; @@ -1804,7 +1806,7 @@ Deno.test( async function fetchWithInvalidContentLength(): Promise< void > { - const addr = "127.0.0.1:4519"; + const addr = `127.0.0.1:${listenPort}`; const data = "a".repeat(10 << 10); const contentLength = data.length * 2; diff --git a/cli/tests/unit/net_test.ts b/cli/tests/unit/net_test.ts index 935a6f846b..32250bbd07 100644 --- a/cli/tests/unit/net_test.ts +++ b/cli/tests/unit/net_test.ts @@ -12,6 +12,10 @@ import { } from "./test_util.ts"; import { join } from "../../../test_util/std/path/mod.ts"; +// Since these tests may run in parallel, ensure this port is unique to this file +const listenPort = 4503; +const listenPort2 = 4504; + let isCI: boolean; try { isCI = Deno.env.get("CI") !== undefined; @@ -20,10 +24,10 @@ try { } Deno.test({ permissions: { net: true } }, function netTcpListenClose() { - const listener = Deno.listen({ hostname: "127.0.0.1", port: 3500 }); + const listener = Deno.listen({ hostname: "127.0.0.1", port: listenPort }); assert(listener.addr.transport === "tcp"); assertEquals(listener.addr.hostname, "127.0.0.1"); - assertEquals(listener.addr.port, 3500); + assertEquals(listener.addr.port, listenPort); assertNotEquals(listener.rid, 0); listener.close(); }); @@ -35,12 +39,12 @@ Deno.test( function netUdpListenClose() { const socket = Deno.listenDatagram({ hostname: "127.0.0.1", - port: 3500, + port: listenPort, transport: "udp", }); assert(socket.addr.transport === "udp"); assertEquals(socket.addr.hostname, "127.0.0.1"); - assertEquals(socket.addr.port, 3500); + assertEquals(socket.addr.port, listenPort); socket.close(); }, ); @@ -127,7 +131,7 @@ Deno.test( permissions: { net: true }, }, async function netTcpCloseWhileAccept() { - const listener = Deno.listen({ port: 4501 }); + const listener = Deno.listen({ port: listenPort }); const p = listener.accept(); listener.close(); // TODO(piscisaureus): the error type should be `Interrupted` here, which @@ -212,22 +216,22 @@ Deno.test( ); Deno.test({ permissions: { net: true } }, async function netTcpDialListen() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: listenPort }); listener.accept().then( async (conn) => { assert(conn.remoteAddr != null); assert(conn.localAddr.transport === "tcp"); assertEquals(conn.localAddr.hostname, "127.0.0.1"); - assertEquals(conn.localAddr.port, 3500); + assertEquals(conn.localAddr.port, listenPort); await conn.write(new Uint8Array([1, 2, 3])); conn.close(); }, ); - const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); + const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort }); assert(conn.remoteAddr.transport === "tcp"); assertEquals(conn.remoteAddr.hostname, "127.0.0.1"); - assertEquals(conn.remoteAddr.port, 3500); + assertEquals(conn.remoteAddr.port, listenPort); assert(conn.localAddr != null); const buf = new Uint8Array(1024); const readResult = await conn.read(buf); @@ -247,23 +251,23 @@ Deno.test({ permissions: { net: true } }, async function netTcpDialListen() { }); Deno.test({ permissions: { net: true } }, async function netTcpSetNoDelay() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: listenPort }); listener.accept().then( async (conn) => { assert(conn.remoteAddr != null); assert(conn.localAddr.transport === "tcp"); assertEquals(conn.localAddr.hostname, "127.0.0.1"); - assertEquals(conn.localAddr.port, 3500); + assertEquals(conn.localAddr.port, listenPort); await conn.write(new Uint8Array([1, 2, 3])); conn.close(); }, ); - const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); + const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort }); conn.setNoDelay(true); assert(conn.remoteAddr.transport === "tcp"); assertEquals(conn.remoteAddr.hostname, "127.0.0.1"); - assertEquals(conn.remoteAddr.port, 3500); + assertEquals(conn.remoteAddr.port, listenPort); assert(conn.localAddr != null); const buf = new Uint8Array(1024); const readResult = await conn.read(buf); @@ -283,23 +287,23 @@ Deno.test({ permissions: { net: true } }, async function netTcpSetNoDelay() { }); Deno.test({ permissions: { net: true } }, async function netTcpSetKeepAlive() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: listenPort }); listener.accept().then( async (conn) => { assert(conn.remoteAddr != null); assert(conn.localAddr.transport === "tcp"); assertEquals(conn.localAddr.hostname, "127.0.0.1"); - assertEquals(conn.localAddr.port, 3500); + assertEquals(conn.localAddr.port, listenPort); await conn.write(new Uint8Array([1, 2, 3])); conn.close(); }, ); - const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); + const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort }); conn.setKeepAlive(true); assert(conn.remoteAddr.transport === "tcp"); assertEquals(conn.remoteAddr.hostname, "127.0.0.1"); - assertEquals(conn.remoteAddr.port, 3500); + assertEquals(conn.remoteAddr.port, listenPort); assert(conn.localAddr != null); const buf = new Uint8Array(1024); const readResult = await conn.read(buf); @@ -360,14 +364,14 @@ Deno.test( Deno.test( { permissions: { net: true } }, async function netUdpSendReceive() { - const alice = Deno.listenDatagram({ port: 3500, transport: "udp" }); + const alice = Deno.listenDatagram({ port: listenPort, transport: "udp" }); assert(alice.addr.transport === "udp"); - assertEquals(alice.addr.port, 3500); + assertEquals(alice.addr.port, listenPort); assertEquals(alice.addr.hostname, "127.0.0.1"); - const bob = Deno.listenDatagram({ port: 4501, transport: "udp" }); + const bob = Deno.listenDatagram({ port: listenPort2, transport: "udp" }); assert(bob.addr.transport === "udp"); - assertEquals(bob.addr.port, 4501); + assertEquals(bob.addr.port, listenPort2); assertEquals(bob.addr.hostname, "127.0.0.1"); const sent = new Uint8Array([1, 2, 3]); @@ -377,7 +381,7 @@ Deno.test( const [recvd, remote] = await bob.receive(); assert(remote.transport === "udp"); - assertEquals(remote.port, 3500); + assertEquals(remote.port, listenPort); assertEquals(recvd.length, 3); assertEquals(1, recvd[0]); assertEquals(2, recvd[1]); @@ -393,18 +397,18 @@ Deno.test( // Must bind sender to an address that can send to the broadcast address on MacOS. // Macos will give us error 49 when sending the broadcast packet if we omit hostname here. const alice = Deno.listenDatagram({ - port: 3500, + port: listenPort, transport: "udp", hostname: "0.0.0.0", }); const bob = Deno.listenDatagram({ - port: 4501, + port: listenPort, transport: "udp", hostname: "0.0.0.0", }); assert(bob.addr.transport === "udp"); - assertEquals(bob.addr.port, 4501); + assertEquals(bob.addr.port, listenPort); assertEquals(bob.addr.hostname, "0.0.0.0"); const broadcastAddr = { ...bob.addr, hostname: "255.255.255.255" }; @@ -415,7 +419,7 @@ Deno.test( assertEquals(byteLength, 3); const [recvd, remote] = await bob.receive(); assert(remote.transport === "udp"); - assertEquals(remote.port, 3500); + assertEquals(remote.port, listenPort); assertEquals(recvd.length, 3); assertEquals(1, recvd[0]); assertEquals(2, recvd[1]); @@ -563,9 +567,9 @@ Deno.test( Deno.test( { permissions: { net: true } }, async function netUdpConcurrentSendReceive() { - const socket = Deno.listenDatagram({ port: 3500, transport: "udp" }); + const socket = Deno.listenDatagram({ port: listenPort, transport: "udp" }); assert(socket.addr.transport === "udp"); - assertEquals(socket.addr.port, 3500); + assertEquals(socket.addr.port, listenPort); assertEquals(socket.addr.hostname, "127.0.0.1"); const recvPromise = socket.receive(); @@ -588,7 +592,7 @@ Deno.test( { permissions: { net: true } }, async function netUdpBorrowMutError() { const socket = Deno.listenDatagram({ - port: 4501, + port: listenPort, transport: "udp", }); // Panic happened on second send: BorrowMutError @@ -761,7 +765,7 @@ Deno.test( Deno.test( { permissions: { net: true } }, async function netListenAsyncIterator() { - const addr = { hostname: "127.0.0.1", port: 3500 }; + const addr = { hostname: "127.0.0.1", port: listenPort }; const listener = Deno.listen(addr); const runAsyncIterator = async () => { for await (const conn of listener) { @@ -794,7 +798,7 @@ Deno.test( permissions: { net: true }, }, async function netCloseWriteSuccess() { - const addr = { hostname: "127.0.0.1", port: 3500 }; + const addr = { hostname: "127.0.0.1", port: listenPort }; const listener = Deno.listen(addr); const closeDeferred = deferred(); listener.accept().then(async (conn) => { @@ -850,7 +854,7 @@ Deno.test( } } - const addr = { hostname: "127.0.0.1", port: 3500 }; + const addr = { hostname: "127.0.0.1", port: listenPort }; const listener = Deno.listen(addr); const listenerPromise = iteratorReq(listener); const connectionPromise = (async () => { @@ -898,13 +902,13 @@ Deno.test( Deno.test({ permissions: { net: true } }, async function whatwgStreams() { (async () => { - const listener = Deno.listen({ hostname: "127.0.0.1", port: 3500 }); + const listener = Deno.listen({ hostname: "127.0.0.1", port: listenPort }); const conn = await listener.accept(); await conn.readable.pipeTo(conn.writable); listener.close(); })(); - const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); + const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort }); const reader = conn.readable.getReader(); const writer = conn.writable.getWriter(); const encoder = new TextEncoder(); @@ -957,7 +961,7 @@ Deno.test( async function netListenUnref() { const [statusCode, _output] = await execCode(` async function main() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: ${listenPort} }); listener.unref(); await listener.accept(); // This doesn't block the program from exiting } @@ -972,14 +976,14 @@ Deno.test( async function netListenUnref() { const [statusCode, _output] = await execCode(` async function main() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: ${listenPort} }); await listener.accept(); listener.unref(); await listener.accept(); // The program exits here throw new Error(); // The program doesn't reach here } main(); - const conn = await Deno.connect({ port: 3500 }); + const conn = await Deno.connect({ port: ${listenPort} }); conn.close(); `); assertEquals(statusCode, 0); @@ -991,7 +995,7 @@ Deno.test( async function netListenUnrefAndRef() { const p = execCode2(` async function main() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: ${listenPort} }); listener.unref(); listener.ref(); // This restores 'ref' state of listener console.log("started"); @@ -1001,7 +1005,7 @@ Deno.test( main(); `); await p.waitStdoutText("started"); - const conn = await Deno.connect({ port: 3500 }); + const conn = await Deno.connect({ port: listenPort }); conn.close(); const [statusCode, output] = await p.finished(); assertEquals(statusCode, 0); @@ -1013,7 +1017,7 @@ Deno.test( { permissions: { net: true } }, async function netListenUnrefConcurrentAccept() { const timer = setTimeout(() => {}, 1000); - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: listenPort }); listener.accept().catch(() => {}); listener.unref(); // Unref'd listener still causes Busy error @@ -1044,12 +1048,12 @@ Deno.test({ Deno.test( { permissions: { net: true, read: true, run: true } }, async function netConnUnref() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: listenPort }); const intervalId = setInterval(() => {}); // This keeps event loop alive. const program = execCode(` async function main() { - const conn = await Deno.connect({ port: 3500 }); + const conn = await Deno.connect({ port: ${listenPort} }); conn.unref(); await conn.read(new Uint8Array(10)); // The program exits here throw new Error(); // The program doesn't reach here @@ -1068,12 +1072,12 @@ Deno.test( Deno.test( { permissions: { net: true, read: true, run: true } }, async function netConnUnrefReadable() { - const listener = Deno.listen({ port: 3500 }); + const listener = Deno.listen({ port: listenPort }); const intervalId = setInterval(() => {}); // This keeps event loop alive. const program = execCode(` async function main() { - const conn = await Deno.connect({ port: 3500 }); + const conn = await Deno.connect({ port: ${listenPort} }); conn.unref(); const reader = conn.readable.getReader(); await reader.read(); // The program exits here @@ -1093,7 +1097,7 @@ Deno.test( Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() { const listener1 = Deno.listen({ hostname: "127.0.0.1", - port: 3500, + port: listenPort, }); listener1.accept().then( (conn) => { @@ -1101,7 +1105,7 @@ Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() { }, ); - const conn1 = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); + const conn1 = await Deno.connect({ hostname: "127.0.0.1", port: listenPort }); const buf1 = new Uint8Array(1024); await conn1.read(buf1); listener1.close(); @@ -1109,7 +1113,7 @@ Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() { const listener2 = Deno.listen({ hostname: "127.0.0.1", - port: 3500, + port: listenPort, }); listener2.accept().then( @@ -1118,7 +1122,7 @@ Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() { }, ); - const conn2 = await Deno.connect({ hostname: "127.0.0.1", port: 3500 }); + const conn2 = await Deno.connect({ hostname: "127.0.0.1", port: listenPort }); const buf2 = new Uint8Array(1024); await conn2.read(buf2); diff --git a/cli/tests/unit/resources_test.ts b/cli/tests/unit/resources_test.ts index 2d1f2fd75b..4a55f05a70 100644 --- a/cli/tests/unit/resources_test.ts +++ b/cli/tests/unit/resources_test.ts @@ -1,6 +1,8 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; +const listenPort = 4505; + Deno.test(function resourcesCloseBadArgs() { assertThrows(() => { Deno.close((null as unknown) as number); @@ -16,8 +18,8 @@ Deno.test(function resourcesStdio() { }); Deno.test({ permissions: { net: true } }, async function resourcesNet() { - const listener = Deno.listen({ port: 4501 }); - const dialerConn = await Deno.connect({ port: 4501 }); + const listener = Deno.listen({ port: listenPort }); + const dialerConn = await Deno.connect({ port: listenPort }); const listenerConn = await listener.accept(); const res = Deno.resources(); diff --git a/cli/tests/unit/serve_test.ts b/cli/tests/unit/serve_test.ts index c26d3f7516..d3faac78da 100644 --- a/cli/tests/unit/serve_test.ts +++ b/cli/tests/unit/serve_test.ts @@ -14,6 +14,9 @@ import { fail, } from "./test_util.ts"; +// Since these tests may run in parallel, ensure this port is unique to this file +const servePort = 4502; + const { upgradeHttpRaw, addTrailers, @@ -42,18 +45,18 @@ Deno.test(async function httpServerShutsDownPortBeforeResolving() { const server = Deno.serve({ handler: (_req) => new Response("ok"), - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), }); await listeningPromise; - assertThrows(() => Deno.listen({ port: 4501 })); + assertThrows(() => Deno.listen({ port: servePort })); ac.abort(); await server.finished; - const listener = Deno.listen({ port: 4501 }); + const listener = Deno.listen({ port: servePort }); listener!.close(); }); @@ -79,14 +82,14 @@ Deno.test(async function httpServerCanResolveHostnames() { const server = Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const resp = await fetch("http://localhost:4501/", { + const resp = await fetch(`http://localhost:${servePort}/`, { headers: { "connection": "close" }, }); const text = await resp.text(); @@ -102,7 +105,7 @@ Deno.test(async function httpServerRejectsOnAddrInUse() { const server = Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -114,7 +117,7 @@ Deno.test(async function httpServerRejectsOnAddrInUse() { Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -135,20 +138,20 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() { // FIXME(bartlomieju): // make sure that request can be inspected console.log(request); - assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); + assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`); assertEquals(await request.text(), ""); assertEquals(remoteAddr.hostname, "127.0.0.1"); promise.resolve(); return new Response("Hello World", { headers: { "foo": "bar" } }); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { headers: { "connection": "close" }, }); await promise; @@ -173,7 +176,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOnError() { await new Promise((r) => setTimeout(r, 100)); throw "fail"; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: () => { @@ -182,14 +185,14 @@ Deno.test({ permissions: { net: true } }, async function httpServerOnError() { }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { headers: { "connection": "close" }, }); const text = await resp.text(); ac.abort(); await server; - assertEquals(text, "failed: http://127.0.0.1:4501/"); + assertEquals(text, `failed: http://127.0.0.1:${servePort}/`); }); Deno.test( @@ -208,7 +211,7 @@ Deno.test( await new Promise((r) => setTimeout(r, 100)); throw "fail"; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: () => { @@ -217,7 +220,7 @@ Deno.test( }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { headers: { "connection": "close" }, }); const text = await resp.text(); @@ -234,7 +237,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload1() { const listeningPromise = deferred(); const server = Deno.serve({ - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -242,14 +245,14 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload1() { // FIXME(bartlomieju): // make sure that request can be inspected console.log(request); - assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); + assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`); assertEquals(await request.text(), ""); promise.resolve(); return new Response("Hello World", { headers: { "foo": "bar" } }); }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { headers: { "connection": "close" }, }); await promise; @@ -269,7 +272,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() { const listeningPromise = deferred(); const server = Deno.serve({ - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -277,14 +280,14 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() { // FIXME(bartlomieju): // make sure that request can be inspected console.log(request); - assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); + assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`); assertEquals(await request.text(), ""); promise.resolve(); return new Response("Hello World", { headers: { "foo": "bar" } }); }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { headers: { "connection": "close" }, }); await promise; @@ -387,14 +390,14 @@ Deno.test( promise.resolve(); return new Response(""); }, - port: 2333, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const conn = await Deno.connect({ port: 2333 }); + const conn = await Deno.connect({ port: servePort }); // Send GET request with a body + content-length. const encoder = new TextEncoder(); const body = @@ -525,18 +528,18 @@ Deno.test( promise.resolve(); return new Response("", { headers: {} }); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const conn = await Deno.connect({ port: 4501 }); + const conn = await Deno.connect({ port: servePort }); // Send GET request with a body + content-length. const encoder = new TextEncoder(); const body = - `GET / HTTP/1.1\r\nHost: 127.0.0.1:4501\r\nContent-Length: 5\r\n\r\n12345`; + `GET / HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\nContent-Length: 5\r\n\r\n12345`; const writeResult = await conn.write(encoder.encode(body)); assertEquals(body.length, writeResult); @@ -590,14 +593,14 @@ function createStreamTest(count: number, delay: number, action: string) { handler: (_request) => { return new Response(makeStream(count, delay)); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/"); + const resp = await fetch(`http://127.0.0.1:${servePort}/`); const text = await resp.text(); ac.abort(); @@ -643,14 +646,14 @@ Deno.test( assertEquals("hello world", reqBody); return new Response("yo"); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { body: stream.readable, method: "POST", headers: { "connection": "close" }, @@ -667,13 +670,13 @@ Deno.test({ permissions: { net: true } }, async function httpServerClose() { const listeningPromise = deferred(); const server = Deno.serve({ handler: () => new Response("ok"), - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const client = await Deno.connect({ port: 4501 }); + const client = await Deno.connect({ port: servePort }); client.close(); ac.abort(); await server; @@ -692,13 +695,13 @@ Deno.test({ permissions: { net: true } }, async function httpServerCloseGet() { responsePromise.resolve(); return new Response("ok"); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const conn = await Deno.connect({ port: 4501 }); + const conn = await Deno.connect({ port: servePort }); const encoder = new TextEncoder(); const body = `GET / HTTP/1.1\r\nHost: example.domain\r\nConnection: close\r\n\r\n`; @@ -719,14 +722,14 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ handler: () => new Response(new Blob([])), - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/"); + const resp = await fetch(`http://127.0.0.1:${servePort}/`); const respBody = await resp.text(); assertEquals("", respBody); @@ -754,7 +757,7 @@ Deno.test( }); return new Response(body); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: (err) => { @@ -768,7 +771,7 @@ Deno.test( }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/"); + const resp = await fetch(`http://127.0.0.1:${servePort}/`); // Incorrectly implemented reader ReadableStream should reject. assertStringIncludes(await resp.text(), "Failed to execute 'enqueue'"); await errorPromise; @@ -833,7 +836,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() { }; return response; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -841,7 +844,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() { await listeningPromise; const def = deferred(); - const ws = new WebSocket("ws://localhost:4501"); + const ws = new WebSocket(`ws://localhost:${servePort}`); ws.onmessage = (m) => assertEquals(m.data, "foo"); ws.onerror = (e) => { console.error(e); @@ -887,7 +890,7 @@ Deno.test( conn.close(); return response; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -895,7 +898,7 @@ Deno.test( await listeningPromise; - const conn = await Deno.connect({ port: 4501 }); + const conn = await Deno.connect({ port: servePort }); await conn.write( new TextEncoder().encode( "GET / HTTP/1.1\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\nUpgrade data", @@ -962,7 +965,7 @@ Deno.test( }; return response; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -970,7 +973,7 @@ Deno.test( await listeningPromise; const def = deferred(); - const ws = new WebSocket("ws://localhost:4501"); + const ws = new WebSocket(`ws://localhost:${servePort}`); ws.onmessage = (m) => assertEquals(m.data, "foo"); ws.onerror = (e) => { console.error(e); @@ -999,7 +1002,7 @@ Deno.test( socket.onopen = () => socket.close(); return response; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -1007,7 +1010,7 @@ Deno.test( await listeningPromise; const def = deferred(); - const ws = new WebSocket("ws://localhost:4501"); + const ws = new WebSocket(`ws://localhost:${servePort}`); ws.onerror = (e) => { console.error(e); fail(); @@ -1041,7 +1044,7 @@ Deno.test( }; return response; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -1049,8 +1052,9 @@ Deno.test( await listeningPromise; const def = deferred(); - const ws = new WebSocket("ws://localhost:4501"); - ws.onmessage = (m) => assertEquals(m.data, "http://localhost:4501/"); + const ws = new WebSocket(`ws://localhost:${servePort}`); + ws.onmessage = (m) => + assertEquals(m.data, `http://localhost:${servePort}/`); ws.onerror = (e) => { console.error(e); fail(); @@ -1078,14 +1082,14 @@ Deno.test( promise.resolve(); return new Response(""); }, - port: 2333, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const conn = await Deno.connect({ port: 2333 }); + const conn = await Deno.connect({ port: servePort }); // Send GET request with a body + content-length. const encoder = new TextEncoder(); const smthElse = "x".repeat(16 * 1024 + 256); @@ -1118,14 +1122,14 @@ Deno.test( promise.resolve(); return new Response(""); }, - port: 2333, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const conn = await Deno.connect({ port: 2333 }); + const conn = await Deno.connect({ port: servePort }); // Send GET request with a body + content-length. const encoder = new TextEncoder(); const smthElse = "x".repeat(16 * 1024 + 256); @@ -1161,14 +1165,14 @@ Deno.test( promise.resolve(); return new Response(""); }, - port: 2333, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const conn = await Deno.connect({ port: 2333 }); + const conn = await Deno.connect({ port: servePort }); // Send GET request with a body + connection: close. const encoder = new TextEncoder(); const body = @@ -1191,7 +1195,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve( - { port: 2333, signal: ac.signal }, + { port: servePort, signal: ac.signal }, (request) => { assert(request.body); @@ -1203,7 +1207,7 @@ Deno.test( const ts = new TransformStream(); const writable = ts.writable.getWriter(); - const resp = await fetch("http://127.0.0.1:2333/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { method: "POST", body: ts.readable, }); @@ -1255,7 +1259,7 @@ Deno.test( const w = new BufWriter(conn); const r = new BufReader(conn); - const body = `GET / HTTP/1.1\r\nHost: 127.0.0.1:4501\r\n\r\n`; + const body = `GET / HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\n\r\n`; const writeResult = await w.write(encoder.encode(body)); assertEquals(body.length, writeResult); await w.flush(); @@ -1313,7 +1317,7 @@ Deno.test( promise.resolve(); return new Response(periodicStream()); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -1321,7 +1325,7 @@ Deno.test( await listeningPromise; // start a client - const clientConn = await Deno.connect({ port: 4501 }); + const clientConn = await Deno.connect({ port: servePort }); const r1 = await writeRequest(clientConn); assertEquals(r1, "0\n1\n2\n"); @@ -1345,16 +1349,16 @@ Deno.test( promise.resolve(); return new Response("hello", { headers: { "X-Header-Test": "Æ" } }); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const clientConn = await Deno.connect({ port: 4501 }); + const clientConn = await Deno.connect({ port: servePort }); const requestText = - "GET / HTTP/1.1\r\nHost: 127.0.0.1:4501\r\nX-Header-Test: á\r\n\r\n"; + `GET / HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\nX-Header-Test: á\r\n\r\n`; const requestBytes = new Uint8Array(requestText.length); for (let i = 0; i < requestText.length; i++) { requestBytes[i] = requestText.charCodeAt(i); @@ -1388,19 +1392,19 @@ Deno.test( const server = Deno.serve({ handler: async (request) => { // FIXME: - // assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); + // assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`); assertEquals(await request.text(), ""); promise.resolve(); return new Response("11"); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const clientConn = await Deno.connect({ port: 4501 }); + const clientConn = await Deno.connect({ port: servePort }); async function writeRequest(conn: Deno.Conn) { const encoder = new TextEncoder(); @@ -1408,7 +1412,7 @@ Deno.test( const w = new BufWriter(conn); const r = new BufReader(conn); const body = - `CONNECT 127.0.0.1:4501 HTTP/1.1\r\nHost: 127.0.0.1:4501\r\n\r\n`; + `CONNECT 127.0.0.1:${servePort} HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\n\r\n`; const writeResult = await w.write(encoder.encode(body)); assertEquals(body.length, writeResult); await w.flush(); @@ -1446,7 +1450,7 @@ Deno.test( promise.resolve(); return new Response("ok"); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -1454,7 +1458,7 @@ Deno.test( }); await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { headers: [ ["connection", "close"], ["cookie", "foo=bar"], @@ -1482,21 +1486,20 @@ Deno.test( const ac = new AbortController(); const hostname = "localhost"; - const port = 4501; const server = Deno.serve({ handler: () => { promise.resolve(); return new Response("ok"); }, - port: port, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const url = `http://${hostname}:${port}/`; + const url = `http://${hostname}:${servePort}/`; const args = ["-X", "DELETE", url]; const { success } = await new Deno.Command("curl", { args, @@ -1525,13 +1528,13 @@ Deno.test( promise.resolve(); return new Response(new Uint8Array([128])); }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), }); await listeningPromise; - const resp = await fetch("http://localhost:4501/"); + const resp = await fetch(`http://localhost:${servePort}/`); await promise; @@ -2211,12 +2214,11 @@ Deno.test( const ac = new AbortController(); const listeningPromise = deferred(); const hostname = "127.0.0.1"; - const port = 4501; const server = Deno.serve({ handler: () => new Response("Hello World"), hostname, - port, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -2227,7 +2229,7 @@ Deno.test( await listeningPromise; const caCert = Deno.readTextFileSync("cli/tests/testdata/tls/RootCA.pem"); const client = Deno.createHttpClient({ caCerts: [caCert] }); - const resp = await fetch(`https://localhost:${port}/`, { + const resp = await fetch(`https://localhost:${servePort}/`, { client, headers: { "connection": "close" }, }); @@ -2332,7 +2334,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ handler: (_request) => new Response(null, { status: 204 }), - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -2340,7 +2342,7 @@ Deno.test( try { await listeningPromise; - const resp = await fetch("http://127.0.0.1:4501/", { + const resp = await fetch(`http://127.0.0.1:${servePort}/`, { method: "GET", headers: { "connection": "close" }, }); @@ -2641,7 +2643,7 @@ Deno.test( const server = Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -2940,7 +2942,7 @@ Deno.test( addTrailers(response, [["baz", "why"]]); return response; }, - port: 4501, + port: servePort, signal: ac.signal, onListen: onListen(listeningPromise), onError: createOnErrorCb(ac), @@ -2948,7 +2950,7 @@ Deno.test( // We don't have a great way to access this right now, so just fetch the trailers with cURL const [_, stderr] = await curlRequestWithStdErr([ - "http://localhost:4501/path", + `http://localhost:${servePort}/path`, "-v", "--http2", "--http2-prior-knowledge", diff --git a/cli/tests/unit/streams_deprecated.ts b/cli/tests/unit/streams_deprecated.ts deleted file mode 100644 index 04dbfa3fb7..0000000000 --- a/cli/tests/unit/streams_deprecated.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "./test_util.ts"; - -Deno.test(async function symlinkSyncPerm() { - const rs = new ReadableStream({ - start(controller) { - controller.enqueue("hello "); - controller.enqueue("deno"); - controller.close(); - }, - }); - - for await (const chunk of rs.getIterator()) { - assertEquals(typeof chunk, "string"); - } -}); diff --git a/cli/tests/unit_node/crypto_cipher_test.ts b/cli/tests/unit_node/crypto/crypto_cipher_test.ts similarity index 96% rename from cli/tests/unit_node/crypto_cipher_test.ts rename to cli/tests/unit_node/crypto/crypto_cipher_test.ts index 2c8cca2567..a8a5130cf1 100644 --- a/cli/tests/unit_node/crypto_cipher_test.ts +++ b/cli/tests/unit_node/crypto/crypto_cipher_test.ts @@ -6,13 +6,13 @@ import { buffer, text } from "node:stream/consumers"; import { assertEquals, assertThrows, -} from "../../../test_util/std/testing/asserts.ts"; +} from "../../../../test_util/std/testing/asserts.ts"; const rsaPrivateKey = Deno.readTextFileSync( - new URL("./testdata/rsa_private.pem", import.meta.url), + new URL("../testdata/rsa_private.pem", import.meta.url), ); const rsaPublicKey = Deno.readTextFileSync( - new URL("./testdata/rsa_public.pem", import.meta.url), + new URL("../testdata/rsa_public.pem", import.meta.url), ); const input = new TextEncoder().encode("hello world"); diff --git a/cli/tests/unit_node/crypto_hash.ts b/cli/tests/unit_node/crypto/crypto_hash_test.ts similarity index 88% rename from cli/tests/unit_node/crypto_hash.ts rename to cli/tests/unit_node/crypto/crypto_hash_test.ts index fae66e0244..6795777703 100644 --- a/cli/tests/unit_node/crypto_hash.ts +++ b/cli/tests/unit_node/crypto/crypto_hash_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. import { createHash, createHmac } from "node:crypto"; -import { assertEquals } from "../../../test_util/std/testing/asserts.ts"; +import { assertEquals } from "../../../../test_util/std/testing/asserts.ts"; // https://github.com/denoland/deno/issues/18140 Deno.test({ diff --git a/cli/tests/unit_node/crypto_key.ts b/cli/tests/unit_node/crypto/crypto_key_test.ts similarity index 98% rename from cli/tests/unit_node/crypto_key.ts rename to cli/tests/unit_node/crypto/crypto_key_test.ts index 49d81003f0..672c9fa7f0 100644 --- a/cli/tests/unit_node/crypto_key.ts +++ b/cli/tests/unit_node/crypto/crypto_key_test.ts @@ -13,7 +13,7 @@ import { Buffer } from "node:buffer"; import { assertEquals, assertThrows, -} from "../../../test_util/std/testing/asserts.ts"; +} from "../../../../test_util/std/testing/asserts.ts"; import { createHmac } from "node:crypto"; const generateKeyPairAsync = promisify( diff --git a/cli/tests/unit_node/crypto_sign_test.ts b/cli/tests/unit_node/crypto/crypto_sign_test.ts similarity index 95% rename from cli/tests/unit_node/crypto_sign_test.ts rename to cli/tests/unit_node/crypto/crypto_sign_test.ts index 9d346e7d02..9988ed71c3 100644 --- a/cli/tests/unit_node/crypto_sign_test.ts +++ b/cli/tests/unit_node/crypto/crypto_sign_test.ts @@ -3,18 +3,18 @@ import { assert, assertEquals, -} from "../../../test_util/std/testing/asserts.ts"; +} from "../../../../test_util/std/testing/asserts.ts"; import { createSign, createVerify, sign, verify } from "node:crypto"; import { Buffer } from "node:buffer"; const rsaPrivatePem = Buffer.from( await Deno.readFile( - new URL("./testdata/rsa_private.pem", import.meta.url), + new URL("../testdata/rsa_private.pem", import.meta.url), ), ); const rsaPublicPem = Buffer.from( await Deno.readFile( - new URL("./testdata/rsa_public.pem", import.meta.url), + new URL("../testdata/rsa_public.pem", import.meta.url), ), ); diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml index 115eea326c..e4a992b51e 100644 --- a/test_util/Cargo.toml +++ b/test_util/Cargo.toml @@ -22,6 +22,7 @@ console_static_text.workspace = true fastwebsockets = { workspace = true, features = ["upgrade"] } flate2.workspace = true futures.workspace = true +glob.workspace = true hyper = { workspace = true, features = ["server", "http1", "http2", "runtime"] } lazy-regex.workspace = true lsp-types.workspace = true diff --git a/test_util/src/factory.rs b/test_util/src/factory.rs new file mode 100644 index 0000000000..f11d774d43 --- /dev/null +++ b/test_util/src/factory.rs @@ -0,0 +1,99 @@ +use std::collections::HashSet; +use std::path::PathBuf; + +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use glob::glob; + +/// Generate a unit test factory verified and backed by a glob. +#[macro_export] +macro_rules! unit_test_factory { + ($test_fn:ident, $base:literal, $glob:literal, [ $( $test:ident $(= $($path:ident)/+)? ),+ $(,)? ]) => { + #[test] + fn check_test_glob() { + $crate::factory::check_test_glob($base, $glob, [ $( ( stringify!($test), stringify!( $( $($path)/+ )? ) ) ),+ ].as_slice()); + } + + $( + #[allow(non_snake_case)] + #[test] + fn $test() { + $test_fn($crate::factory::get_path(stringify!($test), stringify!( $( $($path)/+ )?))) + } + )+ + }; + (__test__ $($prefix:ident)* $test:ident) => { + #[allow(non_snake_case)] + #[test] + fn $test() { + $test_fn(stringify!($($prefix)/+ $test)) + } + }; +} + +pub fn get_path(test: &'static str, path: &'static str) -> String { + if path.is_empty() { + test.to_owned() + } else { + path.replace(' ', "") + } +} + +/// Validate that the glob matches the list of tests specified. +pub fn check_test_glob( + base: &'static str, + glob_pattern: &'static str, + files: &[(&'static str, &'static str)], +) { + let base_dir = PathBuf::from(base) + .canonicalize() + .unwrap() + .to_string_lossy() + // Strip Windows slashes + .replace('\\', "/"); + let mut found = HashSet::new(); + let mut list = vec![]; + for file in glob(&format!("{}/{}", base, glob_pattern)) + .expect("Failed to read test path") + { + let mut file = file + .expect("Invalid file from glob") + .canonicalize() + .unwrap(); + file.set_extension(""); + let name = file.file_name().unwrap().to_string_lossy(); + // Strip windows slashes + let file = file.to_string_lossy().replace('\\', "/"); + let file = file + .strip_prefix(&base_dir) + .expect("File {file} did not start with {base_dir} prefix"); + let file = file.strip_prefix('/').unwrap().to_owned(); + if file.contains('/') { + list.push(format!("{}={}", name, file)) + } else { + list.push(file.clone()); + } + found.insert(file); + } + + let mut error = false; + for (test, path) in files { + // Remove spaces from the macro + let path = if path.is_empty() { + (*test).to_owned() + } else { + path.replace(' ', "") + }; + if found.contains(&path) { + found.remove(&path); + } else { + error = true; + } + } + + if error || !found.is_empty() { + panic!( + "Glob did not match provided list of files. Expected: \n[\n {}\n]", + list.join(",\n ") + ); + } +} diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 96dc1c325b..581abb2b1a 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -55,6 +55,7 @@ use url::Url; pub mod assertions; mod builders; +pub mod factory; pub mod lsp; mod npm; pub mod pty;