diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index f2ac2484083d12..3625c10a1552cd 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -152,10 +152,25 @@ impl Visit for DenoTestCollector { } ast::Expr::Member(member_expr) => { if let ast::MemberProp::Ident(ns_prop_ident) = &member_expr.prop { + let mut member_expr = member_expr; + let mut ns_prop_ident = ns_prop_ident; + let range = ns_prop_ident.range(); + if matches!(ns_prop_ident.sym.as_str(), "ignore" | "only") { + let ast::Expr::Member(member_expr_) = member_expr.obj.as_ref() + else { + return; + }; + member_expr = member_expr_; + let ast::MemberProp::Ident(ns_prop_ident_) = &member_expr.prop + else { + return; + }; + ns_prop_ident = ns_prop_ident_; + } if ns_prop_ident.sym == "test" { if let ast::Expr::Ident(ident) = member_expr.obj.as_ref() { if ident.sym == "Deno" { - self.check_call_expr(node, &ns_prop_ident.range()); + self.check_call_expr(node, &range); } } } @@ -528,6 +543,10 @@ mod tests { Deno.test(function useFnName() {}); Deno.test("test b", function anotherTest() {}); + + Deno.test.ignore("test ignore", () => {}); + + Deno.test.only("test only", () => {}); "#; let parsed_module = deno_ast::parse_module(deno_ast::ParseParams { specifier: specifier.to_string(), @@ -687,7 +706,103 @@ mod tests { ]) }), data: None, - } + }, + lsp::CodeLens { + range: lsp::Range { + start: lsp::Position { + line: 10, + character: 16, + }, + end: lsp::Position { + line: 10, + character: 22, + }, + }, + command: Some(lsp::Command { + title: "▶\u{fe0e} Run Test".to_string(), + command: "deno.test".to_string(), + arguments: Some(vec![ + json!("https://deno.land/x/mod.ts"), + json!("test ignore"), + json!({ + "inspect": false, + }), + ]), + }), + data: None, + }, + lsp::CodeLens { + range: lsp::Range { + start: lsp::Position { + line: 10, + character: 16, + }, + end: lsp::Position { + line: 10, + character: 22, + }, + }, + command: Some(lsp::Command { + title: "Debug".to_string(), + command: "deno.test".to_string(), + arguments: Some(vec![ + json!("https://deno.land/x/mod.ts"), + json!("test ignore"), + json!({ + "inspect": true, + }), + ]), + }), + data: None, + }, + lsp::CodeLens { + range: lsp::Range { + start: lsp::Position { + line: 12, + character: 16, + }, + end: lsp::Position { + line: 12, + character: 20, + }, + }, + command: Some(lsp::Command { + title: "▶\u{fe0e} Run Test".to_string(), + command: "deno.test".to_string(), + arguments: Some(vec![ + json!("https://deno.land/x/mod.ts"), + json!("test only"), + json!({ + "inspect": false, + }), + ]), + }), + data: None, + }, + lsp::CodeLens { + range: lsp::Range { + start: lsp::Position { + line: 12, + character: 16, + }, + end: lsp::Position { + line: 12, + character: 20, + }, + }, + command: Some(lsp::Command { + title: "Debug".to_string(), + command: "deno.test".to_string(), + arguments: Some(vec![ + json!("https://deno.land/x/mod.ts"), + json!("test only"), + json!({ + "inspect": true, + }), + ]), + }), + data: None, + }, ] ); } diff --git a/cli/tests/integration/jupyter_tests.rs b/cli/tests/integration/jupyter_tests.rs new file mode 100644 index 00000000000000..847290ef82032a --- /dev/null +++ b/cli/tests/integration/jupyter_tests.rs @@ -0,0 +1,8 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +itest!(jupyter_install_command_not_exists { + args: "jupyter --unstable --install", + output: "jupyter/install_command_not_exists.out", + envs: vec![("PATH".to_string(), "".to_string())], + exit_code: 1, +}); diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs index f599e2e8798c4f..19796f245f05b5 100644 --- a/cli/tests/integration/mod.rs +++ b/cli/tests/integration/mod.rs @@ -121,7 +121,10 @@ mod inspector; mod install; #[path = "js_unit_tests.rs"] mod js_unit_tests; -mod jsr_tests; +#[path = "jsr_tests.rs"] +mod jsr; +#[path = "jupyter_tests.rs"] +mod jupyter; #[path = "lint_tests.rs"] mod lint; #[path = "lsp_tests.rs"] diff --git a/cli/tests/testdata/jupyter/install_command_not_exists.out b/cli/tests/testdata/jupyter/install_command_not_exists.out new file mode 100644 index 00000000000000..3d599f7fc19e0b --- /dev/null +++ b/cli/tests/testdata/jupyter/install_command_not_exists.out @@ -0,0 +1,4 @@ +error: Failed to spawn 'jupyter' command. Is JupyterLab installed (https://jupyter.org/install) and available on the PATH? + +Caused by: +[WILDCARD] diff --git a/cli/tests/unit/signal_test.ts b/cli/tests/unit/signal_test.ts index 3a1ebece983431..2ba2ffb15a2b78 100644 --- a/cli/tests/unit/signal_test.ts +++ b/cli/tests/unit/signal_test.ts @@ -110,27 +110,25 @@ Deno.test( permissions: { run: true }, }, async function signalListenerTest() { - const { promise, resolve } = Promise.withResolvers(); let c = 0; const listener = () => { c += 1; }; + // This test needs to be careful that it doesn't accidentally aggregate multiple + // signals into one. Sending two or more SIGxxx before the handler can be run will + // result in signal coalescing. Deno.addSignalListener("SIGUSR1", listener); - setTimeout(async () => { - // Sends SIGUSR1 3 times. - for (const _ of Array(3)) { + // Sends SIGUSR1 3 times. + for (let i = 1; i <= 3; i++) { + await delay(1); + Deno.kill(Deno.pid, "SIGUSR1"); + while (c < i) { await delay(20); - Deno.kill(Deno.pid, "SIGUSR1"); } - await promise; - Deno.removeSignalListener("SIGUSR1", listener); - }); - - // We'll get three signals eventually - while (c < 3) { - await delay(20); } - resolve(); + Deno.removeSignalListener("SIGUSR1", listener); + await delay(100); + assertEquals(c, 3); }, ); @@ -140,7 +138,6 @@ Deno.test( permissions: { run: true }, }, async function multipleSignalListenerTest() { - const { promise, resolve } = Promise.withResolvers(); let c = ""; const listener0 = () => { c += "0"; @@ -148,39 +145,45 @@ Deno.test( const listener1 = () => { c += "1"; }; + // This test needs to be careful that it doesn't accidentally aggregate multiple + // signals into one. Sending two or more SIGxxx before the handler can be run will + // result in signal coalescing. Deno.addSignalListener("SIGUSR2", listener0); Deno.addSignalListener("SIGUSR2", listener1); - setTimeout(async () => { - // Sends SIGUSR2 3 times. - for (const _ of Array(3)) { - await delay(20); - Deno.kill(Deno.pid, "SIGUSR2"); - } - while (c.length < 6) { + + // Sends SIGUSR2 3 times. + for (let i = 1; i <= 3; i++) { + await delay(1); + Deno.kill(Deno.pid, "SIGUSR2"); + while (c.length < i * 2) { await delay(20); } - Deno.removeSignalListener("SIGUSR2", listener1); - // Sends SIGUSR2 3 times. - for (const _ of Array(3)) { + } + + Deno.removeSignalListener("SIGUSR2", listener1); + + // Sends SIGUSR2 3 times. + for (let i = 1; i <= 3; i++) { + await delay(1); + Deno.kill(Deno.pid, "SIGUSR2"); + while (c.length < 6 + i) { await delay(20); - Deno.kill(Deno.pid, "SIGUSR2"); } + } + + // Sends SIGUSR1 (irrelevant signal) 3 times. + for (const _ of Array(3)) { await delay(20); - // Sends SIGUSR1 (irrelevant signal) 3 times. - for (const _ of Array(3)) { - await delay(20); - Deno.kill(Deno.pid, "SIGUSR1"); - } + Deno.kill(Deno.pid, "SIGUSR1"); + } - while (c.length < 9) { - await delay(20); - } + // No change + assertEquals(c, "010101000"); + + Deno.removeSignalListener("SIGUSR2", listener0); - Deno.removeSignalListener("SIGUSR2", listener0); - resolve(); - }); + await delay(100); - await promise; // The first 3 events are handled by both handlers // The last 3 events are handled only by handler0 assertEquals(c, "010101000"); diff --git a/cli/tests/unit_node/zlib_test.ts b/cli/tests/unit_node/zlib_test.ts index 1819be268d3750..fa94493c134364 100644 --- a/cli/tests/unit_node/zlib_test.ts +++ b/cli/tests/unit_node/zlib_test.ts @@ -9,6 +9,8 @@ import { createBrotliCompress, createBrotliDecompress, createDeflate, + gzipSync, + unzipSync, } from "node:zlib"; import { Buffer } from "node:buffer"; import { createReadStream, createWriteStream } from "node:fs"; @@ -32,6 +34,13 @@ Deno.test("brotli compression async", async () => { assertEquals(decompressed.toString(), "hello world"); }); +Deno.test("gzip compression sync", { sanitizeResources: false }, () => { + const buf = Buffer.from("hello world"); + const compressed = gzipSync(buf); + const decompressed = unzipSync(compressed); + assertEquals(decompressed.toString(), "hello world"); +}); + Deno.test("brotli compression", async () => { const { promise, resolve } = Promise.withResolvers(); const compress = createBrotliCompress(); @@ -125,3 +134,24 @@ Deno.test("should work with a buffer from an encoded string", () => { const decompressed = brotliDecompressSync(compressed); assertEquals(decompressed.toString(), "hello world"); }); + +Deno.test( + "zlib compression with dataview", + { sanitizeResources: false }, + () => { + const buf = Buffer.from("hello world"); + const compressed = gzipSync(new DataView(buf.buffer)); + const decompressed = unzipSync(compressed); + assertEquals(decompressed.toString(), "hello world"); + }, +); + +Deno.test("zlib compression with an encoded string", { + sanitizeResources: false, +}, () => { + const encoder = new TextEncoder(); + const buffer = encoder.encode("hello world"); + const compressed = gzipSync(buffer); + const decompressed = unzipSync(compressed); + assertEquals(decompressed.toString(), "hello world"); +}); diff --git a/cli/tools/jupyter/install.rs b/cli/tools/jupyter/install.rs index ef442e1255d215..3b2079db060efe 100644 --- a/cli/tools/jupyter/install.rs +++ b/cli/tools/jupyter/install.rs @@ -6,6 +6,7 @@ use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::json; use std::env::current_exe; +use std::io::ErrorKind; use std::io::Write; use std::path::Path; use tempfile::TempDir; @@ -76,19 +77,34 @@ pub fn install() -> Result<(), AnyError> { &temp_dir.path().to_string_lossy(), ]) .spawn(); + let mut child = match child_result { + Ok(child) => child, + Err(err) + if matches!( + err.kind(), + ErrorKind::NotFound | ErrorKind::PermissionDenied + ) => + { + return Err(err).context(concat!( + "Failed to spawn 'jupyter' command. Is JupyterLab installed ", + "(https://jupyter.org/install) and available on the PATH?" + )); + } + Err(err) => { + return Err(err).context("Failed to spawn 'jupyter' command."); + } + }; - if let Ok(mut child) = child_result { - let wait_result = child.wait(); - match wait_result { - Ok(status) => { - if !status.success() { - bail!("Failed to install kernelspec, try again."); - } - } - Err(err) => { - bail!("Failed to install kernelspec: {}", err); + let wait_result = child.wait(); + match wait_result { + Ok(status) => { + if !status.success() { + bail!("Failed to install kernelspec, try again."); } } + Err(err) => { + bail!("Failed to install kernelspec: {}", err); + } } let _ = std::fs::remove_dir(temp_dir); diff --git a/ext/node/polyfills/_zlib.mjs b/ext/node/polyfills/_zlib.mjs index a66ab6d0460da7..15a0a51e3258da 100644 --- a/ext/node/polyfills/_zlib.mjs +++ b/ext/node/polyfills/_zlib.mjs @@ -155,10 +155,27 @@ export const inflateRawSync = function (buffer, opts) { return zlibBufferSync(new InflateRaw(opts), buffer); }; +function sanitizeInput(input) { + if (typeof input === "string") input = Buffer.from(input); + + if ( + !Buffer.isBuffer(input) && + (input.buffer && !input.buffer.constructor === ArrayBuffer) + ) throw new TypeError("Not a string, buffer or dataview"); + + if (input.buffer) { + input = new Uint8Array(input.buffer, input.byteOffset, input.byteLength); + } + + return input; +} + function zlibBuffer(engine, buffer, callback) { var buffers = []; var nread = 0; + buffer = sanitizeInput(buffer); + engine.on("error", onError); engine.on("end", onEnd); @@ -197,9 +214,7 @@ function zlibBuffer(engine, buffer, callback) { } function zlibBufferSync(engine, buffer) { - if (typeof buffer === "string") buffer = Buffer.from(buffer); - - if (!Buffer.isBuffer(buffer)) throw new TypeError("Not a string or buffer"); + buffer = sanitizeInput(buffer); var flushFlag = engine._finishFlushFlag;