diff options
Diffstat (limited to 'tests/unit/signal_test.ts')
-rw-r--r-- | tests/unit/signal_test.ts | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/tests/unit/signal_test.ts b/tests/unit/signal_test.ts new file mode 100644 index 000000000..2ba2ffb15 --- /dev/null +++ b/tests/unit/signal_test.ts @@ -0,0 +1,296 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +import { assertEquals, assertThrows, delay } from "./test_util.ts"; + +Deno.test( + { ignore: Deno.build.os !== "windows" }, + function signalsNotImplemented() { + const msg = + "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK)."; + assertThrows( + () => { + Deno.addSignalListener("SIGALRM", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGCHLD", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGHUP", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGIO", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGPIPE", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGQUIT", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGTERM", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGUSR1", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGUSR2", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => { + Deno.addSignalListener("SIGWINCH", () => {}); + }, + Error, + msg, + ); + assertThrows( + () => Deno.addSignalListener("SIGKILL", () => {}), + Error, + msg, + ); + assertThrows( + () => Deno.addSignalListener("SIGSTOP", () => {}), + Error, + msg, + ); + assertThrows( + () => Deno.addSignalListener("SIGILL", () => {}), + Error, + msg, + ); + assertThrows( + () => Deno.addSignalListener("SIGFPE", () => {}), + Error, + msg, + ); + assertThrows( + () => Deno.addSignalListener("SIGSEGV", () => {}), + Error, + msg, + ); + }, +); + +Deno.test( + { + ignore: Deno.build.os === "windows", + permissions: { run: true }, + }, + async function signalListenerTest() { + 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); + // 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.removeSignalListener("SIGUSR1", listener); + await delay(100); + assertEquals(c, 3); + }, +); + +Deno.test( + { + ignore: Deno.build.os === "windows", + permissions: { run: true }, + }, + async function multipleSignalListenerTest() { + let c = ""; + const listener0 = () => { + c += "0"; + }; + 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); + + // 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 (let i = 1; i <= 3; i++) { + await delay(1); + Deno.kill(Deno.pid, "SIGUSR2"); + while (c.length < 6 + i) { + await delay(20); + } + } + + // Sends SIGUSR1 (irrelevant signal) 3 times. + for (const _ of Array(3)) { + await delay(20); + Deno.kill(Deno.pid, "SIGUSR1"); + } + + // No change + assertEquals(c, "010101000"); + + Deno.removeSignalListener("SIGUSR2", listener0); + + await delay(100); + + // The first 3 events are handled by both handlers + // The last 3 events are handled only by handler0 + assertEquals(c, "010101000"); + }, +); + +// This tests that pending op_signal_poll doesn't block the runtime from exiting the process. +Deno.test( + { + permissions: { run: true, read: true }, + }, + async function canExitWhileListeningToSignal() { + const { code } = await new Deno.Command(Deno.execPath(), { + args: [ + "eval", + "--unstable", + "Deno.addSignalListener('SIGINT', () => {})", + ], + }).output(); + assertEquals(code, 0); + }, +); + +Deno.test( + { + ignore: Deno.build.os !== "windows", + permissions: { run: true }, + }, + function windowsThrowsOnNegativeProcessIdTest() { + assertThrows( + () => { + Deno.kill(-1, "SIGKILL"); + }, + TypeError, + "Invalid pid", + ); + }, +); + +Deno.test( + { + ignore: Deno.build.os !== "windows", + permissions: { run: true }, + }, + function noOpenSystemIdleProcessTest() { + let signal: Deno.Signal = "SIGKILL"; + + assertThrows( + () => { + Deno.kill(0, signal); + }, + TypeError, + `Invalid pid`, + ); + + signal = "SIGTERM"; + assertThrows( + () => { + Deno.kill(0, signal); + }, + TypeError, + `Invalid pid`, + ); + }, +); + +Deno.test(function signalInvalidHandlerTest() { + assertThrows(() => { + // deno-lint-ignore no-explicit-any + Deno.addSignalListener("SIGINT", "handler" as any); + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + Deno.removeSignalListener("SIGINT", "handler" as any); + }); +}); + +Deno.test( + { + ignore: Deno.build.os === "windows", + permissions: { run: true }, + }, + function signalForbiddenSignalTest() { + assertThrows( + () => Deno.addSignalListener("SIGKILL", () => {}), + TypeError, + "Binding to signal 'SIGKILL' is not allowed", + ); + assertThrows( + () => Deno.addSignalListener("SIGSTOP", () => {}), + TypeError, + "Binding to signal 'SIGSTOP' is not allowed", + ); + assertThrows( + () => Deno.addSignalListener("SIGILL", () => {}), + TypeError, + "Binding to signal 'SIGILL' is not allowed", + ); + assertThrows( + () => Deno.addSignalListener("SIGFPE", () => {}), + TypeError, + "Binding to signal 'SIGFPE' is not allowed", + ); + assertThrows( + () => Deno.addSignalListener("SIGSEGV", () => {}), + TypeError, + "Binding to signal 'SIGSEGV' is not allowed", + ); + }, +); |