Skip to content

Commit 224918d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feat/port-test-reference
2 parents 5a60dfd + 4188361 commit 224918d

6 files changed

Lines changed: 286 additions & 20 deletions

File tree

PORTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
5757
| `test_error` | Ported ✅ | Medium |
5858
| `test_exception` | Not ported | Medium |
5959
| `test_finalizer` | Not ported | Medium |
60-
| `test_function` | Not ported | Medium |
60+
| `test_function` | Ported ✅ | Medium |
6161
| `test_general` | Not ported | Hard |
6262
| `test_handle_scope` | Ported ✅ | Easy |
6363
| `test_instance_data` | Not ported | Medium |

implementors/node/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Harness of Node.js
2+
3+
## Build addons
4+
5+
To build the addons, run the following command:
6+
7+
```bash
8+
$ npm run addons:configure
9+
$ npm run addons:build
10+
```
11+
12+
## Running tests
13+
14+
Run the following command to run the tests:
15+
16+
```bash
17+
$ npm run node:test
18+
```
19+
20+
To run a specific test file, use the `--test-name-pattern` flag:
21+
22+
```bash
23+
$ NODE_OPTIONS=--test-name-pattern=js-native-api/test_constructor/test_null npm run node:test
24+
```
25+
26+
The test names are their relative path to the `tests` folder, with file extensions.
27+
The pattern can be a regular expression.

implementors/node/run-tests.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,25 @@
11
import path from "node:path";
2-
import { test, type TestContext } from "node:test";
2+
import { test } from "node:test";
33

44
import { listDirectoryEntries, runFileInSubprocess } from "./tests.ts";
55

66
const ROOT_PATH = path.resolve(import.meta.dirname, "..", "..");
77
const TESTS_ROOT_PATH = path.join(ROOT_PATH, "tests");
88

9-
async function populateSuite(
10-
testContext: TestContext,
9+
function populateSuite(
1110
dir: string
12-
): Promise<void> {
11+
) {
1312
const { directories, files } = listDirectoryEntries(dir);
1413

1514
for (const file of files) {
16-
await testContext.test(file, () => runFileInSubprocess(dir, file));
15+
test(path.relative(TESTS_ROOT_PATH, path.join(dir, file)), () => runFileInSubprocess(dir, file));
1716
}
1817

1918
for (const directory of directories) {
20-
await testContext.test(directory, async (subTest) => {
21-
await populateSuite(subTest, path.join(dir, directory));
22-
});
19+
populateSuite(path.join(dir, directory));
2320
}
2421
}
2522

26-
test("harness", async (t) => {
27-
await populateSuite(t, path.join(TESTS_ROOT_PATH, "harness"));
28-
});
29-
30-
test("js-native-api", async (t) => {
31-
await populateSuite(t, path.join(TESTS_ROOT_PATH, "js-native-api"));
32-
});
33-
34-
test("node-api", async (t) => {
35-
await populateSuite(t, path.join(TESTS_ROOT_PATH, "node-api"));
36-
});
23+
populateSuite(path.join(TESTS_ROOT_PATH, "harness"));
24+
populateSuite(path.join(TESTS_ROOT_PATH, "js-native-api"));
25+
populateSuite(path.join(TESTS_ROOT_PATH, "node-api"));
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_node_api_cts_addon(test_function test_function.c)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"use strict";
2+
// Flags: --expose-gc
3+
4+
// Testing api calls for function
5+
const test_function = loadAddon("test_function");
6+
7+
function func1() {
8+
return 1;
9+
}
10+
assert.strictEqual(test_function.TestCall(func1), 1);
11+
12+
function func2() {
13+
return null;
14+
}
15+
assert.strictEqual(test_function.TestCall(func2), null);
16+
17+
function func3(input) {
18+
return input + 1;
19+
}
20+
assert.strictEqual(test_function.TestCall(func3, 1), 2);
21+
22+
function func4(input) {
23+
return func3(input);
24+
}
25+
assert.strictEqual(test_function.TestCall(func4, 1), 2);
26+
27+
assert.strictEqual(test_function.TestName.name, "Name");
28+
assert.strictEqual(test_function.TestNameShort.name, "Name_");
29+
30+
let tracked_function = test_function.MakeTrackedFunction(mustCall());
31+
assert(!!tracked_function);
32+
tracked_function = null;
33+
gc();
34+
35+
assert.deepStrictEqual(test_function.TestCreateFunctionParameters(), {
36+
envIsNull: "Invalid argument",
37+
nameIsNull: "napi_ok",
38+
cbIsNull: "Invalid argument",
39+
resultIsNull: "Invalid argument",
40+
});
41+
42+
assert.throws(() => test_function.TestBadReturnExceptionPending(), {
43+
code: "throwing exception",
44+
name: "Error",
45+
});
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#include <js_native_api.h>
2+
#include "../common.h"
3+
#include "../entry_point.h"
4+
5+
static napi_value TestCreateFunctionParameters(napi_env env,
6+
napi_callback_info info) {
7+
napi_status status;
8+
napi_value result, return_value;
9+
10+
NODE_API_CALL(env, napi_create_object(env, &return_value));
11+
12+
status = napi_create_function(NULL,
13+
"TrackedFunction",
14+
NAPI_AUTO_LENGTH,
15+
TestCreateFunctionParameters,
16+
NULL,
17+
&result);
18+
19+
add_returned_status(env,
20+
"envIsNull",
21+
return_value,
22+
"Invalid argument",
23+
napi_invalid_arg,
24+
status);
25+
26+
napi_create_function(env,
27+
NULL,
28+
NAPI_AUTO_LENGTH,
29+
TestCreateFunctionParameters,
30+
NULL,
31+
&result);
32+
33+
add_last_status(env, "nameIsNull", return_value);
34+
35+
napi_create_function(env,
36+
"TrackedFunction",
37+
NAPI_AUTO_LENGTH,
38+
NULL,
39+
NULL,
40+
&result);
41+
42+
add_last_status(env, "cbIsNull", return_value);
43+
44+
napi_create_function(env,
45+
"TrackedFunction",
46+
NAPI_AUTO_LENGTH,
47+
TestCreateFunctionParameters,
48+
NULL,
49+
NULL);
50+
51+
add_last_status(env, "resultIsNull", return_value);
52+
53+
return return_value;
54+
}
55+
56+
static napi_value TestCallFunction(napi_env env, napi_callback_info info) {
57+
size_t argc = 10;
58+
napi_value args[10];
59+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
60+
61+
NODE_API_ASSERT(env, argc > 0, "Wrong number of arguments");
62+
63+
napi_valuetype valuetype0;
64+
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
65+
66+
NODE_API_ASSERT(env, valuetype0 == napi_function,
67+
"Wrong type of arguments. Expects a function as first argument.");
68+
69+
napi_value* argv = args + 1;
70+
argc = argc - 1;
71+
72+
napi_value global;
73+
NODE_API_CALL(env, napi_get_global(env, &global));
74+
75+
napi_value result;
76+
NODE_API_CALL(env, napi_call_function(env, global, args[0], argc, argv, &result));
77+
78+
return result;
79+
}
80+
81+
static napi_value TestFunctionName(napi_env env, napi_callback_info info) {
82+
return NULL;
83+
}
84+
85+
static void finalize_function(napi_env env, void* data, void* hint) {
86+
napi_ref ref = data;
87+
88+
// Retrieve the JavaScript undefined value.
89+
napi_value undefined;
90+
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
91+
92+
// Retrieve the JavaScript function we must call.
93+
napi_value js_function;
94+
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, ref, &js_function));
95+
96+
// Call the JavaScript function to indicate that the generated JavaScript
97+
// function is about to be gc-ed.
98+
NODE_API_CALL_RETURN_VOID(env,
99+
napi_call_function(env, undefined, js_function, 0, NULL, NULL));
100+
101+
// Destroy the persistent reference to the function we just called so as to
102+
// properly clean up.
103+
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, ref));
104+
}
105+
106+
static napi_value MakeTrackedFunction(napi_env env, napi_callback_info info) {
107+
size_t argc = 1;
108+
napi_value js_finalize_cb;
109+
napi_valuetype arg_type;
110+
111+
// Retrieve and validate from the arguments the function we will use to
112+
// indicate to JavaScript that the function we are about to create is about to
113+
// be gc-ed.
114+
NODE_API_CALL(env,
115+
napi_get_cb_info(env, info, &argc, &js_finalize_cb, NULL, NULL));
116+
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
117+
NODE_API_CALL(env, napi_typeof(env, js_finalize_cb, &arg_type));
118+
NODE_API_ASSERT(env, arg_type == napi_function, "Argument must be a function");
119+
120+
// Dynamically create a function.
121+
napi_value result;
122+
NODE_API_CALL(env,
123+
napi_create_function(
124+
env, "TrackedFunction", NAPI_AUTO_LENGTH, TestFunctionName, NULL,
125+
&result));
126+
127+
// Create a strong reference to the function we will call when the tracked
128+
// function is about to be gc-ed.
129+
napi_ref js_finalize_cb_ref;
130+
NODE_API_CALL(env,
131+
napi_create_reference(env, js_finalize_cb, 1, &js_finalize_cb_ref));
132+
133+
// Attach a finalizer to the dynamically created function and pass it the
134+
// strong reference we created in the previous step.
135+
NODE_API_CALL(env,
136+
napi_wrap(
137+
env, result, js_finalize_cb_ref, finalize_function, NULL, NULL));
138+
139+
return result;
140+
}
141+
142+
static napi_value TestBadReturnExceptionPending(napi_env env, napi_callback_info info) {
143+
napi_throw_error(env, "throwing exception", "throwing exception");
144+
145+
// addons should only ever return a valid napi_value even if an
146+
// exception occurs, but we have seen that the C++ wrapper
147+
// with exceptions enabled sometimes returns an invalid value
148+
// when an exception is thrown. Test that we ignore the return
149+
// value then an exception is pending. We use 0xFFFFFFFF as a value
150+
// that should never be a valid napi_value and node seems to
151+
// crash if it is not ignored indicating that it is indeed invalid.
152+
return (napi_value)(0xFFFFFFFFF);
153+
}
154+
155+
EXTERN_C_START
156+
napi_value Init(napi_env env, napi_value exports) {
157+
napi_value fn1;
158+
NODE_API_CALL(env, napi_create_function(
159+
env, NULL, NAPI_AUTO_LENGTH, TestCallFunction, NULL, &fn1));
160+
161+
napi_value fn2;
162+
NODE_API_CALL(env, napi_create_function(
163+
env, "Name", NAPI_AUTO_LENGTH, TestFunctionName, NULL, &fn2));
164+
165+
napi_value fn3;
166+
NODE_API_CALL(env, napi_create_function(
167+
env, "Name_extra", 5, TestFunctionName, NULL, &fn3));
168+
169+
napi_value fn4;
170+
NODE_API_CALL(env,
171+
napi_create_function(
172+
env, "MakeTrackedFunction", NAPI_AUTO_LENGTH, MakeTrackedFunction,
173+
NULL, &fn4));
174+
175+
napi_value fn5;
176+
NODE_API_CALL(env,
177+
napi_create_function(
178+
env, "TestCreateFunctionParameters", NAPI_AUTO_LENGTH,
179+
TestCreateFunctionParameters, NULL, &fn5));
180+
181+
napi_value fn6;
182+
NODE_API_CALL(env,
183+
napi_create_function(
184+
env, "TestBadReturnExceptionPending", NAPI_AUTO_LENGTH,
185+
TestBadReturnExceptionPending, NULL, &fn6));
186+
187+
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestCall", fn1));
188+
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestName", fn2));
189+
NODE_API_CALL(env,
190+
napi_set_named_property(env, exports, "TestNameShort", fn3));
191+
NODE_API_CALL(env,
192+
napi_set_named_property(env, exports, "MakeTrackedFunction", fn4));
193+
194+
NODE_API_CALL(env,
195+
napi_set_named_property(
196+
env, exports, "TestCreateFunctionParameters", fn5));
197+
198+
NODE_API_CALL(env,
199+
napi_set_named_property(
200+
env, exports, "TestBadReturnExceptionPending", fn6));
201+
202+
return exports;
203+
}
204+
EXTERN_C_END

0 commit comments

Comments
 (0)