Skip to content

feat: port 15 easy js-native-api tests to CTS#25

Open
kraenhansen wants to merge 8 commits intonodejs:mainfrom
kraenhansen:feat/easy-js-native-api-tests
Open

feat: port 15 easy js-native-api tests to CTS#25
kraenhansen wants to merge 8 commits intonodejs:mainfrom
kraenhansen:feat/easy-js-native-api-tests

Conversation

@kraenhansen
Copy link
Contributor

@kraenhansen kraenhansen commented Feb 28, 2026

Summary

Ports 15 "Easy" difficulty js-native-api tests to the CTS.

Ported tests

3_callbacks, 4_object_factory, 5_function_factory, 7_factory_wrap, 8_passing_wrapped, test_array, test_bigint, test_date, test_handle_scope, test_new_target, test_number, test_promise, test_properties, test_reference_double_free, test_symbol

C/C++ sources copied from Node.js with minimal changes (entry_point.h macro). JS tests adapted to use CTS globals (loadAddon, assert, gcUntil, mustCall, mustNotCall) instead of Node.js test harness (common.js, require()).

Coverage verification

All C/C++ files are byte-for-byte identical to upstream. JS test files were compared assertion-by-assertion:

Test C/C++ JS Verdict
3_callbacks Identical Identical (common.mustCallmustCall) Identical coverage
4_object_factory Identical Identical Identical coverage
5_function_factory Identical Identical Identical coverage
7_factory_wrap Identical (3 files) Identical (top-level await) Identical coverage
8_passing_wrapped Identical (3 files) Identical (flattened to top-level await) Identical coverage
test_array Identical Identical Identical coverage
test_bigint Identical Identical Identical coverage
test_date Identical Identical Identical coverage
test_handle_scope Identical Identical Identical coverage
test_new_target Identical Identical Identical coverage
test_number Identical (3 files) Identical (minor assert arg order swap) Identical coverage
test_promise Identical Identical (common.mustCall/common.mustNotCallmustCall/mustNotCall) Identical coverage
test_properties Identical Identical (some assert.ok -> assert) Identical coverage
test_reference_double_free Identical Identical (2 JS files) Identical coverage
test_symbol Identical Identical (3 JS files) Identical coverage

New harness helper: mustCall / mustNotCall

Node.js tests use common.mustCall(fn) to wrap callbacks and assert they are called. The CTS provides equivalent globals that preserve the same call pattern:

  • mustCall(fn) — wraps fn and asserts it is called exactly once before process exit. Returns the wrapper function directly, so the call pattern is identical to Node.js's common.mustCall().
  • mustNotCall(msg) — returns a function that throws immediately if called.

Example:

// Node.js:
promise.then(common.mustCall((result) => { assert.strictEqual(result, 42); }));

// CTS (same pattern, just drop `common.`):
promise.then(mustCall((result) => { assert.strictEqual(result, 42); }));

Note: The Node.js implementor uses process.on("exit") to verify call counts, matching Node.js's own common.mustCall implementation. Other runtimes can implement this verification using their own lifecycle hooks.

Notable adaptations

  • test_dataview: Reclassified from Easy to Medium — depends on experimental SharedArrayBuffer APIs (node_api_is_sharedarraybuffer) not available in node-api-headers. Tracked in Support experimental Node-API features in the CTS #26.
  • test_instance_data: Reclassified from Easy to Medium — the upstream test verifies finalizer and environment teardown behavior via subprocesses and worker threads, which can't be faithfully ported without harness support.
  • test_promise: Uses mustCall() / mustNotCall() to assert callbacks are invoked, replacing common.mustCall().
  • 7_factory_wrap / 8_passing_wrapped: Use await gcUntil(...) for finalizer testing.

Other changes

  • Updates node-api-headers from 1.7.0 to 1.8.0
  • Updates PORTING.md with ported status and new "Experimental Node-API Features" section

Test plan

  • npm run addons:configure && npm run addons:build — all 16 addons compile
  • npm run node:test — all 45 tests pass (4 harness + 41 test files)

🤖 Generated with Claude Code

@kraenhansen kraenhansen marked this pull request as draft February 28, 2026 12:47
@kraenhansen kraenhansen changed the title feat: extend harness with assert methods, gcUntil, and --expose-gc feat: port 17 easy js-native-api tests to CTS Feb 28, 2026
@kraenhansen kraenhansen force-pushed the feat/easy-js-native-api-tests branch from 9188967 to ad36fb5 Compare March 5, 2026 16:29
kraenhansen and others added 3 commits March 5, 2026 18:51
Port all "Easy" difficulty engine-specific tests from Node.js:
3_callbacks, 4_object_factory, 5_function_factory, 7_factory_wrap,
8_passing_wrapped, test_array, test_bigint, test_dataview, test_date,
test_handle_scope, test_instance_data, test_new_target, test_number,
test_promise, test_properties, test_reference_double_free, test_symbol.

C/C++ sources are copied from Node.js with minimal changes (entry_point.h
macro). JS tests are adapted to use CTS globals (loadAddon, assert,
gcUntil) instead of Node.js test harness (common.js, require).

test_dataview: removed SharedArrayBuffer tests that depend on experimental
node_api_is_sharedarraybuffer API (not in stable node-api-headers).

Also updates node-api-headers from 1.7.0 to 1.8.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mark 17 easy js-native-api tests as Ported (test_dataview as Partial due
to missing experimental SharedArrayBuffer APIs in node-api-headers).

Add "Experimental Node-API Features" section documenting which tests
depend on experimental APIs not yet available in node-api-headers, and
the approach needed to support them. References nodejs#26.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kraenhansen kraenhansen force-pushed the feat/easy-js-native-api-tests branch from ad36fb5 to d8c7229 Compare March 5, 2026 17:52
@kraenhansen kraenhansen changed the title feat: port 17 easy js-native-api tests to CTS feat: port 16 easy js-native-api tests to CTS Mar 7, 2026
@kraenhansen kraenhansen marked this pull request as ready for review March 7, 2026 15:13
The upstream test verifies finalizer and environment teardown behavior
via subprocesses and worker threads, not just increment(). The simplified
CTS port lost that coverage, so remove it and reclassify as Medium.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kraenhansen kraenhansen changed the title feat: port 16 easy js-native-api tests to CTS feat: port 15 easy js-native-api tests to CTS Mar 7, 2026
@kraenhansen kraenhansen moved this from Need Triage to Has PR in Node-API Team Project Mar 7, 2026
@KevinEady
Copy link

mustCall(fn) — returns a [wrapper, called] tuple where called is a Promise that resolves once wrapper is invoked. This avoids runtime-specific APIs like setTimeout or process.on('exit').

What happens if the function is never called? Would the awaiting on the promise hang indefinitely?

Use a regular function wrapper with fn.apply(this, args) to preserve
receiver binding, and verify call counts on process exit instead of
returning a [wrapper, called] Promise tuple.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kraenhansen
Copy link
Contributor Author

kraenhansen commented Mar 8, 2026

Would the awaiting on the promise hang indefinitely?

@KevinEady Yes - and I think that's not great. We could extend it to fail on a timeout.

I choose this because I expected it to be easier to implement for the other implementors, but thinking more about it, I think this optimization is pre-mature and we should preserve the "fail on exit if function wasn't called" semantics for now. I've pushed 36dfc98 to restore that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants