mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
71e4ac774b
These are unstable options and the APIs is now deprecated. To limit amount of unstable flags we elected to have these APIs removed.
616 lines
16 KiB
TypeScript
616 lines
16 KiB
TypeScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
import {
|
|
assert,
|
|
assertEquals,
|
|
assertStrictEquals,
|
|
assertStringIncludes,
|
|
assertThrows,
|
|
DENO_FUTURE,
|
|
} from "./test_util.ts";
|
|
|
|
Deno.test(
|
|
{ permissions: { read: true, run: false } },
|
|
function runPermissions() {
|
|
assertThrows(() => {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
|
|
});
|
|
}, Deno.errors.PermissionDenied);
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runSuccess() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
// freeze the array to ensure it's not modified
|
|
cmd: Object.freeze([
|
|
Deno.execPath(),
|
|
"eval",
|
|
"console.log('hello world')",
|
|
]),
|
|
stdout: "piped",
|
|
stderr: "null",
|
|
});
|
|
const status = await p.status();
|
|
assertEquals(status.success, true);
|
|
assertEquals(status.code, 0);
|
|
assertEquals(status.signal, undefined);
|
|
p.stdout.close();
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runUrl() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
new URL(`file:///${Deno.execPath()}`),
|
|
"eval",
|
|
"console.log('hello world')",
|
|
],
|
|
stdout: "piped",
|
|
stderr: "null",
|
|
});
|
|
const status = await p.status();
|
|
assertEquals(status.success, true);
|
|
assertEquals(status.code, 0);
|
|
assertEquals(status.signal, undefined);
|
|
p.stdout.close();
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runStdinRid0(): Promise<
|
|
void
|
|
> {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
|
|
stdin: 0,
|
|
stdout: "piped",
|
|
stderr: "null",
|
|
});
|
|
const status = await p.status();
|
|
assertEquals(status.success, true);
|
|
assertEquals(status.code, 0);
|
|
assertEquals(status.signal, undefined);
|
|
p.stdout.close();
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
function runInvalidStdio() {
|
|
assertThrows(() =>
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
|
|
// @ts-expect-error because Deno.run should throw on invalid stdin.
|
|
stdin: "a",
|
|
})
|
|
);
|
|
assertThrows(() =>
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
|
|
// @ts-expect-error because Deno.run should throw on invalid stdout.
|
|
stdout: "b",
|
|
})
|
|
);
|
|
assertThrows(() =>
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
|
|
// @ts-expect-error because Deno.run should throw on invalid stderr.
|
|
stderr: "c",
|
|
})
|
|
);
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runCommandFailedWithCode() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "Deno.exit(41 + 1)"],
|
|
});
|
|
const status = await p.status();
|
|
assertEquals(status.success, false);
|
|
assertEquals(status.code, 42);
|
|
assertEquals(status.signal, undefined);
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{
|
|
permissions: { run: true, read: true },
|
|
},
|
|
async function runCommandFailedWithSignal() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"Deno.kill(Deno.pid, 'SIGKILL')",
|
|
],
|
|
});
|
|
const status = await p.status();
|
|
assertEquals(status.success, false);
|
|
if (Deno.build.os === "windows") {
|
|
assertEquals(status.code, 1);
|
|
assertEquals(status.signal, undefined);
|
|
} else {
|
|
assertEquals(status.code, 128 + 9);
|
|
assertEquals(status.signal, 9);
|
|
}
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test({ permissions: { run: true } }, function runNotFound() {
|
|
let error;
|
|
try {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
Deno.run({ cmd: ["this file hopefully doesn't exist"] });
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
assert(error !== undefined);
|
|
assert(error instanceof Deno.errors.NotFound);
|
|
});
|
|
|
|
Deno.test(
|
|
{ permissions: { write: true, run: true, read: true } },
|
|
async function runWithCwdIsAsync() {
|
|
const enc = new TextEncoder();
|
|
const cwd = await Deno.makeTempDir({ prefix: "deno_command_test" });
|
|
|
|
const exitCodeFile = "deno_was_here";
|
|
const programFile = "poll_exit.ts";
|
|
const program = `
|
|
async function tryExit() {
|
|
try {
|
|
const code = parseInt(await Deno.readTextFile("${exitCodeFile}"));
|
|
Deno.exit(code);
|
|
} catch {
|
|
// Retry if we got here before deno wrote the file.
|
|
setTimeout(tryExit, 0.01);
|
|
}
|
|
}
|
|
|
|
tryExit();
|
|
`;
|
|
|
|
Deno.writeFileSync(`${cwd}/${programFile}`, enc.encode(program));
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cwd,
|
|
cmd: [Deno.execPath(), "run", "--allow-read", programFile],
|
|
});
|
|
|
|
// Write the expected exit code *after* starting deno.
|
|
// This is how we verify that `run()` is actually asynchronous.
|
|
const code = 84;
|
|
Deno.writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`));
|
|
|
|
const status = await p.status();
|
|
assertEquals(status.success, false);
|
|
assertEquals(status.code, code);
|
|
assertEquals(status.signal, undefined);
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runStdinPiped(): Promise<
|
|
void
|
|
> {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
`
|
|
const buffer = new Uint8Array(5);
|
|
await Deno.stdin.read(buffer);
|
|
if (new TextDecoder().decode(buffer) !== "hello") {
|
|
throw new Error('Expected \\'hello\\'')
|
|
}
|
|
`,
|
|
],
|
|
stdin: "piped",
|
|
});
|
|
assert(p.stdin);
|
|
assert(!p.stdout);
|
|
assert(!p.stderr);
|
|
|
|
const msg = new TextEncoder().encode("hello");
|
|
const n = await p.stdin.write(msg);
|
|
assertEquals(n, msg.byteLength);
|
|
|
|
p.stdin.close();
|
|
|
|
const status = await p.status();
|
|
assertEquals(status.success, true);
|
|
assertEquals(status.code, 0);
|
|
assertEquals(status.signal, undefined);
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runStdoutPiped(): Promise<
|
|
void
|
|
> {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"await Deno.stdout.write(new TextEncoder().encode('hello'))",
|
|
],
|
|
stdout: "piped",
|
|
});
|
|
assert(!p.stdin);
|
|
assert(!p.stderr);
|
|
|
|
const data = new Uint8Array(10);
|
|
let r = await p.stdout.read(data);
|
|
if (r === null) {
|
|
throw new Error("p.stdout.read(...) should not be null");
|
|
}
|
|
assertEquals(r, 5);
|
|
const s = new TextDecoder().decode(data.subarray(0, r));
|
|
assertEquals(s, "hello");
|
|
r = await p.stdout.read(data);
|
|
assertEquals(r, null);
|
|
p.stdout.close();
|
|
|
|
const status = await p.status();
|
|
assertEquals(status.success, true);
|
|
assertEquals(status.code, 0);
|
|
assertEquals(status.signal, undefined);
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runStderrPiped(): Promise<
|
|
void
|
|
> {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"await Deno.stderr.write(new TextEncoder().encode('hello'))",
|
|
],
|
|
stderr: "piped",
|
|
});
|
|
assert(!p.stdin);
|
|
assert(!p.stdout);
|
|
|
|
const data = new Uint8Array(10);
|
|
let r = await p.stderr.read(data);
|
|
if (r === null) {
|
|
throw new Error("p.stderr.read should not return null here");
|
|
}
|
|
assertEquals(r, 5);
|
|
const s = new TextDecoder().decode(data.subarray(0, r));
|
|
assertEquals(s, "hello");
|
|
r = await p.stderr.read(data);
|
|
assertEquals(r, null);
|
|
p.stderr!.close();
|
|
|
|
const status = await p.status();
|
|
assertEquals(status.success, true);
|
|
assertEquals(status.code, 0);
|
|
assertEquals(status.signal, undefined);
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runOutput() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"await Deno.stdout.write(new TextEncoder().encode('hello'))",
|
|
],
|
|
stdout: "piped",
|
|
});
|
|
const output = await p.output();
|
|
const s = new TextDecoder().decode(output);
|
|
assertEquals(s, "hello");
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runStderrOutput(): Promise<
|
|
void
|
|
> {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"await Deno.stderr.write(new TextEncoder().encode('error'))",
|
|
],
|
|
stderr: "piped",
|
|
});
|
|
const error = await p.stderrOutput();
|
|
const s = new TextDecoder().decode(error);
|
|
assertEquals(s, "error");
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{
|
|
// Ignoring because uses `file.rid`
|
|
ignore: DENO_FUTURE,
|
|
permissions: { run: true, write: true, read: true },
|
|
},
|
|
async function runRedirectStdoutStderr() {
|
|
const tempDir = await Deno.makeTempDir();
|
|
const fileName = tempDir + "/redirected_stdio.txt";
|
|
using file = await Deno.open(fileName, {
|
|
create: true,
|
|
write: true,
|
|
});
|
|
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"Deno.stderr.write(new TextEncoder().encode('error\\n')); Deno.stdout.write(new TextEncoder().encode('output\\n'));",
|
|
],
|
|
stdout: file.rid,
|
|
stderr: file.rid,
|
|
});
|
|
|
|
await p.status();
|
|
p.close();
|
|
|
|
const fileContents = await Deno.readFile(fileName);
|
|
const decoder = new TextDecoder();
|
|
const text = decoder.decode(fileContents);
|
|
|
|
assertStringIncludes(text, "error");
|
|
assertStringIncludes(text, "output");
|
|
// deno-lint-ignore no-console
|
|
console.log("finished tgis test");
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{
|
|
// Ignoring because uses `file.rid`
|
|
ignore: DENO_FUTURE,
|
|
permissions: { run: true, write: true, read: true },
|
|
},
|
|
async function runRedirectStdin() {
|
|
const tempDir = await Deno.makeTempDir();
|
|
const fileName = tempDir + "/redirected_stdio.txt";
|
|
await Deno.writeTextFile(fileName, "hello");
|
|
using file = await Deno.open(fileName);
|
|
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
`
|
|
const buffer = new Uint8Array(5);
|
|
await Deno.stdin.read(buffer);
|
|
if (new TextDecoder().decode(buffer) !== "hello") {
|
|
throw new Error('Expected \\'hello\\'')
|
|
}
|
|
`,
|
|
],
|
|
stdin: file.rid,
|
|
});
|
|
|
|
const status = await p.status();
|
|
assertEquals(status.code, 0);
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runEnv() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"Deno.stdout.write(new TextEncoder().encode(Deno.env.get('FOO') + Deno.env.get('BAR')))",
|
|
],
|
|
env: {
|
|
FOO: "0123",
|
|
BAR: "4567",
|
|
},
|
|
stdout: "piped",
|
|
});
|
|
const output = await p.output();
|
|
const s = new TextDecoder().decode(output);
|
|
assertEquals(s, "01234567");
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runClose() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
"setTimeout(() => Deno.stdout.write(new TextEncoder().encode('error')), 10000)",
|
|
],
|
|
stderr: "piped",
|
|
});
|
|
assert(!p.stdin);
|
|
assert(!p.stdout);
|
|
|
|
p.close();
|
|
|
|
const data = new Uint8Array(10);
|
|
const r = await p.stderr.read(data);
|
|
assertEquals(r, null);
|
|
p.stderr.close();
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function runKillAfterStatus() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [Deno.execPath(), "eval", 'console.log("hello")'],
|
|
});
|
|
await p.status();
|
|
|
|
let error = null;
|
|
try {
|
|
p.kill("SIGTERM");
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
|
|
assert(
|
|
error instanceof Deno.errors.NotFound ||
|
|
// On Windows, the underlying Windows API may return
|
|
// `ERROR_ACCESS_DENIED` when the process has exited, but hasn't been
|
|
// completely cleaned up yet and its `pid` is still valid.
|
|
(Deno.build.os === "windows" &&
|
|
error instanceof Deno.errors.PermissionDenied),
|
|
);
|
|
|
|
p.close();
|
|
},
|
|
);
|
|
|
|
Deno.test({ permissions: { run: false } }, function killPermissions() {
|
|
assertThrows(() => {
|
|
// Unlike the other test cases, we don't have permission to spawn a
|
|
// subprocess we can safely kill. Instead we send SIGCONT to the current
|
|
// process - assuming that Deno does not have a special handler set for it
|
|
// and will just continue even if a signal is erroneously sent.
|
|
Deno.kill(Deno.pid, "SIGCONT");
|
|
}, Deno.errors.PermissionDenied);
|
|
});
|
|
|
|
Deno.test(
|
|
{ ignore: Deno.build.os !== "windows", permissions: { run: true } },
|
|
function negativePidInvalidWindows() {
|
|
assertThrows(() => {
|
|
Deno.kill(-1, "SIGTERM");
|
|
}, TypeError);
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ ignore: Deno.build.os !== "windows", permissions: { run: true } },
|
|
function invalidSignalNameWindows() {
|
|
assertThrows(() => {
|
|
Deno.kill(Deno.pid, "SIGUSR1");
|
|
}, TypeError);
|
|
},
|
|
);
|
|
|
|
Deno.test(
|
|
{ permissions: { run: true, read: true } },
|
|
async function killSuccess() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "setTimeout(() => {}, 10000)"],
|
|
});
|
|
|
|
try {
|
|
Deno.kill(p.pid, "SIGKILL");
|
|
const status = await p.status();
|
|
|
|
assertEquals(status.success, false);
|
|
if (Deno.build.os === "windows") {
|
|
assertEquals(status.code, 1);
|
|
assertEquals(status.signal, undefined);
|
|
} else {
|
|
assertEquals(status.code, 137);
|
|
assertEquals(status.signal, 9);
|
|
}
|
|
} finally {
|
|
p.close();
|
|
}
|
|
},
|
|
);
|
|
|
|
Deno.test({ permissions: { run: true, read: true } }, function killFailed() {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [Deno.execPath(), "eval", "setTimeout(() => {}, 10000)"],
|
|
});
|
|
assert(!p.stdin);
|
|
assert(!p.stdout);
|
|
|
|
assertThrows(() => {
|
|
// @ts-expect-error testing runtime error of bad signal
|
|
Deno.kill(p.pid, "foobar");
|
|
}, TypeError);
|
|
|
|
p.close();
|
|
});
|
|
|
|
Deno.test(
|
|
{
|
|
permissions: { run: true, read: true, write: true },
|
|
ignore: Deno.build.os === "windows",
|
|
},
|
|
async function non_existent_cwd(): Promise<void> {
|
|
// deno-lint-ignore no-deprecated-deno-api
|
|
const p = Deno.run({
|
|
cmd: [
|
|
Deno.execPath(),
|
|
"eval",
|
|
`const dir = Deno.makeTempDirSync();
|
|
Deno.chdir(dir);
|
|
Deno.removeSync(dir);
|
|
const p = Deno.run({cmd:[Deno.execPath(), "eval", "console.log(1);"]});
|
|
const { code } = await p.status();
|
|
p.close();
|
|
Deno.exit(code);
|
|
`,
|
|
],
|
|
stdout: "piped",
|
|
stderr: "piped",
|
|
});
|
|
|
|
const { code } = await p.status();
|
|
const stderr = new TextDecoder().decode(await p.stderrOutput());
|
|
p.close();
|
|
p.stdout.close();
|
|
assertStrictEquals(code, 1);
|
|
assertStringIncludes(stderr, "Failed getting cwd.");
|
|
},
|
|
);
|