blob: 8c31f9a1ddda45acf60efcea1ca0abbc040e274b [file] [log] [blame]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for initial argument passing.
*/
import {Process, SyscallEntry, SyscallHandler, util, WASI} from '../index.js';
describe('argv.js', () => {
/**
* A handler just to capture output.
*/
class TestSyscallHandler extends SyscallHandler.DirectWasiPreview1 {
constructor(...args) {
super(...args);
this.stdout = '';
this.stderr = '';
this.td = new TextDecoder();
}
/** @override */
handle_fd_write(fd, buf) {
switch (fd) {
case 1:
this.stdout += this.td.decode(buf, {stream: true});
return WASI.errno.ESUCCESS;
case 2:
this.stderr += this.td.decode(buf, {stream: true});
return WASI.errno.ESUCCESS;
}
return WASI.errno.EINVAL;
}
}
/**
* The format the WASM program outputs.
*
* @typedef {{
* argc: number,
* argv: !Array<string>,
* mem: !Object,
* }}
*/
const TestData = {};
/**
* Helper function to run the wasm module & return output.
*
* @param {!ArrayBuffer} prog The program to run.
* @param {!Array<string>=} argv The program arguments.
* @return {!Object} The program results.
*/
async function run(prog, argv) {
const handler = new TestSyscallHandler();
const sys_handlers = [handler];
const proc = new Process.Foreground({
executable: prog,
argv: argv,
sys_handlers: sys_handlers,
sys_entries: [
new SyscallEntry.WasiPreview1({sys_handlers}),
],
});
const ret = await proc.run();
assert.equal(handler.stderr, '');
return {
returncode: ret,
stdout: handler.stdout,
stderr: handler.stderr,
data: /** @type {!TestData} */ (JSON.parse(handler.stdout)),
};
}
/**
* Load some common state that all tests in here want.
*/
before(async function() {
/**
* Fetch & read the body once to speed up the tests.
*
* @type {!ArrayBuffer}
*/
this.prog = await fetch('argv.wasm')
.then((response) => response.arrayBuffer());
});
/**
* Check default Program argv behavior.
*/
it('no args', async function() {
const result = await run(this.prog);
const data = result.data;
assert.equal(data.argc, 1);
assert.deepEqual(data.argv, ['wasi-program']);
});
/**
* Check argv=[] behavior.
*/
it('empty args', async function() {
const result = await run(this.prog, []);
const data = result.data;
assert.equal(data.argc, 0);
assert.deepEqual(data.argv, []);
});
/**
* Check argv=[prog] behavior.
*/
it('argv0 only', async function() {
const result = await run(this.prog, ['my-prog']);
const data = result.data;
assert.equal(data.argc, 1);
assert.deepEqual(data.argv, ['my-prog']);
});
/**
* Check multiple arguments behavior.
*/
it('couple args', async function() {
const result = await run(this.prog, ['my-prog', 'foo', 'bar']);
const data = result.data;
assert.equal(data.argc, 3);
assert.deepEqual(data.argv, ['my-prog', 'foo', 'bar']);
});
/**
* Check arguments with whitespace behavior.
*/
it('whitespace args', async function() {
const result = await run(this.prog, ['p', 'spa ce', 'ta\tb', 'new\nline']);
const data = result.data;
assert.equal(data.argc, 4);
assert.deepEqual(data.argv, ['p', 'spa ce', 'ta\tb', 'new\nline']);
});
/**
* Check UTF-8 encoded arguments behavior.
*/
it('utf8 args', async function() {
const result = await run(this.prog, ['my-prog', 'das', 'ist', 'heiß']);
const data = result.data;
assert.equal(data.argc, 4);
assert.deepEqual(data.argv, ['my-prog', 'das', 'ist', 'heiß']);
});
/**
* Verify we abort early with non-array argv's.
*/
it('not array', async function() {
try {
/** @suppress {checkTypes} We call run() incorrectly on purpose. */
const run_ = async () => { await run(this.prog, {}); };
await run_();
} catch (e) {
// assert.throws doesn't work with promises.
assert.instanceOf(e, util.ApiViolation);
}
});
/**
* Verify we abort early with non-string args.
*/
it('not string', async function() {
try {
await run(this.prog, ['ok', 123]);
} catch (e) {
// assert.throws doesn't work with promises.
assert.instanceOf(e, util.ApiViolation);
}
});
});