Skip to content

Commit

Permalink
test: split wasi tests
Browse files Browse the repository at this point in the history
Move the child process code into a fixture and split the test
so that it can be run in parallel and it's easier to identify
where the failure is coming from. Also use the
spawnSyncAndExitWithoutError() utility so that the test shows
complete information on failure.

Instead of marking all the wasi tests as flaky, only mark the
wasi-poll one which is flaking in the CI now.

PR-URL: #51836
Refs: #51822
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
  • Loading branch information
joyeecheung authored and richardlau committed Mar 25, 2024
1 parent 3fe59ba commit dd708d3
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 127 deletions.
43 changes: 43 additions & 0 deletions test/common/wasi.js
@@ -0,0 +1,43 @@
// Test version set to preview1
'use strict';

const { spawnSyncAndExitWithoutError } = require('./child_process');
const fixtures = require('./fixtures');
const childPath = fixtures.path('wasi-preview-1.js');

function testWasiPreview1(args, spawnArgs = {}, expectations = {}) {
const newEnv = {
...process.env,
NODE_DEBUG_NATIVE: 'wasi',
NODE_PLATFORM: process.platform,
...spawnArgs.env,
};
spawnArgs.env = newEnv;

console.log('Testing with --turbo-fast-api-calls:', ...args);
spawnSyncAndExitWithoutError(
process.execPath, [
'--turbo-fast-api-calls',
childPath,
...args,
],
spawnArgs,
expectations,
);

console.log('Testing with --no-turbo-fast-api-calls:', ...args);
spawnSyncAndExitWithoutError(
process.execPath,
[
'--no-turbo-fast-api-calls',
childPath,
...args,
],
spawnArgs,
expectations,
);
}

module.exports = {
testWasiPreview1,
};
48 changes: 48 additions & 0 deletions test/fixtures/wasi-preview-1.js
@@ -0,0 +1,48 @@
'use strict';

const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const fs = require('fs');
const path = require('path');
const common = require('../common');
const { WASI } = require('wasi');

function returnOnExitEnvToValue(env) {
const envValue = env.RETURN_ON_EXIT;
if (envValue === undefined) {
return undefined;
}

return envValue === 'true';
}

common.expectWarning('ExperimentalWarning',
'WASI is an experimental feature and might change at any time');

tmpdir.refresh();
const wasmDir = path.join(__dirname, '..', 'wasi', 'wasm');
const wasiPreview1 = new WASI({
version: 'preview1',
args: ['foo', '-bar', '--baz=value'],
env: process.env,
preopens: {
'/sandbox': fixtures.path('wasi'),
'/tmp': tmpdir.path,
},
returnOnExit: returnOnExitEnvToValue(process.env),
});

// Validate the getImportObject helper
assert.strictEqual(wasiPreview1.wasiImport,
wasiPreview1.getImportObject().wasi_snapshot_preview1);
const modulePathPreview1 = path.join(wasmDir, `${process.argv[2]}.wasm`);
const bufferPreview1 = fs.readFileSync(modulePathPreview1);

(async () => {
const { instance: instancePreview1 } =
await WebAssembly.instantiate(bufferPreview1,
wasiPreview1.getImportObject());

wasiPreview1.start(instancePreview1);
})().then(common.mustCall());
7 changes: 7 additions & 0 deletions test/wasi/test-wasi-exitcode.js
@@ -0,0 +1,7 @@
'use strict';
require('../common');
const { testWasiPreview1 } = require('../common/wasi');

testWasiPreview1(['exitcode']);
testWasiPreview1(['exitcode'], { env: { RETURN_ON_EXIT: true } });
testWasiPreview1(['exitcode'], { env: { RETURN_ON_EXIT: false } }, { status: 120 });
14 changes: 14 additions & 0 deletions test/wasi/test-wasi-io.js
@@ -0,0 +1,14 @@
'use strict';
const common = require('../common');
const { checkoutEOL } = common;
const { testWasiPreview1 } = require('../common/wasi');

testWasiPreview1(['freopen'], {}, { stdout: `hello from input2.txt${checkoutEOL}` });
testWasiPreview1(['read_file'], {}, { stdout: `hello from input.txt${checkoutEOL}` });
testWasiPreview1(['read_file_twice'], {}, {
stdout: `hello from input.txt${checkoutEOL}hello from input.txt${checkoutEOL}`,
});
// Tests that are currently unsupported on Windows.
if (!common.isWindows) {
testWasiPreview1(['stdin'], { input: 'hello world' }, { stdout: 'hello world' });
}
5 changes: 5 additions & 0 deletions test/wasi/test-wasi-poll.js
@@ -0,0 +1,5 @@
'use strict';
require('../common');
const { testWasiPreview1 } = require('../common/wasi');

testWasiPreview1(['poll']);
149 changes: 23 additions & 126 deletions test/wasi/test-wasi.js
@@ -1,131 +1,28 @@
'use strict';
const common = require('../common');

function returnOnExitEnvToValue(env) {
const envValue = env.RETURN_ON_EXIT;
if (envValue === undefined) {
return undefined;
}

return envValue === 'true';
const { testWasiPreview1 } = require('../common/wasi');

// TODO(joyeecheung): tests that don't need special configurations can be ported
// to a special python test case configuration and get run in parallel.
// Tests that are currently unsupported on IBM i PASE.
if (!common.isIBMi) {
testWasiPreview1(['clock_getres']);
testWasiPreview1(['getrusage']);
}

if (process.argv[2] === 'wasi-child-preview1') {
// Test version set to preview1
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const fs = require('fs');
const path = require('path');

common.expectWarning('ExperimentalWarning',
'WASI is an experimental feature and might change at any time');

const { WASI } = require('wasi');
tmpdir.refresh();
const wasmDir = path.join(__dirname, 'wasm');
const wasiPreview1 = new WASI({
version: 'preview1',
args: ['foo', '-bar', '--baz=value'],
env: process.env,
preopens: {
'/sandbox': fixtures.path('wasi'),
'/tmp': tmpdir.path,
},
returnOnExit: returnOnExitEnvToValue(process.env),
});

// Validate the getImportObject helper
assert.strictEqual(wasiPreview1.wasiImport,
wasiPreview1.getImportObject().wasi_snapshot_preview1);
const modulePathPreview1 = path.join(wasmDir, `${process.argv[3]}.wasm`);
const bufferPreview1 = fs.readFileSync(modulePathPreview1);

(async () => {
const { instance: instancePreview1 } =
await WebAssembly.instantiate(bufferPreview1,
wasiPreview1.getImportObject());

wasiPreview1.start(instancePreview1);
})().then(common.mustCall());
} else {
const assert = require('assert');
const cp = require('child_process');
const { checkoutEOL } = common;

function innerRunWASI(options, args, flavor = 'preview1') {
console.log('executing', options.test);
const opts = {
env: {
...process.env,
NODE_DEBUG_NATIVE: 'wasi',
NODE_PLATFORM: process.platform,
},
};

if (options.stdin !== undefined)
opts.input = options.stdin;

if ('returnOnExit' in options) {
opts.env.RETURN_ON_EXIT = options.returnOnExit;
}

const child = cp.spawnSync(process.execPath, [
...args,
__filename,
'wasi-child-' + flavor,
options.test,
], opts);
console.log(child.stderr.toString());
assert.strictEqual(child.status, options.exitCode || 0);
assert.strictEqual(child.signal, null);
assert.strictEqual(child.stdout.toString(), options.stdout || '');
}

function runWASI(options) {
innerRunWASI(options, ['--no-turbo-fast-api-calls']);
innerRunWASI(options, ['--turbo-fast-api-calls']);
}

runWASI({ test: 'cant_dotdot' });

// Tests that are currently unsupported on IBM i PASE.
if (!common.isIBMi) {
runWASI({ test: 'clock_getres' });
}
runWASI({ test: 'exitcode' });
runWASI({ test: 'exitcode', returnOnExit: true });
runWASI({ test: 'exitcode', exitCode: 120, returnOnExit: false });
runWASI({ test: 'fd_prestat_get_refresh' });
runWASI({ test: 'freopen', stdout: `hello from input2.txt${checkoutEOL}` });
runWASI({ test: 'ftruncate' });
runWASI({ test: 'getentropy' });

// Tests that are currently unsupported on IBM i PASE.
if (!common.isIBMi) {
runWASI({ test: 'getrusage' });
}
runWASI({ test: 'gettimeofday' });
runWASI({ test: 'main_args' });
runWASI({ test: 'notdir' });
runWASI({ test: 'poll' });
runWASI({ test: 'preopen_populates' });

if (!common.isWindows && process.platform !== 'android') {
runWASI({ test: 'readdir' });
}

runWASI({ test: 'read_file', stdout: `hello from input.txt${checkoutEOL}` });
runWASI({
test: 'read_file_twice',
stdout: `hello from input.txt${checkoutEOL}hello from input.txt${checkoutEOL}`,
});
runWASI({ test: 'stat' });
runWASI({ test: 'sock' });
runWASI({ test: 'write_file' });

// Tests that are currently unsupported on Windows.
if (!common.isWindows) {
runWASI({ test: 'stdin', stdin: 'hello world', stdout: 'hello world' });
}
// Tests that are currently unsupported on Windows and Android.
if (!common.isWindows && process.platform !== 'android') {
testWasiPreview1(['readdir']);
}

testWasiPreview1(['cant_dotdot']);
testWasiPreview1(['fd_prestat_get_refresh']);
testWasiPreview1(['ftruncate']);
testWasiPreview1(['getentropy']);
testWasiPreview1(['gettimeofday']);
testWasiPreview1(['main_args']);
testWasiPreview1(['notdir']);
testWasiPreview1(['preopen_populates']);
testWasiPreview1(['stat']);
testWasiPreview1(['sock']);
testWasiPreview1(['write_file']);
2 changes: 1 addition & 1 deletion test/wasi/wasi.status
Expand Up @@ -9,4 +9,4 @@ prefix wasi
# Windows on ARM
[$system==win32 && $arch==arm64]
# https://github.com/nodejs/node/issues/51822
test-wasi: PASS, FLAKY
test-wasi-poll: PASS, FLAKY

0 comments on commit dd708d3

Please sign in to comment.