2020-09-21 08:26:41 -04:00
|
|
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
2020-12-03 13:57:35 -05:00
|
|
|
import Context, { ExitStatus } from "./snapshot_preview1.ts";
|
|
|
|
import { assert, assertEquals, assertThrows } from "../testing/asserts.ts";
|
2020-08-25 08:36:50 -04:00
|
|
|
import { copy } from "../fs/mod.ts";
|
2020-06-24 09:27:31 -04:00
|
|
|
import * as path from "../path/mod.ts";
|
|
|
|
|
2020-09-04 07:14:51 -04:00
|
|
|
const tests = [
|
2020-10-20 16:54:27 -04:00
|
|
|
"testdata/std_env_args.wasm",
|
|
|
|
"testdata/std_env_vars.wasm",
|
|
|
|
"testdata/std_fs_create_dir.wasm",
|
|
|
|
"testdata/std_fs_file_create.wasm",
|
|
|
|
"testdata/std_fs_file_metadata.wasm",
|
|
|
|
"testdata/std_fs_file_seek.wasm",
|
|
|
|
"testdata/std_fs_file_set_len.wasm",
|
|
|
|
"testdata/std_fs_file_sync_all.wasm",
|
|
|
|
"testdata/std_fs_file_sync_data.wasm",
|
|
|
|
"testdata/std_fs_hard_link.wasm",
|
|
|
|
"testdata/std_fs_metadata.wasm",
|
|
|
|
"testdata/std_fs_read.wasm",
|
|
|
|
"testdata/std_fs_read_dir.wasm",
|
|
|
|
"testdata/std_fs_remove_dir_all.wasm",
|
|
|
|
"testdata/std_fs_rename.wasm",
|
|
|
|
"testdata/std_fs_symlink_metadata.wasm",
|
|
|
|
"testdata/std_fs_write.wasm",
|
2020-09-04 07:14:51 -04:00
|
|
|
"testdata/std_io_stderr.wasm",
|
|
|
|
"testdata/std_io_stdin.wasm",
|
|
|
|
"testdata/std_io_stdout.wasm",
|
|
|
|
"testdata/std_process_exit.wasm",
|
2020-10-20 16:54:27 -04:00
|
|
|
"testdata/wasi_clock_res_get.wasm",
|
|
|
|
"testdata/wasi_clock_time_get.wasm",
|
|
|
|
"testdata/wasi_fd_fdstat_get.wasm",
|
|
|
|
"testdata/wasi_fd_fdstat_get.wasm",
|
|
|
|
"testdata/wasi_fd_fdstat_get.wasm",
|
2020-09-21 09:17:01 -04:00
|
|
|
"testdata/wasi_fd_renumber.wasm",
|
2020-09-04 07:14:51 -04:00
|
|
|
"testdata/wasi_fd_tell_file.wasm",
|
|
|
|
"testdata/wasi_fd_write_file.wasm",
|
|
|
|
"testdata/wasi_fd_write_stderr.wasm",
|
|
|
|
"testdata/wasi_fd_write_stdout.wasm",
|
2020-10-24 10:04:59 -04:00
|
|
|
"testdata/wasi_path_open.wasm",
|
2020-10-20 16:54:27 -04:00
|
|
|
"testdata/wasi_proc_exit.wasm",
|
2020-09-04 07:14:51 -04:00
|
|
|
"testdata/wasi_random_get.wasm",
|
2020-09-16 16:30:12 -04:00
|
|
|
"testdata/wasi_sched_yield.wasm",
|
2020-09-04 07:14:51 -04:00
|
|
|
];
|
|
|
|
|
2020-10-26 11:57:09 -04:00
|
|
|
const ignore = [];
|
2020-08-25 08:36:50 -04:00
|
|
|
|
|
|
|
// TODO(caspervonb) investigate why these tests are failing on windows and fix
|
|
|
|
// them.
|
2020-10-24 10:04:59 -04:00
|
|
|
// The failing tests all involve symlinks in some way, my best guess so far is
|
|
|
|
// that there's something going wrong with copying the symlinks over to the
|
|
|
|
// temporary working directory, but only in some cases.
|
2020-08-25 08:36:50 -04:00
|
|
|
if (Deno.build.os == "windows") {
|
2020-10-20 16:54:27 -04:00
|
|
|
ignore.push("testdata/std_fs_metadata.wasm");
|
|
|
|
ignore.push("testdata/std_fs_read_dir.wasm");
|
2020-10-24 10:04:59 -04:00
|
|
|
ignore.push("testdata/wasi_path_open.wasm");
|
2020-08-25 08:36:50 -04:00
|
|
|
}
|
|
|
|
|
2020-08-27 05:21:57 -04:00
|
|
|
const rootdir = path.dirname(path.fromFileUrl(import.meta.url));
|
|
|
|
const testdir = path.join(rootdir, "testdata");
|
2020-06-24 09:27:31 -04:00
|
|
|
|
2020-09-04 07:14:51 -04:00
|
|
|
for (const pathname of tests) {
|
2020-08-27 05:21:57 -04:00
|
|
|
Deno.test({
|
2020-09-04 07:14:51 -04:00
|
|
|
name: path.basename(pathname),
|
|
|
|
ignore: ignore.includes(pathname),
|
2020-08-27 05:21:57 -04:00
|
|
|
fn: async function () {
|
|
|
|
const prelude = await Deno.readTextFile(
|
2020-09-04 07:14:51 -04:00
|
|
|
path.resolve(rootdir, pathname.replace(/\.wasm$/, ".json")),
|
2020-08-27 05:21:57 -04:00
|
|
|
);
|
|
|
|
const options = JSON.parse(prelude);
|
|
|
|
|
2020-10-24 10:04:59 -04:00
|
|
|
// TODO(caspervonb) investigate more.
|
|
|
|
// On Windows creating a tempdir in the default directory breaks nearly
|
|
|
|
// all the tests, possibly due to symlinks pointing to the original file
|
|
|
|
// which crosses drive boundaries.
|
|
|
|
const workdir = await Deno.makeTempDir({
|
|
|
|
dir: testdir,
|
|
|
|
});
|
|
|
|
|
2020-08-27 05:21:57 -04:00
|
|
|
await copy(
|
|
|
|
path.join(testdir, "fixtures"),
|
|
|
|
path.join(workdir, "fixtures"),
|
|
|
|
);
|
|
|
|
|
|
|
|
try {
|
|
|
|
const process = await Deno.run({
|
|
|
|
cwd: workdir,
|
|
|
|
cmd: [
|
|
|
|
`${Deno.execPath()}`,
|
|
|
|
"run",
|
|
|
|
"--quiet",
|
|
|
|
"--unstable",
|
|
|
|
"--allow-all",
|
2020-12-10 07:36:07 -05:00
|
|
|
"--no-check",
|
2020-08-27 05:21:57 -04:00
|
|
|
path.resolve(rootdir, "snapshot_preview1_test_runner.ts"),
|
|
|
|
prelude,
|
2020-09-04 07:14:51 -04:00
|
|
|
path.resolve(rootdir, pathname),
|
2020-08-27 05:21:57 -04:00
|
|
|
],
|
|
|
|
stdin: "piped",
|
|
|
|
stdout: "piped",
|
|
|
|
stderr: "piped",
|
|
|
|
});
|
|
|
|
|
|
|
|
if (options.stdin) {
|
|
|
|
const stdin = new TextEncoder().encode(options.stdin);
|
|
|
|
await Deno.writeAll(process.stdin, stdin);
|
|
|
|
}
|
2020-06-24 09:27:31 -04:00
|
|
|
|
2020-08-27 05:21:57 -04:00
|
|
|
process.stdin.close();
|
|
|
|
|
|
|
|
const stdout = await Deno.readAll(process.stdout);
|
|
|
|
|
|
|
|
if (options.stdout) {
|
|
|
|
assertEquals(new TextDecoder().decode(stdout), options.stdout);
|
|
|
|
} else {
|
|
|
|
await Deno.writeAll(Deno.stdout, stdout);
|
2020-08-25 08:36:50 -04:00
|
|
|
}
|
2020-08-27 05:21:57 -04:00
|
|
|
|
|
|
|
process.stdout.close();
|
|
|
|
|
|
|
|
const stderr = await Deno.readAll(process.stderr);
|
|
|
|
|
|
|
|
if (options.stderr) {
|
|
|
|
assertEquals(new TextDecoder().decode(stderr), options.stderr);
|
|
|
|
} else {
|
|
|
|
await Deno.writeAll(Deno.stderr, stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
process.stderr.close();
|
|
|
|
|
|
|
|
const status = await process.status();
|
|
|
|
assertEquals(status.code, options.exitCode ? +options.exitCode : 0);
|
|
|
|
|
|
|
|
process.close();
|
|
|
|
} catch (err) {
|
|
|
|
throw err;
|
|
|
|
} finally {
|
|
|
|
await Deno.remove(workdir, { recursive: true });
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
2020-06-24 09:27:31 -04:00
|
|
|
}
|
2020-11-02 12:50:45 -05:00
|
|
|
|
|
|
|
Deno.test("context_start", function () {
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.start({
|
|
|
|
exports: {
|
|
|
|
_start() {},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"must provide a memory export",
|
|
|
|
);
|
|
|
|
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.start({
|
|
|
|
exports: {
|
|
|
|
_initialize() {},
|
|
|
|
memory: new WebAssembly.Memory({ initial: 1 }),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"export _initialize must not be a function",
|
|
|
|
);
|
|
|
|
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.start({
|
|
|
|
exports: {
|
|
|
|
memory: new WebAssembly.Memory({ initial: 1 }),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"export _start must be a function",
|
|
|
|
);
|
2020-12-03 13:57:35 -05:00
|
|
|
|
|
|
|
try {
|
|
|
|
const context = new Context({
|
|
|
|
exitOnReturn: false,
|
|
|
|
});
|
|
|
|
context.start({
|
|
|
|
exports: {
|
|
|
|
_start() {
|
|
|
|
const exit = context.exports["proc_exit"] as CallableFunction;
|
|
|
|
exit(0);
|
|
|
|
},
|
|
|
|
memory: new WebAssembly.Memory({ initial: 1 }),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} catch (err) {
|
|
|
|
assert(err instanceof ExitStatus);
|
|
|
|
assertEquals(err.code, 0);
|
|
|
|
}
|
2020-12-10 07:37:09 -05:00
|
|
|
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.start({
|
|
|
|
exports: {
|
|
|
|
memory: new WebAssembly.Memory({ initial: 1 }),
|
|
|
|
_start() {},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
context.start({
|
|
|
|
exports: {},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
Error,
|
|
|
|
"WebAssembly.Instance has already started",
|
|
|
|
);
|
2020-11-02 12:50:45 -05:00
|
|
|
});
|
2020-12-03 13:36:13 -05:00
|
|
|
|
|
|
|
Deno.test("context_initialize", function () {
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.initialize({
|
|
|
|
exports: {
|
|
|
|
_initialize() {},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"must provide a memory export",
|
|
|
|
);
|
|
|
|
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.initialize({
|
|
|
|
exports: {
|
|
|
|
_start() {},
|
|
|
|
memory: new WebAssembly.Memory({ initial: 1 }),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"export _start must not be a function",
|
|
|
|
);
|
|
|
|
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.initialize({
|
|
|
|
exports: {
|
|
|
|
memory: new WebAssembly.Memory({ initial: 1 }),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"export _initialize must be a function",
|
|
|
|
);
|
2020-12-10 07:37:09 -05:00
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
const context = new Context({});
|
|
|
|
context.initialize({
|
|
|
|
exports: {
|
|
|
|
memory: new WebAssembly.Memory({ initial: 1 }),
|
|
|
|
_initialize() {},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
context.initialize({
|
|
|
|
exports: {},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
Error,
|
|
|
|
"WebAssembly.Instance has already started",
|
|
|
|
);
|
2020-12-03 13:36:13 -05:00
|
|
|
});
|
2021-01-05 06:16:46 -05:00
|
|
|
|
|
|
|
Deno.test("std_io_stdin.wasm with stdin as file", function () {
|
|
|
|
const stdinPath = Deno.makeTempFileSync();
|
|
|
|
Deno.writeTextFileSync(stdinPath, "Hello, stdin!");
|
|
|
|
|
|
|
|
const stdinFile = Deno.openSync(stdinPath);
|
|
|
|
|
|
|
|
const context = new Context({
|
|
|
|
exitOnReturn: false,
|
|
|
|
stdin: stdinFile.rid,
|
|
|
|
});
|
|
|
|
|
|
|
|
const binary = Deno.readFileSync(path.join(testdir, "std_io_stdin.wasm"));
|
|
|
|
const module = new WebAssembly.Module(binary);
|
|
|
|
const instance = new WebAssembly.Instance(module, {
|
|
|
|
wasi_snapshot_preview1: context.exports,
|
|
|
|
});
|
|
|
|
|
|
|
|
context.start(instance);
|
|
|
|
|
|
|
|
stdinFile.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("std_io_stdout.wasm with stdout as file", function () {
|
|
|
|
const stdoutPath = Deno.makeTempFileSync();
|
|
|
|
const stdoutFile = Deno.openSync(stdoutPath, { create: true, write: true });
|
|
|
|
|
|
|
|
const context = new Context({
|
|
|
|
exitOnReturn: false,
|
|
|
|
stdout: stdoutFile.rid,
|
|
|
|
});
|
|
|
|
|
|
|
|
const binary = Deno.readFileSync(path.join(testdir, "std_io_stdout.wasm"));
|
|
|
|
const module = new WebAssembly.Module(binary);
|
|
|
|
const instance = new WebAssembly.Instance(module, {
|
|
|
|
wasi_snapshot_preview1: context.exports,
|
|
|
|
});
|
|
|
|
|
|
|
|
context.start(instance);
|
|
|
|
|
|
|
|
stdoutFile.close();
|
|
|
|
|
|
|
|
assertEquals(Deno.readTextFileSync(stdoutPath), "Hello, stdout!");
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("std_io_stderr.wasm with stderr as file", function () {
|
|
|
|
const stderrPath = Deno.makeTempFileSync();
|
|
|
|
const stderrFile = Deno.openSync(stderrPath, { create: true, write: true });
|
|
|
|
|
|
|
|
const context = new Context({
|
|
|
|
exitOnReturn: false,
|
|
|
|
stderr: stderrFile.rid,
|
|
|
|
});
|
|
|
|
|
|
|
|
const binary = Deno.readFileSync(path.join(testdir, "std_io_stderr.wasm"));
|
|
|
|
const module = new WebAssembly.Module(binary);
|
|
|
|
const instance = new WebAssembly.Instance(module, {
|
|
|
|
wasi_snapshot_preview1: context.exports,
|
|
|
|
});
|
|
|
|
|
|
|
|
context.start(instance);
|
|
|
|
|
|
|
|
stderrFile.close();
|
|
|
|
|
|
|
|
assertEquals(Deno.readTextFileSync(stderrPath), "Hello, stderr!");
|
|
|
|
});
|