Skip to content

Commit

Permalink
test: add node compat test cases (#27134)
Browse files Browse the repository at this point in the history
This PR enables node compat test cases found passing by using the tool
added in #27122

The percentage of passing test case increases from 16.16% to 30.43% by
this change.
  • Loading branch information
kt3k authored and bartlomieju committed Dec 5, 2024
1 parent feba98a commit e05a305
Show file tree
Hide file tree
Showing 527 changed files with 30,399 additions and 530 deletions.
540 changes: 536 additions & 4 deletions tests/node_compat/config.jsonc

Large diffs are not rendered by default.

527 changes: 1 addition & 526 deletions tests/node_compat/runner/TODO.md

Large diffs are not rendered by default.

143 changes: 143 additions & 0 deletions tests/node_compat/test/abort/test-addon-uv-handle-leak.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const { spawnSync } = require('child_process');

// This is a sibling test to test/addons/uv-handle-leak.

const bindingPath = path.resolve(
__dirname, '..', 'addons', 'uv-handle-leak', 'build',
`${common.buildType}/binding.node`);

if (!fs.existsSync(bindingPath))
common.skip('binding not built yet');

if (process.argv[2] === 'child') {

const { Worker } = require('worker_threads');

// The worker thread loads and then unloads `bindingPath`. Because of this the
// symbols in `bindingPath` are lost when the worker thread quits, but the
// number of open handles in the worker thread's event loop is assessed in the
// main thread afterwards, and the names of the callbacks associated with the
// open handles is retrieved at that time as well. Thus, we require
// `bindingPath` here so that the symbols and their names survive the life
// cycle of the worker thread.
require(bindingPath);

new Worker(`
const binding = require(${JSON.stringify(bindingPath)});
binding.leakHandle();
binding.leakHandle(0);
binding.leakHandle(0x42);
`, { eval: true });
} else {
const child = cp.spawnSync(process.execPath, [__filename, 'child']);
const stderr = child.stderr.toString();

assert.strictEqual(child.stdout.toString(), '');

const lines = stderr.split('\n');

let state = 'initial';

// Parse output that is formatted like this:

// uv loop at [0x559b65ed5770] has open handles:
// [0x7f2de0018430] timer (active)
// Close callback: 0x7f2df31de220 CloseCallback(uv_handle_s*) [...]
// Data: 0x7f2df33df140 example_instance [...]
// (First field): 0x7f2df33dedc0 vtable for ExampleOwnerClass [...]
// [0x7f2de000b870] timer
// Close callback: 0x7f2df31de220 CloseCallback(uv_handle_s*) [...]
// Data: (nil)
// [0x7f2de000b910] timer
// Close callback: 0x7f2df31de220 CloseCallback(uv_handle_s*) [...]
// Data: 0x42
// uv loop at [0x559b65ed5770] has 3 open handles in total

function isGlibc() {
try {
const lddOut = spawnSync('ldd', [process.execPath]).stdout;
const libcInfo = lddOut.toString().split('\n').map(
(line) => line.match(/libc\.so.+=>\s*(\S+)\s/)).filter((info) => info);
if (libcInfo.length === 0)
return false;
const nmOut = spawnSync('nm', ['-D', libcInfo[0][1]]).stdout;
if (/gnu_get_libc_version/.test(nmOut))
return true;
} catch {
return false;
}
}


if (!(common.isFreeBSD ||
common.isAIX ||
common.isIBMi ||
(common.isLinux && !isGlibc()) ||
common.isWindows)) {
assert(stderr.includes('ExampleOwnerClass'), stderr);
assert(stderr.includes('CloseCallback'), stderr);
assert(stderr.includes('example_instance'), stderr);
}

while (lines.length > 0) {
const line = lines.shift().trim();
if (line.length === 0) {
continue; // Skip empty lines.
}

switch (state) {
case 'initial':
assert.match(line, /^uv loop at \[.+\] has open handles:$/);
state = 'handle-start';
break;
case 'handle-start':
if (/^uv loop at \[.+\] has \d+ open handles in total$/.test(line)) {
state = 'source-line';
break;
}
assert.match(line, /^\[.+\] timer( \(active\))?$/);
state = 'close-callback';
break;
case 'close-callback':
assert.match(line, /^Close callback:/);
state = 'data';
break;
case 'data':
assert.match(line, /^Data: .+$/);
state = 'maybe-first-field';
break;
case 'maybe-first-field':
if (!/^\(First field\)/.test(line)) {
lines.unshift(line);
}
state = 'handle-start';
break;
case 'source-line':
assert.match(line, /CheckedUvLoopClose/);
state = 'assertion-failure';
break;
case 'assertion-failure':
assert.match(line, /Assertion failed:/);
state = 'done';
break;
case 'done':
break;
}
}

assert.strictEqual(state, 'done');
}
20 changes: 20 additions & 0 deletions tests/node_compat/test/benchmark/test-benchmark-async-hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';

const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

if (!common.enoughTestMem)
common.skip('Insufficient memory for async_hooks benchmark test');

const runBenchmark = require('../common/benchmark');

runBenchmark('async_hooks');
21 changes: 21 additions & 0 deletions tests/node_compat/test/benchmark/test-benchmark-http.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';

const common = require('../common');

if (!common.enoughTestMem)
common.skip('Insufficient memory for HTTP benchmark test');

// Because the http benchmarks use hardcoded ports, this should be in sequential
// rather than parallel to make sure it does not conflict with tests that choose
// random available ports.

const runBenchmark = require('../common/benchmark');

runBenchmark('http', { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 });
23 changes: 23 additions & 0 deletions tests/node_compat/test/benchmark/test-benchmark-http2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');

if (!common.enoughTestMem)
common.skip('Insufficient memory for HTTP/2 benchmark test');

// Because the http benchmarks use hardcoded ports, this should be in sequential
// rather than parallel to make sure it does not conflict with tests that choose
// random available ports.

const runBenchmark = require('../common/benchmark');

runBenchmark('http2', { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 });
24 changes: 24 additions & 0 deletions tests/node_compat/test/benchmark/test-benchmark-tls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';

const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

if (!common.enoughTestMem)
common.skip('Insufficient memory for TLS benchmark test');

// Because the TLS benchmarks use hardcoded ports, this should be in sequential
// rather than parallel to make sure it does not conflict with tests that choose
// random available ports.

const runBenchmark = require('../common/benchmark');

runBenchmark('tls', { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 });
21 changes: 21 additions & 0 deletions tests/node_compat/test/benchmark/test-benchmark-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';

const common = require('../common');

if (!common.enoughTestMem)
common.skip('Insufficient memory for Worker benchmark test');

// Because the worker benchmarks can run on different threads,
// this should be in sequential rather than parallel to make sure
// it does not conflict with tests that choose random available ports.

const runBenchmark = require('../common/benchmark');

runBenchmark('worker', { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 });
19 changes: 19 additions & 0 deletions tests/node_compat/test/es-module/test-cjs-prototype-pollution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';

const { mustNotCall, mustCall } = require('../common');

Object.defineProperties(Object.prototype, {
then: {
set: mustNotCall('set %Object.prototype%.then'),
get: mustNotCall('get %Object.prototype%.then'),
},
});

import('data:text/javascript,').then(mustCall());
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';
const common = require('../common');
const tmpdir = require('../common/tmpdir');

const assert = require('node:assert');
const fs = require('node:fs/promises');

tmpdir.refresh();
const target = tmpdir.fileURL(`${Math.random()}.mjs`);

(async () => {

await assert.rejects(import(target), { code: 'ERR_MODULE_NOT_FOUND' });

await fs.writeFile(target, 'export default "actual target"\n');

const moduleRecord = await import(target);

await fs.rm(target);

assert.strictEqual(await import(target), moduleRecord);
})().then(common.mustCall());
17 changes: 17 additions & 0 deletions tests/node_compat/test/es-module/test-esm-loader-cache-clearing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';
require('../common');

const { cache } = require;

Object.keys(cache).forEach((key) => {
delete cache[key];
});
// Require the same module again triggers the crash
require('../common');
54 changes: 54 additions & 0 deletions tests/node_compat/test/es-module/test-esm-windows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 20.11.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.

'use strict';

// This test ensures that JavaScript file that includes
// a reserved Windows word can be loaded as ESM module

const common = require('../common');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const fs = require('fs').promises;
const path = require('path');

const imp = (file) => {
return import(path.relative(__dirname, file).replace(/\\/g, '/'));
};

(async () => {
tmpdir.refresh();
const rel = (file) => tmpdir.resolve(file);

{ // Load a single script
const file = rel('con.mjs');
await fs.writeFile(file, 'export default "ok"');
assert.strictEqual((await imp(file)).default, 'ok');
await fs.unlink(file);
}

{ // Load a module
const entry = rel('entry.mjs');
const nmDir = rel('node_modules');
const mDir = rel('node_modules/con');
const pkg = rel('node_modules/con/package.json');
const script = rel('node_modules/con/index.mjs');

await fs.writeFile(entry, 'export {default} from "con"');
await fs.mkdir(nmDir);
await fs.mkdir(mDir);
await fs.writeFile(pkg, '{"main":"index.mjs"}');
await fs.writeFile(script, 'export default "ok"');

assert.strictEqual((await imp(entry)).default, 'ok');
await fs.unlink(script);
await fs.unlink(pkg);
await fs.rmdir(mDir);
await fs.rmdir(nmDir);
await fs.unlink(entry);
}
})().then(common.mustCall());
Loading

0 comments on commit e05a305

Please sign in to comment.