diff options
author | Matt Mastracci <matthew@mastracci.com> | 2024-02-12 13:46:50 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-12 13:46:50 -0700 |
commit | f60720090c7bd8cdf91d7aebd0c42e01c86b3b83 (patch) | |
tree | 9becb7ff7e40d37769601fa049beccd101d58a98 /tests/napi | |
parent | bd1358efab8ba7339a8e70034315fa7da840292e (diff) |
chore: move test_ffi and test_nap to tests/ [WIP] (#22394)
Moving some additional NAPI and. FFI tests out of the tree root.
Diffstat (limited to 'tests/napi')
48 files changed, 3270 insertions, 0 deletions
diff --git a/tests/napi/.gitignore b/tests/napi/.gitignore new file mode 100644 index 000000000..54de1ef34 --- /dev/null +++ b/tests/napi/.gitignore @@ -0,0 +1,7 @@ +package-lock.json + +# Test generated artifacts +.swc +*.dylib +*.so +*.dll diff --git a/tests/napi/Cargo.toml b/tests/napi/Cargo.toml new file mode 100644 index 000000000..611d6d550 --- /dev/null +++ b/tests/napi/Cargo.toml @@ -0,0 +1,22 @@ +# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +[package] +name = "test_napi" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +publish = false +repository.workspace = true + +[lib] +crate-type = ["cdylib"] + +[dependencies] +napi-sys = { version = "=2.2.2", default-features = false, features = ["napi7"] } + +[dev-dependencies] +test_util.workspace = true + +[build-dependencies] +napi-build = "1" diff --git a/tests/napi/array_test.js b/tests/napi/array_test.js new file mode 100644 index 000000000..572d3a899 --- /dev/null +++ b/tests/napi/array_test.js @@ -0,0 +1,19 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const array = loadTestLibrary(); + +Deno.test("napi array new", function () { + const e = [0, "Hello", {}]; + const r = array.test_array_new(e); + assertEquals(typeof r, "object"); + assertEquals(r.length, 3); + assertEquals(e, r); +}); + +Deno.test("napi array new with length", function () { + const r = array.test_array_new_with_length(100); + assertEquals(typeof r, "object"); + assertEquals(r.length, 100); +}); diff --git a/tests/napi/arraybuffer_test.js b/tests/napi/arraybuffer_test.js new file mode 100644 index 000000000..f55b9a78c --- /dev/null +++ b/tests/napi/arraybuffer_test.js @@ -0,0 +1,24 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assert, assertEquals, loadTestLibrary } from "./common.js"; + +const typedarray = loadTestLibrary(); + +Deno.test("napi arraybuffer detach", function () { + const buf = new ArrayBuffer(5); + assertEquals(buf.byteLength, 5); + typedarray.test_detached(buf); + assertEquals(buf.byteLength, 0); +}); + +Deno.test("napi arraybuffer is detached", function () { + const buf = new ArrayBuffer(5); + assertEquals(buf.byteLength, 5); + assert(!typedarray.is_detached(buf)); + typedarray.test_detached(buf); + assert(typedarray.is_detached(buf)); + + [2, {}, "foo", null, undefined, new Uint8Array(10)].forEach((value) => { + assert(!typedarray.is_detached(value)); + }); +}); diff --git a/tests/napi/async_test.js b/tests/napi/async_test.js new file mode 100644 index 000000000..4d9ad99c2 --- /dev/null +++ b/tests/napi/async_test.js @@ -0,0 +1,16 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const asyncTask = loadTestLibrary(); + +Deno.test("napi async task schedule", async () => { + let called = false; + await new Promise((resolve) => { + asyncTask.test_async_work(() => { + called = true; + resolve(); + }); + }); + assertEquals(called, true); +}); diff --git a/tests/napi/bigint_test.js b/tests/napi/bigint_test.js new file mode 100644 index 000000000..4a9ada205 --- /dev/null +++ b/tests/napi/bigint_test.js @@ -0,0 +1,63 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, assertThrows, loadTestLibrary } from "./common.js"; + +const bi = loadTestLibrary(); + +Deno.test("cases", function () { + const cases = [ + 0n, + -0n, + 1n, + -1n, + 100n, + 2121n, + -1233n, + 986583n, + -976675n, + 98765432213456789876546896323445679887645323232436587988766545658n, + -4350987086545760976737453646576078997096876957864353245245769809n, + ]; + + for (const num of cases) { + if (num > -(2n ** 63n) && num < 2n ** 63n) { + assertEquals(bi.testInt64(num), num); + assertEquals(bi.isLossless(num, true), true); + } else { + assertEquals(bi.isLossless(num, true), false); + } + + if (num >= 0 && num < 2n ** 64n) { + assertEquals(bi.testUint64(num), num); + assertEquals(bi.isLossless(num, false), true); + } else { + assertEquals(bi.isLossless(num, false), false); + } + + assertEquals(bi.testWords(num), num); + } +}); + +Deno.test( + // TODO(bartlomieju): fix this test + { ignore: true }, + function tooBigBigInt() { + assertThrows( + () => bi.createTooBigBigInt(), + Error, + "Invalid argument", + ); + }, +); + +Deno.test( + // TODO(bartlomieju): fix this test + { ignore: true }, + function exceptionForwarding() { + assertThrows( + () => bi.makeBigIntWordsThrow(), + Error, + "Maximum BigInt size exceeded", + ); + }, +); diff --git a/tests/napi/build.rs b/tests/napi/build.rs new file mode 100644 index 000000000..c2fe86a53 --- /dev/null +++ b/tests/napi/build.rs @@ -0,0 +1,7 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/tests/napi/callback_test.js b/tests/napi/callback_test.js new file mode 100644 index 000000000..98622d48d --- /dev/null +++ b/tests/napi/callback_test.js @@ -0,0 +1,38 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const callback = loadTestLibrary(); + +Deno.test("napi callback run with args", function () { + const result = callback.test_callback_run((a, b) => a + b, [1, 2]); + assertEquals(result, 3); +}); + +Deno.test("napi callback run with args (no return)", function () { + const result = callback.test_callback_run(() => {}, []); + assertEquals(result, undefined); +}); + +Deno.test("napi callback run with args (extra arguments)", function () { + const result = callback.test_callback_run((a, b) => a + b, [ + "Hello,", + " Deno!", + 1, + 2, + 3, + ]); + assertEquals(result, "Hello, Deno!"); +}); + +Deno.test("napi callback run with args & recv", function () { + const result = callback.test_callback_run_with_recv( + function () { + assertEquals(this, 69); + return this; + }, + [], + 69, + ); + assertEquals(result, 69); +}); diff --git a/tests/napi/cleanup_hook_test.js b/tests/napi/cleanup_hook_test.js new file mode 100644 index 000000000..0d83bc5b4 --- /dev/null +++ b/tests/napi/cleanup_hook_test.js @@ -0,0 +1,36 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const properties = loadTestLibrary(); + +if (import.meta.main) { + properties.installCleanupHook(); + console.log("installed cleanup hook"); +} else { + Deno.test("napi cleanup hook", async () => { + const { stdout, stderr, code } = await new Deno.Command(Deno.execPath(), { + args: [ + "run", + "--config", + Deno.realPathSync("../config/deno.json"), + "--no-lock", + "--allow-read", + "--allow-run", + "--allow-ffi", + "--unstable-ffi", + import.meta.url, + ], + }).output(); + + assertEquals(new TextDecoder().decode(stderr), ""); + assertEquals(code, 0); + + const stdoutText = new TextDecoder().decode(stdout); + const stdoutLines = stdoutText.split("\n"); + assertEquals(stdoutLines.length, 4); + assertEquals(stdoutLines[0], "installed cleanup hook"); + assertEquals(stdoutLines[1], "cleanup(18)"); + assertEquals(stdoutLines[2], "cleanup(42)"); + }); +} diff --git a/tests/napi/coerce_test.js b/tests/napi/coerce_test.js new file mode 100644 index 000000000..64f014801 --- /dev/null +++ b/tests/napi/coerce_test.js @@ -0,0 +1,74 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const coerce = loadTestLibrary(); + +Deno.test("napi coerce bool", function () { + assertEquals(coerce.test_coerce_bool(true), true); + assertEquals(coerce.test_coerce_bool(false), false); + assertEquals(coerce.test_coerce_bool(0), false); + assertEquals(coerce.test_coerce_bool(69), true); + assertEquals(coerce.test_coerce_bool(Number.MAX_SAFE_INTEGER), true); + assertEquals(coerce.test_coerce_bool(new Array(10)), true); + assertEquals(coerce.test_coerce_bool("Hello, Deno!"), true); + assertEquals(coerce.test_coerce_bool(Symbol("[[test]]")), true); + assertEquals(coerce.test_coerce_bool({}), true); + assertEquals(coerce.test_coerce_bool(() => false), true); + assertEquals(coerce.test_coerce_bool(undefined), false); + assertEquals(coerce.test_coerce_bool(null), false); +}); + +Deno.test("napi coerce number", function () { + assertEquals(coerce.test_coerce_number(true), 1); + assertEquals(coerce.test_coerce_number(false), 0); + assertEquals(coerce.test_coerce_number(0), 0); + assertEquals(coerce.test_coerce_number(69), 69); + assertEquals(coerce.test_coerce_number(""), 0); + assertEquals( + coerce.test_coerce_number(Number.MAX_SAFE_INTEGER), + Number.MAX_SAFE_INTEGER, + ); + assertEquals(coerce.test_coerce_number(new Array(10)), NaN); + assertEquals(coerce.test_coerce_number("Hello, Deno!"), NaN); + assertEquals(coerce.test_coerce_number({}), NaN); + assertEquals(coerce.test_coerce_number(() => false), NaN); + assertEquals(coerce.test_coerce_number(undefined), NaN); + assertEquals(coerce.test_coerce_number(null), 0); +}); + +Deno.test("napi coerce string", function () { + assertEquals(coerce.test_coerce_string(true), "true"); + assertEquals(coerce.test_coerce_string(false), "false"); + assertEquals(coerce.test_coerce_string(0), "0"); + assertEquals(coerce.test_coerce_string(69), "69"); + assertEquals(coerce.test_coerce_string(""), ""); + assertEquals( + coerce.test_coerce_string(Number.MAX_SAFE_INTEGER), + "9007199254740991", + ); + assertEquals(coerce.test_coerce_string(new Array(10)), ",,,,,,,,,"); + assertEquals(coerce.test_coerce_string("Hello, Deno!"), "Hello, Deno!"); + assertEquals(coerce.test_coerce_string({}), "[object Object]"); + assertEquals(coerce.test_coerce_string(() => false), "() => false"); + assertEquals(coerce.test_coerce_string(undefined), "undefined"); + assertEquals(coerce.test_coerce_string(null), "null"); +}); + +Deno.test("napi coerce object", function () { + assertEquals(coerce.test_coerce_object(true), new Boolean(true)); + assertEquals(coerce.test_coerce_object(false), new Boolean(false)); + assertEquals(coerce.test_coerce_object(0), new Number(0)); + assertEquals(coerce.test_coerce_object(69), new Number(0)); + assertEquals(coerce.test_coerce_object(""), new String("")); + assertEquals( + coerce.test_coerce_object(Number.MAX_SAFE_INTEGER), + new Number(Number.MAX_SAFE_INTEGER), + ); + assertEquals(coerce.test_coerce_object(new Array(10)), new Array(10)); + assertEquals( + coerce.test_coerce_object("Hello, Deno!"), + new String("Hello, Deno!"), + ); + assertEquals(coerce.test_coerce_object({}), {}); +}); diff --git a/tests/napi/common.js b/tests/napi/common.js new file mode 100644 index 000000000..3a4f253ef --- /dev/null +++ b/tests/napi/common.js @@ -0,0 +1,28 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +export { + assert, + assertEquals, + assertRejects, + assertThrows, +} from "@std/assert/mod.ts"; +export { fromFileUrl } from "@std/path/mod.ts"; +import process from "node:process"; + +const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); +export const [libPrefix, libSuffix] = { + darwin: ["lib", "dylib"], + linux: ["lib", "so"], + windows: ["", "dll"], +}[Deno.build.os]; + +export function loadTestLibrary() { + const specifier = `${targetDir}/${libPrefix}test_napi.${libSuffix}`; + + // Internal, used in ext/node + const module = {}; + // Pass some flag, it should be ignored, but make sure it doesn't print + // warnings. + process.dlopen(module, specifier, 0); + return module.exports; +} diff --git a/tests/napi/date_test.js b/tests/napi/date_test.js new file mode 100644 index 000000000..49eec5cca --- /dev/null +++ b/tests/napi/date_test.js @@ -0,0 +1,17 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const date = loadTestLibrary(); + +Deno.test("napi date", function () { + const dateTypeTestDate = date.createDate(1549183351); + assertEquals(date.isDate(dateTypeTestDate), true); + assertEquals(date.isDate(new Date(1549183351)), true); + assertEquals(date.isDate(2.4), false); + assertEquals(date.isDate("not a date"), false); + assertEquals(date.isDate(undefined), false); + assertEquals(date.isDate(null), false); + assertEquals(date.isDate({}), false); + assertEquals(date.getDateValue(new Date(1549183351)), 1549183351); +}); diff --git a/tests/napi/env_test.js b/tests/napi/env_test.js new file mode 100644 index 000000000..72b868a66 --- /dev/null +++ b/tests/napi/env_test.js @@ -0,0 +1,10 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assert, loadTestLibrary } from "./common.js"; + +const env = loadTestLibrary(); + +Deno.test("napi get global", function () { + const g = env.testNodeGlobal(); + assert(g === globalThis); +}); diff --git a/tests/napi/error_test.js b/tests/napi/error_test.js new file mode 100644 index 000000000..57acde5c1 --- /dev/null +++ b/tests/napi/error_test.js @@ -0,0 +1,215 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { + assert, + assertEquals, + assertThrows, + loadTestLibrary, +} from "./common.js"; + +const testError = loadTestLibrary(); + +const theError = new Error("Some error"); +const theTypeError = new TypeError("Some type error"); +const theSyntaxError = new SyntaxError("Some syntax error"); +const theRangeError = new RangeError("Some type error"); +const theReferenceError = new ReferenceError("Some reference error"); +const theURIError = new URIError("Some URI error"); +const theEvalError = new EvalError("Some eval error"); + +function assertThrowsWithCode(fn, value) { + let thrown = false; + + try { + fn(); + } catch (e) { + thrown = true; + assertEquals(e.message, value.message); + assertEquals(e.code, value.code); + } finally { + assert(thrown); + } +} + +Deno.test("napi error", function () { + class MyError extends Error {} + const myError = new MyError("Some MyError"); + + // Test that native error object is correctly classed + assertEquals(testError.checkError(theError), true); + + // Test that native type error object is correctly classed + assertEquals(testError.checkError(theTypeError), true); + + // Test that native syntax error object is correctly classed + assertEquals(testError.checkError(theSyntaxError), true); + + // Test that native range error object is correctly classed + assertEquals(testError.checkError(theRangeError), true); + + // Test that native reference error object is correctly classed + assertEquals(testError.checkError(theReferenceError), true); + + // Test that native URI error object is correctly classed + assertEquals(testError.checkError(theURIError), true); + + // Test that native eval error object is correctly classed + assertEquals(testError.checkError(theEvalError), true); + + // Test that class derived from native error is correctly classed + assertEquals(testError.checkError(myError), true); + + // Test that non-error object is correctly classed + assertEquals(testError.checkError({}), false); + + // Test that non-error primitive is correctly classed + assertEquals(testError.checkError("non-object"), false); + + assertThrows( + () => { + testError.throwExistingError(); + }, + Error, + "existing error", + ); + + assertThrows( + () => { + testError.throwError(); + }, + Error, + "error", + ); + + assertThrows( + () => { + testError.throwRangeError(); + }, + RangeError, + "range error", + ); + + assertThrows( + () => { + testError.throwTypeError(); + }, + TypeError, + "type error", + ); + + // assertThrows(() => { + // testError.throwSyntaxError(); + // }, "SyntaxError: syntax error"); + + [42, {}, [], Symbol("xyzzy"), true, "ball", undefined, null, NaN] + .forEach((value) => { + let thrown = false; + + try { + testError.throwArbitrary(value); + } catch (e) { + thrown = true; + assertEquals(e, value); + } finally { + assert(thrown); + } + }); + + assertThrowsWithCode( + () => testError.throwErrorCode(), + { + code: "ERR_TEST_CODE", + message: "Error [error]", + }, + ); + + assertThrowsWithCode( + () => testError.throwRangeErrorCode(), + { + code: "ERR_TEST_CODE", + message: "RangeError [range error]", + }, + ); + + assertThrowsWithCode( + () => testError.throwTypeErrorCode(), + { + code: "ERR_TEST_CODE", + message: "TypeError [type error]", + }, + ); + + // assertThrowsWithCode( + // () => testError.throwSyntaxErrorCode(), + // { + // code: "ERR_TEST_CODE", + // message: "SyntaxError [syntax error]", + // }, + // ); + + let error = testError.createError(); + assert( + error instanceof Error, + "expected error to be an instance of Error", + ); + assertEquals(error.message, "error"); + + error = testError.createRangeError(); + assert( + error instanceof RangeError, + "expected error to be an instance of RangeError", + ); + assertEquals(error.message, "range error"); + + error = testError.createTypeError(); + assert( + error instanceof TypeError, + "expected error to be an instance of TypeError", + ); + assertEquals(error.message, "type error"); + + // TODO(bartlomieju): this is experimental API + // error = testError.createSyntaxError(); + // assert( + // error instanceof SyntaxError, + // "expected error to be an instance of SyntaxError", + // ); + // assertEquals(error.message, "syntax error"); + + error = testError.createErrorCode(); + assert( + error instanceof Error, + "expected error to be an instance of Error", + ); + assertEquals(error.code, "ERR_TEST_CODE"); + assertEquals(error.message, "Error [error]"); + assertEquals(error.name, "Error"); + + error = testError.createRangeErrorCode(); + assert( + error instanceof RangeError, + "expected error to be an instance of RangeError", + ); + assertEquals(error.message, "RangeError [range error]"); + assertEquals(error.code, "ERR_TEST_CODE"); + assertEquals(error.name, "RangeError"); + + error = testError.createTypeErrorCode(); + assert( + error instanceof TypeError, + "expected error to be an instance of TypeError", + ); + assertEquals(error.message, "TypeError [type error]"); + assertEquals(error.code, "ERR_TEST_CODE"); + assertEquals(error.name, "TypeError"); + + // TODO(bartlomieju): this is experimental API + // error = testError.createSyntaxErrorCode(); + // assert( + // error instanceof SyntaxError, + // "expected error to be an instance of SyntaxError", + // ); + // assertEquals(error.message, "SyntaxError [syntax error]"); + // assertEquals(error.code, "ERR_TEST_CODE"); + // assertEquals(error.name, "SyntaxError"); +}); diff --git a/tests/napi/init_test.js b/tests/napi/init_test.js new file mode 100644 index 000000000..5f2507876 --- /dev/null +++ b/tests/napi/init_test.js @@ -0,0 +1,14 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assert, libSuffix } from "./common.js"; + +const ops = Deno[Deno.internal].core.ops; + +Deno.test("ctr initialization (napi_module_register)", { + ignore: Deno.build.os == "windows", +}, function () { + const path = new URL(`./module.${libSuffix}`, import.meta.url).pathname; + const obj = ops.op_napi_open(path, {}); + assert(obj != null); + assert(typeof obj === "object"); +}); diff --git a/tests/napi/make_callback_test.js b/tests/napi/make_callback_test.js new file mode 100644 index 000000000..b1f7912ae --- /dev/null +++ b/tests/napi/make_callback_test.js @@ -0,0 +1,53 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const mc = loadTestLibrary(); + +Deno.test("napi makeCallback1", function () { + const resource = {}; + + let callCount = 0; + function cb() { + callCount++; + assertEquals(arguments.length, 0); + assertEquals(this, globalThis); + return 42; + } + assertEquals(mc.makeCallback(resource, globalThis, cb), 42); + assertEquals(callCount, 1); +}); + +Deno.test("napi makeCallback2", function () { + const resource = {}; + + let callCount = 0; + function cb(x) { + callCount++; + assertEquals(arguments.length, 1); + assertEquals(this, globalThis); + assertEquals(x, 1337); + return 42; + } + assertEquals(mc.makeCallback(resource, globalThis, cb, 1337), 42); + assertEquals(callCount, 1); +}); + +Deno.test("napi makeCallback3", function () { + const resource = {}; + + let callCount = 0; + + function multiArgFunc(arg1, arg2, arg3) { + callCount++; + assertEquals(arg1, 1); + assertEquals(arg2, 2); + assertEquals(arg3, 3); + return 42; + } + assertEquals( + mc.makeCallback(resource, globalThis, multiArgFunc, 1, 2, 3), + 42, + ); + assertEquals(callCount, 1); +}); diff --git a/tests/napi/mem_test.js b/tests/napi/mem_test.js new file mode 100644 index 000000000..bee8c194e --- /dev/null +++ b/tests/napi/mem_test.js @@ -0,0 +1,11 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assert, loadTestLibrary } from "./common.js"; + +const mem = loadTestLibrary(); + +Deno.test("napi adjust external memory", function () { + const adjusted = mem.adjust_external_memory(); + assert(typeof adjusted === "number"); + assert(adjusted > 0); +}); diff --git a/tests/napi/module.c b/tests/napi/module.c new file mode 100644 index 000000000..4548aa37f --- /dev/null +++ b/tests/napi/module.c @@ -0,0 +1,68 @@ +typedef struct napi_module { + int nm_version; + unsigned int nm_flags; + const char* nm_filename; + void* nm_register_func; + const char* nm_modname; + void* nm_priv; + void* reserved[4]; +} napi_module; + +#ifdef _WIN32 +#define NAPI_EXTERN __declspec(dllexport) +#define NAPI_CDECL __cdecl +#else +#define NAPI_EXTERN __attribute__((visibility("default"))) +#define NAPI_CDECL +#endif + +NAPI_EXTERN void NAPI_CDECL +napi_module_register(napi_module* mod); + +#if defined(_MSC_VER) +#if defined(__cplusplus) +#define NAPI_C_CTOR(fn) \ + static void NAPI_CDECL fn(void); \ + namespace { \ + struct fn##_ { \ + fn##_() { fn(); } \ + } fn##_v_; \ + } \ + static void NAPI_CDECL fn(void) +#else // !defined(__cplusplus) +#pragma section(".CRT$XCU", read) +// The NAPI_C_CTOR macro defines a function fn that is called during CRT +// initialization. +// C does not support dynamic initialization of static variables and this code +// simulates C++ behavior. Exporting the function pointer prevents it from being +// optimized. See for details: +// https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 +#define NAPI_C_CTOR(fn) \ + static void NAPI_CDECL fn(void); \ + __declspec(dllexport, allocate(".CRT$XCU")) void(NAPI_CDECL * fn##_)(void) = \ + fn; \ + static void NAPI_CDECL fn(void) +#endif // defined(__cplusplus) +#else +#define NAPI_C_CTOR(fn) \ + static void fn(void) __attribute__((constructor)); \ + static void fn(void) +#endif + +#define NAPI_MODULE_TEST(modname, regfunc) \ + static napi_module _module = { \ + 1, \ + 0, \ + __FILE__, \ + regfunc, \ + #modname, \ + 0, \ + {0}, \ + }; \ + NAPI_C_CTOR(_register_##modname) { napi_module_register(&_module); } \ + +void* init(void* env __attribute__((unused)), void* exports) { + return exports; +} + +NAPI_MODULE_TEST(TEST_NAPI_MODULE_NAME, init) diff --git a/tests/napi/numbers_test.js b/tests/napi/numbers_test.js new file mode 100644 index 000000000..8a99c831d --- /dev/null +++ b/tests/napi/numbers_test.js @@ -0,0 +1,18 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const numbers = loadTestLibrary(); + +Deno.test("napi int32", function () { + assertEquals(numbers.test_int32(69), 69); + assertEquals(numbers.test_int32(Number.MAX_SAFE_INTEGER), -1); +}); + +Deno.test("napi int64", function () { + assertEquals(numbers.test_int64(69), 69); + assertEquals( + numbers.test_int64(Number.MAX_SAFE_INTEGER), + Number.MAX_SAFE_INTEGER, + ); +}); diff --git a/tests/napi/object_wrap_test.js b/tests/napi/object_wrap_test.js new file mode 100644 index 000000000..f79fd08f8 --- /dev/null +++ b/tests/napi/object_wrap_test.js @@ -0,0 +1,41 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assert, assertEquals, loadTestLibrary } from "./common.js"; + +const objectWrap = loadTestLibrary(); + +Deno.test("napi object wrap new", function () { + const obj = new objectWrap.NapiObject(0); + assertEquals(obj.get_value(), 0); + obj.set_value(10); + assertEquals(obj.get_value(), 10); + obj.increment(); + assertEquals(obj.get_value(), 11); + obj.increment(); + obj.set_value(10); + assertEquals(obj.get_value(), 10); + assertEquals(objectWrap.NapiObject.factory(), 64); +}); + +Deno.test("napi bind finalizer", function () { + const obj = {}; + objectWrap.test_bind_finalizer(obj); +}); + +Deno.test("napi external finalizer", function () { + let obj = objectWrap.test_external_finalizer(); + assert(obj); + obj = null; +}); + +Deno.test("napi external buffer", function () { + let buf = objectWrap.test_external_buffer(); + assertEquals(buf, new Uint8Array([1, 2, 3])); + buf = null; +}); + +Deno.test("napi external arraybuffer", function () { + let buf = objectWrap.test_external_arraybuffer(); + assertEquals(new Uint8Array(buf), new Uint8Array([1, 2, 3])); + buf = null; +}); diff --git a/tests/napi/promise_test.js b/tests/napi/promise_test.js new file mode 100644 index 000000000..e4bbfee6b --- /dev/null +++ b/tests/napi/promise_test.js @@ -0,0 +1,34 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, assertRejects, loadTestLibrary } from "./common.js"; + +const promise = loadTestLibrary(); + +Deno.test("napi new promise and resolve", async () => { + const p = promise.test_promise_new(); + promise.test_promise_resolve(69); + + assertEquals(await p, 69); +}); + +Deno.test("napi new promise and reject", () => { + const p = promise.test_promise_new(); + + assertRejects(async () => { + promise.test_promise_reject(new TypeError("pikaboo")); + await p; + }, TypeError); +}); + +Deno.test("napi new promise and reject", async () => { + const p = promise.test_promise_new(); + const is = promise.test_promise_is(p); + assertEquals(typeof is, "boolean"); + assertEquals(is, true); + + assertEquals(promise.test_promise_is(undefined), false); + assertEquals(promise.test_promise_is({}), false); + promise.test_promise_resolve(69); + + assertEquals(await p, 69); +}); diff --git a/tests/napi/properties_test.js b/tests/napi/properties_test.js new file mode 100644 index 000000000..21a3555e8 --- /dev/null +++ b/tests/napi/properties_test.js @@ -0,0 +1,24 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const properties = loadTestLibrary(); + +Deno.test("napi properties", () => { + assertEquals(properties.test_property_rw, 1); + properties.test_property_rw = 2; + assertEquals(properties.test_property_rw, 2); + + assertEquals(properties.test_property_r, 1); + + // https://github.com/denoland/deno/issues/17509 + assertEquals(properties.test_simple_property, { + nice: 69, + }); + + assertEquals(properties.key_v8_string, 1); + const symbols = Object.getOwnPropertySymbols(properties); + assertEquals(symbols.length, 1); + assertEquals(symbols[0].description, "key_v8_symbol"); + assertEquals(properties[symbols[0]], 1); +}); diff --git a/tests/napi/src/array.rs b/tests/napi/src/array.rs new file mode 100644 index 000000000..6df420eb5 --- /dev/null +++ b/tests/napi/src/array.rs @@ -0,0 +1,73 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_number; +use napi_sys::ValueType::napi_object; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_array_new( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_object); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_array(env, &mut value)); + + let mut length: u32 = 0; + assert_napi_ok!(napi_get_array_length(env, args[0], &mut length)); + + for i in 0..length { + let mut e: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_element(env, args[0], i, &mut e)); + assert_napi_ok!(napi_set_element(env, value, i, e)); + } + + value +} + +extern "C" fn test_array_new_with_length( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_number); + + let mut len: u32 = 0; + assert_napi_ok!(napi_get_value_uint32(env, args[0], &mut len)); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_array_with_length(env, len as usize, &mut value)); + + value +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_array_new", test_array_new), + napi_new_property!( + env, + "test_array_new_with_length", + test_array_new_with_length + ), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/arraybuffer.rs b/tests/napi/src/arraybuffer.rs new file mode 100644 index 000000000..8402f5d86 --- /dev/null +++ b/tests/napi/src/arraybuffer.rs @@ -0,0 +1,52 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::*; + +extern "C" fn test_detached( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value = false; + assert_napi_ok!(napi_is_detached_arraybuffer(env, args[0], &mut value)); + assert!(!value); + assert_napi_ok!(napi_detach_arraybuffer(env, args[0])); + assert_napi_ok!(napi_is_detached_arraybuffer(env, args[0], &mut value)); + assert!(value); + args[0] +} + +extern "C" fn is_detached( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value = false; + assert_napi_ok!(napi_is_detached_arraybuffer(env, args[0], &mut value)); + + let mut result = std::ptr::null_mut(); + assert_napi_ok!(napi_get_boolean(env, value, &mut result)); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_detached", test_detached), + napi_new_property!(env, "is_detached", is_detached), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/async.rs b/tests/napi/src/async.rs new file mode 100644 index 000000000..3d3827b51 --- /dev/null +++ b/tests/napi/src/async.rs @@ -0,0 +1,114 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_function; +use napi_sys::*; +use std::os::raw::c_char; +use std::os::raw::c_void; +use std::ptr; + +pub struct Baton { + called: bool, + func: napi_ref, + task: napi_async_work, +} + +unsafe extern "C" fn execute(_env: napi_env, data: *mut c_void) { + let baton: &mut Baton = &mut *(data as *mut Baton); + assert!(!baton.called); + assert!(!baton.func.is_null()); + + baton.called = true; +} + +unsafe extern "C" fn complete( + env: napi_env, + status: napi_status, + data: *mut c_void, +) { + assert!(status == napi_ok); + let baton: Box<Baton> = Box::from_raw(data as *mut Baton); + assert!(baton.called); + assert!(!baton.func.is_null()); + + let mut global: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_global(env, &mut global)); + + let mut callback: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_reference_value(env, baton.func, &mut callback)); + + let mut _result: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_call_function( + env, + global, + callback, + 0, + ptr::null(), + &mut _result + )); + assert_napi_ok!(napi_delete_reference(env, baton.func)); + assert_napi_ok!(napi_delete_async_work(env, baton.task)); +} + +extern "C" fn test_async_work( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_function); + + let mut resource_name: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + "test_async_resource".as_ptr() as *const c_char, + usize::MAX, + &mut resource_name, + )); + + let async_work: napi_async_work = ptr::null_mut(); + + let mut func: napi_ref = ptr::null_mut(); + assert_napi_ok!(napi_create_reference(env, args[0], 1, &mut func)); + let baton = Box::new(Baton { + called: false, + func, + task: async_work, + }); + let mut async_work = baton.task; + let baton_ptr = Box::into_raw(baton) as *mut c_void; + + assert_napi_ok!(napi_create_async_work( + env, + ptr::null_mut(), + resource_name, + Some(execute), + Some(complete), + baton_ptr, + &mut async_work, + )); + let mut baton = unsafe { Box::from_raw(baton_ptr as *mut Baton) }; + baton.task = async_work; + Box::into_raw(baton); + assert_napi_ok!(napi_queue_async_work(env, async_work)); + + ptr::null_mut() +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = + &[napi_new_property!(env, "test_async_work", test_async_work)]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/bigint.rs b/tests/napi/src/bigint.rs new file mode 100644 index 000000000..d86782331 --- /dev/null +++ b/tests/napi/src/bigint.rs @@ -0,0 +1,205 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::cstr; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::Status::napi_pending_exception; +use napi_sys::ValueType::napi_bigint; +use napi_sys::*; +use std::ptr; + +extern "C" fn is_lossless( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 2); + assert_eq!(argc, 2); + + let mut is_signed = false; + assert_napi_ok!(napi_get_value_bool(env, args[1], &mut is_signed)); + + let mut lossless = false; + + if is_signed { + let mut input: i64 = 0; + assert_napi_ok!(napi_get_value_bigint_int64( + env, + args[0], + &mut input, + &mut lossless + )); + } else { + let mut input: u64 = 0; + assert_napi_ok!(napi_get_value_bigint_uint64( + env, + args[0], + &mut input, + &mut lossless + )); + } + + let mut output: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_boolean(env, lossless, &mut output)); + + output +} + +extern "C" fn test_int64( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, _argc, _) = napi_get_callback_info!(env, info, 2); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_bigint); + + let mut input: i64 = 0; + let mut lossless = false; + assert_napi_ok!(napi_get_value_bigint_int64( + env, + args[0], + &mut input, + &mut lossless + )); + + let mut output: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_bigint_int64(env, input, &mut output)); + + output +} + +extern "C" fn test_uint64( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, _argc, _) = napi_get_callback_info!(env, info, 2); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_bigint); + + let mut input: u64 = 0; + let mut lossless = false; + assert_napi_ok!(napi_get_value_bigint_uint64( + env, + args[0], + &mut input, + &mut lossless + )); + + let mut output: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_bigint_uint64(env, input, &mut output)); + + output +} + +extern "C" fn test_words( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, _argc, _) = napi_get_callback_info!(env, info, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_bigint); + + let mut expected_work_count = 0; + assert_napi_ok!(napi_get_value_bigint_words( + env, + args[0], + ptr::null_mut(), + &mut expected_work_count, + ptr::null_mut() + )); + + let mut sign_bit = 0; + let mut word_count: usize = 10; + let mut words: Vec<u64> = Vec::with_capacity(10); + + assert_napi_ok!(napi_get_value_bigint_words( + env, + args[0], + &mut sign_bit, + &mut word_count, + words.as_mut_ptr(), + )); + + assert_eq!(word_count, expected_work_count); + let mut output: napi_value = ptr::null_mut(); + + assert_napi_ok!(napi_create_bigint_words( + env, + sign_bit, + word_count, + words.as_ptr(), + &mut output, + )); + output +} + +extern "C" fn create_too_big_big_int( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let sign_bit = 0; + let word_count = usize::MAX; + let words: Vec<u64> = Vec::with_capacity(10); + + let mut output: napi_value = ptr::null_mut(); + let result = unsafe { + napi_create_bigint_words( + env, + sign_bit, + word_count, + words.as_ptr(), + &mut output, + ) + }; + assert_eq!(result, 1); + + output +} + +extern "C" fn make_big_int_words_throw( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let words: Vec<u64> = Vec::with_capacity(10); + let mut output = ptr::null_mut(); + + let status = unsafe { + napi_create_bigint_words(env, 0, usize::MAX, words.as_ptr(), &mut output) + }; + + if status != napi_pending_exception { + unsafe { + napi_throw_error( + env, + ptr::null_mut(), + cstr!("Expected status 'napi_pending_exception'"), + ) + }; + } + + ptr::null_mut() +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "isLossless", is_lossless), + napi_new_property!(env, "testInt64", test_int64), + napi_new_property!(env, "testUint64", test_uint64), + napi_new_property!(env, "testWords", test_words), + napi_new_property!(env, "createTooBigBigInt", create_too_big_big_int), + napi_new_property!(env, "makeBigIntWordsThrow", make_big_int_words_throw), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/callback.rs b/tests/napi/src/callback.rs new file mode 100644 index 000000000..8909f5176 --- /dev/null +++ b/tests/napi/src/callback.rs @@ -0,0 +1,117 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_function; +use napi_sys::ValueType::napi_object; +use napi_sys::ValueType::napi_undefined; +use napi_sys::*; +use std::ptr; + +/// `test_callback_run((a, b) => a + b, [1, 2])` => 3 +extern "C" fn test_callback_run( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + // We want to have argv with size 4, even though the callback will have + // only two arguments. We'll assert that the remaining two args are undefined. + let (args, argc, _) = napi_get_callback_info!(env, info, 4); + assert_eq!(argc, 2); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_function); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[1], &mut ty)); + assert_eq!(ty, napi_object); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[2], &mut ty)); + assert_eq!(ty, napi_undefined); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[3], &mut ty)); + assert_eq!(ty, napi_undefined); + + let mut len = 0; + assert_napi_ok!(napi_get_array_length(env, args[1], &mut len)); + + let mut argv = Vec::with_capacity(len as usize); + for index in 0..len { + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_element(env, args[1], index, &mut value)); + argv.push(value); + } + let mut global: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_global(env, &mut global)); + + let mut result: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_call_function( + env, + global, + args[0], + argv.len(), + argv.as_mut_ptr(), + &mut result, + )); + + result +} + +extern "C" fn test_callback_run_with_recv( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 3); + assert_eq!(argc, 3); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_function); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[1], &mut ty)); + assert_eq!(ty, napi_object); + + let mut len = 0; + assert_napi_ok!(napi_get_array_length(env, args[1], &mut len)); + + let mut argv = Vec::with_capacity(len as usize); + for index in 0..len { + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_element(env, args[1], index, &mut value)); + argv.push(value); + } + + let mut result: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_call_function( + env, + args[2], // recv + args[0], // cb + argv.len(), + argv.as_mut_ptr(), + &mut result, + )); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_callback_run", test_callback_run), + napi_new_property!( + env, + "test_callback_run_with_recv", + test_callback_run_with_recv + ), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/coerce.rs b/tests/napi/src/coerce.rs new file mode 100644 index 000000000..a40578384 --- /dev/null +++ b/tests/napi/src/coerce.rs @@ -0,0 +1,70 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_coerce_bool( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_coerce_to_bool(env, args[0], &mut value)); + value +} + +extern "C" fn test_coerce_number( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_coerce_to_number(env, args[0], &mut value)); + value +} + +extern "C" fn test_coerce_object( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_coerce_to_object(env, args[0], &mut value)); + value +} + +extern "C" fn test_coerce_string( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_coerce_to_string(env, args[0], &mut value)); + value +} +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_coerce_bool", test_coerce_bool), + napi_new_property!(env, "test_coerce_number", test_coerce_number), + napi_new_property!(env, "test_coerce_object", test_coerce_object), + napi_new_property!(env, "test_coerce_string", test_coerce_string), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/date.rs b/tests/napi/src/date.rs new file mode 100644 index 000000000..4d3c155c3 --- /dev/null +++ b/tests/napi/src/date.rs @@ -0,0 +1,74 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_number; +use napi_sys::*; +use std::ptr; + +extern "C" fn create_date( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_number); + + let mut time = -1.0; + assert_napi_ok!(napi_get_value_double(env, args[0], &mut time)); + + let mut date: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_date(env, time, &mut date)); + + date +} + +extern "C" fn is_date(env: napi_env, info: napi_callback_info) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let date: napi_value = args[0]; + let mut result: napi_value = std::ptr::null_mut(); + let mut is_date = false; + + assert_napi_ok!(napi_is_date(env, date, &mut is_date)); + assert_napi_ok!(napi_get_boolean(env, is_date, &mut result)); + + result +} + +extern "C" fn get_date_value( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let date: napi_value = args[0]; + let mut result: napi_value = std::ptr::null_mut(); + let mut value = 0.0; + + assert_napi_ok!(napi_get_date_value(env, date, &mut value)); + assert_napi_ok!(napi_create_double(env, value, &mut result)); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "createDate", create_date), + napi_new_property!(env, "isDate", is_date), + napi_new_property!(env, "getDateValue", get_date_value), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/env.rs b/tests/napi/src/env.rs new file mode 100644 index 000000000..ebc6532a3 --- /dev/null +++ b/tests/napi/src/env.rs @@ -0,0 +1,31 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::*; + +extern "C" fn get_node_global( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (_, argc, _) = napi_get_callback_info!(env, info, 0); + assert_eq!(argc, 0); + + let mut result: napi_value = std::ptr::null_mut(); + assert_napi_ok!(napi_get_global(env, &mut result)); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = + &[napi_new_property!(env, "testNodeGlobal", get_node_global)]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/error.rs b/tests/napi/src/error.rs new file mode 100644 index 000000000..e0d79c836 --- /dev/null +++ b/tests/napi/src/error.rs @@ -0,0 +1,288 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::cstr; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::*; +use std::ptr; + +extern "C" fn check_error( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + let mut r = false; + assert_napi_ok!(napi_is_error(env, args[0], &mut r)); + let mut result: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_boolean(env, r, &mut result)); + result +} + +extern "C" fn create_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_error( + env, + ptr::null_mut(), + message, + &mut result + )); + result +} + +extern "C" fn create_range_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("range error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_range_error( + env, + ptr::null_mut(), + message, + &mut result + )); + result +} + +extern "C" fn create_type_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("type error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_type_error( + env, + ptr::null_mut(), + message, + &mut result + )); + result +} + +extern "C" fn create_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + let mut code: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("Error [error]"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("ERR_TEST_CODE"), + usize::MAX, + &mut code + )); + assert_napi_ok!(napi_create_error(env, code, message, &mut result)); + result +} + +extern "C" fn create_range_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + let mut code: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("RangeError [range error]"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("ERR_TEST_CODE"), + usize::MAX, + &mut code + )); + assert_napi_ok!(napi_create_range_error(env, code, message, &mut result)); + result +} + +extern "C" fn create_type_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + let mut code: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("TypeError [type error]"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("ERR_TEST_CODE"), + usize::MAX, + &mut code + )); + assert_napi_ok!(napi_create_type_error(env, code, message, &mut result)); + result +} + +extern "C" fn throw_existing_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut message: napi_value = ptr::null_mut(); + let mut error: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("existing error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_error( + env, + std::ptr::null_mut(), + message, + &mut error + )); + assert_napi_ok!(napi_throw(env, error)); + std::ptr::null_mut() +} + +extern "C" fn throw_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_error(env, std::ptr::null_mut(), cstr!("error"),)); + std::ptr::null_mut() +} + +extern "C" fn throw_range_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_range_error( + env, + std::ptr::null_mut(), + cstr!("range error"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_type_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_type_error( + env, + std::ptr::null_mut(), + cstr!("type error"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_arbitrary( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + assert_napi_ok!(napi_throw(env, args[0])); + std::ptr::null_mut() +} + +extern "C" fn throw_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_error( + env, + cstr!("ERR_TEST_CODE"), + cstr!("Error [error]"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_range_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_range_error( + env, + cstr!("ERR_TEST_CODE"), + cstr!("RangeError [range error]"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_type_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_type_error( + env, + cstr!("ERR_TEST_CODE"), + cstr!("TypeError [type error]"), + )); + std::ptr::null_mut() +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "checkError", check_error), + napi_new_property!(env, "throwExistingError", throw_existing_error), + napi_new_property!(env, "throwError", throw_error), + napi_new_property!(env, "throwRangeError", throw_range_error), + napi_new_property!(env, "throwTypeError", throw_type_error), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "throwSyntaxError", throw_syntax_error), + napi_new_property!(env, "throwErrorCode", throw_error_code), + napi_new_property!(env, "throwRangeErrorCode", throw_range_error_code), + napi_new_property!(env, "throwTypeErrorCode", throw_type_error_code), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "throwSyntaxErrorCode", throw_syntax_error_code), + napi_new_property!(env, "throwArbitrary", throw_arbitrary), + napi_new_property!(env, "createError", create_error), + napi_new_property!(env, "createRangeError", create_range_error), + napi_new_property!(env, "createTypeError", create_type_error), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "createSyntaxError", create_syntax_error), + napi_new_property!(env, "createErrorCode", create_error_code), + napi_new_property!(env, "createRangeErrorCode", create_range_error_code), + napi_new_property!(env, "createTypeErrorCode", create_type_error_code), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "createSyntaxErrorCode", create_syntax_error_code), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/finalizer.rs b/tests/napi/src/finalizer.rs new file mode 100644 index 000000000..9769e775e --- /dev/null +++ b/tests/napi/src/finalizer.rs @@ -0,0 +1,141 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_object; +use napi_sys::*; +use std::ptr; + +unsafe extern "C" fn finalize_cb( + _env: napi_env, + data: *mut ::std::os::raw::c_void, + hint: *mut ::std::os::raw::c_void, +) { + assert!(data.is_null()); + assert!(hint.is_null()); +} + +extern "C" fn test_bind_finalizer( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_object); + + let obj = args[0]; + unsafe { + napi_add_finalizer( + env, + obj, + ptr::null_mut(), + Some(finalize_cb), + ptr::null_mut(), + ptr::null_mut(), + ) + }; + obj +} + +struct Thing { + _allocation: Vec<u8>, +} + +unsafe extern "C" fn finalize_cb_drop( + _env: napi_env, + data: *mut ::std::os::raw::c_void, + hint: *mut ::std::os::raw::c_void, +) { + let _ = Box::from_raw(data as *mut Thing); + assert!(hint.is_null()); +} + +extern "C" fn test_external_finalizer( + env: napi_env, + _: napi_callback_info, +) -> napi_value { + let data = Box::into_raw(Box::new(Thing { + _allocation: vec![1, 2, 3], + })); + + let mut result = ptr::null_mut(); + assert_napi_ok!(napi_create_external( + env, + data as _, + Some(finalize_cb_drop), + ptr::null_mut(), + &mut result + )); + result +} + +unsafe extern "C" fn finalize_cb_vec( + _env: napi_env, + data: *mut ::std::os::raw::c_void, + hint: *mut ::std::os::raw::c_void, +) { + let _ = Vec::from_raw_parts(data as *mut u8, 3, 3); + assert!(hint.is_null()); +} + +extern "C" fn test_external_buffer( + env: napi_env, + _: napi_callback_info, +) -> napi_value { + let mut result = ptr::null_mut(); + let buf: Vec<u8> = vec![1, 2, 3]; + assert_napi_ok!(napi_create_external_buffer( + env, + 3, + buf.as_ptr() as _, + Some(finalize_cb_vec), + ptr::null_mut(), + &mut result + )); + std::mem::forget(buf); + + result +} + +extern "C" fn test_external_arraybuffer( + env: napi_env, + _: napi_callback_info, +) -> napi_value { + let mut result = ptr::null_mut(); + let buf: Vec<u8> = vec![1, 2, 3]; + assert_napi_ok!(napi_create_external_arraybuffer( + env, + buf.as_ptr() as _, + 3, + Some(finalize_cb_vec), + ptr::null_mut(), + &mut result + )); + std::mem::forget(buf); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_bind_finalizer", test_bind_finalizer), + napi_new_property!(env, "test_external_finalizer", test_external_finalizer), + napi_new_property!(env, "test_external_buffer", test_external_buffer), + napi_new_property!( + env, + "test_external_arraybuffer", + test_external_arraybuffer + ), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/lib.rs b/tests/napi/src/lib.rs new file mode 100644 index 000000000..b9f93fbd6 --- /dev/null +++ b/tests/napi/src/lib.rs @@ -0,0 +1,171 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +#![allow(clippy::all)] +#![allow(clippy::undocumented_unsafe_blocks)] + +use std::ffi::c_void; + +use napi_sys::*; + +pub mod array; +pub mod arraybuffer; +pub mod r#async; +pub mod bigint; +pub mod callback; +pub mod coerce; +pub mod date; +pub mod env; +pub mod error; +pub mod finalizer; +pub mod make_callback; +pub mod mem; +pub mod numbers; +pub mod object_wrap; +pub mod primitives; +pub mod promise; +pub mod properties; +pub mod strings; +pub mod symbol; +pub mod tsfn; +pub mod typedarray; + +#[macro_export] +macro_rules! cstr { + ($s: literal) => {{ + std::ffi::CString::new($s).unwrap().into_raw() + }}; +} + +#[macro_export] +macro_rules! assert_napi_ok { + ($call: expr) => {{ + assert_eq!(unsafe { $call }, napi_sys::Status::napi_ok); + }}; +} + +#[macro_export] +macro_rules! napi_get_callback_info { + ($env: expr, $callback_info: expr, $size: literal) => {{ + let mut args = [std::ptr::null_mut(); $size]; + let mut argc = $size; + let mut this = std::ptr::null_mut(); + crate::assert_napi_ok!(napi_get_cb_info( + $env, + $callback_info, + &mut argc, + args.as_mut_ptr(), + &mut this, + std::ptr::null_mut(), + )); + (args, argc, this) + }}; +} + +#[macro_export] +macro_rules! napi_new_property { + ($env: expr, $name: expr, $value: expr) => { + napi_property_descriptor { + utf8name: concat!($name, "\0").as_ptr() as *const std::os::raw::c_char, + name: std::ptr::null_mut(), + method: Some($value), + getter: None, + setter: None, + data: std::ptr::null_mut(), + attributes: 0, + value: std::ptr::null_mut(), + } + }; +} + +extern "C" fn cleanup(arg: *mut c_void) { + println!("cleanup({})", arg as i64); +} + +extern "C" fn remove_this_hook(arg: *mut c_void) { + let env = arg as napi_env; + unsafe { napi_remove_env_cleanup_hook(env, Some(remove_this_hook), arg) }; +} + +static SECRET: i64 = 42; +static WRONG_SECRET: i64 = 17; +static THIRD_SECRET: i64 = 18; + +extern "C" fn install_cleanup_hook( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (_args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 0); + + unsafe { + napi_add_env_cleanup_hook(env, Some(cleanup), WRONG_SECRET as *mut c_void); + napi_add_env_cleanup_hook(env, Some(cleanup), SECRET as *mut c_void); + napi_add_env_cleanup_hook(env, Some(cleanup), THIRD_SECRET as *mut c_void); + napi_add_env_cleanup_hook(env, Some(remove_this_hook), env as *mut c_void); + napi_remove_env_cleanup_hook( + env, + Some(cleanup), + WRONG_SECRET as *mut c_void, + ); + } + + std::ptr::null_mut() +} + +pub fn init_cleanup_hook(env: napi_env, exports: napi_value) { + let properties = &[napi_new_property!( + env, + "installCleanupHook", + install_cleanup_hook + )]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} + +#[no_mangle] +unsafe extern "C" fn napi_register_module_v1( + env: napi_env, + _: napi_value, +) -> napi_value { + #[cfg(windows)] + { + napi_sys::setup(); + } + + // We create a fresh exports object and leave the passed + // exports object empty. + // + // https://github.com/denoland/deno/issues/17349 + let mut exports = std::ptr::null_mut(); + assert_napi_ok!(napi_create_object(env, &mut exports)); + + strings::init(env, exports); + numbers::init(env, exports); + typedarray::init(env, exports); + arraybuffer::init(env, exports); + array::init(env, exports); + env::init(env, exports); + error::init(env, exports); + finalizer::init(env, exports); + primitives::init(env, exports); + properties::init(env, exports); + promise::init(env, exports); + coerce::init(env, exports); + object_wrap::init(env, exports); + callback::init(env, exports); + r#async::init(env, exports); + date::init(env, exports); + tsfn::init(env, exports); + mem::init(env, exports); + bigint::init(env, exports); + symbol::init(env, exports); + make_callback::init(env, exports); + + init_cleanup_hook(env, exports); + + exports +} diff --git a/tests/napi/src/make_callback.rs b/tests/napi/src/make_callback.rs new file mode 100644 index 000000000..945df3452 --- /dev/null +++ b/tests/napi/src/make_callback.rs @@ -0,0 +1,85 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::cstr; +use napi_sys::ValueType::napi_function; +use napi_sys::*; +use std::ptr; + +extern "C" fn make_callback( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + const MAX_ARGUMENTS: usize = 10; + const RESERVED_ARGUMENTS: usize = 3; + + let mut args = [std::ptr::null_mut(); MAX_ARGUMENTS]; + let mut argc = MAX_ARGUMENTS; + assert_napi_ok!(napi_get_cb_info( + env, + info, + &mut argc, + args.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + )); + + assert!(argc > 0); + let resource = args[0]; + let recv = args[1]; + let func = args[2]; + + let mut argv: Vec<napi_value> = Vec::new(); + argv.resize(MAX_ARGUMENTS - RESERVED_ARGUMENTS, ptr::null_mut()); + for i in RESERVED_ARGUMENTS..argc { + argv[i - RESERVED_ARGUMENTS] = args[i]; + } + + let mut func_type: napi_valuetype = -1; + assert_napi_ok!(napi_typeof(env, func, &mut func_type)); + + let mut resource_name = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("test"), + usize::MAX, + &mut resource_name + )); + + let mut context: napi_async_context = ptr::null_mut(); + assert_napi_ok!(napi_async_init(env, resource, resource_name, &mut context)); + + let mut result = ptr::null_mut(); + assert_eq!(func_type, napi_function); + assert_napi_ok!(napi_make_callback( + env, + context, + recv, + func, + argc - RESERVED_ARGUMENTS, + argv.as_mut_ptr(), + &mut result + )); + + assert_napi_ok!(napi_async_destroy(env, context)); + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let mut fn_: napi_value = ptr::null_mut(); + + assert_napi_ok!(napi_create_function( + env, + ptr::null_mut(), + usize::MAX, + Some(make_callback), + ptr::null_mut(), + &mut fn_, + )); + assert_napi_ok!(napi_set_named_property( + env, + exports, + cstr!("makeCallback"), + fn_ + )); +} diff --git a/tests/napi/src/mem.rs b/tests/napi/src/mem.rs new file mode 100644 index 000000000..ebb6a5c7a --- /dev/null +++ b/tests/napi/src/mem.rs @@ -0,0 +1,34 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_new_property; +use napi_sys::*; +use std::ptr; + +extern "C" fn adjust_external_memory( + env: napi_env, + _: napi_callback_info, +) -> napi_value { + let mut adjusted_value = 0; + + assert_napi_ok!(napi_adjust_external_memory(env, 1024, &mut adjusted_value)); + + let mut result = ptr::null_mut(); + assert_napi_ok!(napi_create_int64(env, adjusted_value, &mut result)); + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[napi_new_property!( + env, + "adjust_external_memory", + adjust_external_memory + )]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/numbers.rs b/tests/napi/src/numbers.rs new file mode 100644 index 000000000..777ccbfac --- /dev/null +++ b/tests/napi/src/numbers.rs @@ -0,0 +1,60 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_number; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_int32( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_number); + + let mut int32 = -1; + assert_napi_ok!(napi_get_value_int32(env, args[0], &mut int32)); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_int32(env, int32, &mut value)); + value +} + +extern "C" fn test_int64( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_number); + + let mut int64 = -1; + assert_napi_ok!(napi_get_value_int64(env, args[0], &mut int64)); + + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_int64(env, int64, &mut value)); + value +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_int32", test_int32), + napi_new_property!(env, "test_int64", test_int64), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/object_wrap.rs b/tests/napi/src/object_wrap.rs new file mode 100644 index 000000000..d04107cf0 --- /dev/null +++ b/tests/napi/src/object_wrap.rs @@ -0,0 +1,156 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_number; +use napi_sys::*; +use std::os::raw::c_char; +use std::os::raw::c_void; +use std::ptr; + +pub struct NapiObject { + counter: i32, + _wrapper: napi_ref, +} + +impl NapiObject { + #[allow(clippy::new_ret_no_self)] + pub extern "C" fn new(env: napi_env, info: napi_callback_info) -> napi_value { + let mut new_target: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_new_target(env, info, &mut new_target)); + let is_constructor = !new_target.is_null(); + + let (args, argc, this) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + if is_constructor { + let mut value = 0; + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_number); + + assert_napi_ok!(napi_get_value_int32(env, args[0], &mut value)); + + let mut wrapper: napi_ref = ptr::null_mut(); + let obj = Box::new(Self { + counter: value, + _wrapper: wrapper, + }); + assert_napi_ok!(napi_wrap( + env, + this, + Box::into_raw(obj) as *mut c_void, + None, + ptr::null_mut(), + &mut wrapper, + )); + + return this; + } + + unreachable!(); + } + + pub extern "C" fn set_value( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (args, argc, this) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + let mut obj: *mut Self = ptr::null_mut(); + assert_napi_ok!(napi_unwrap( + env, + this, + &mut obj as *mut _ as *mut *mut c_void + )); + + assert_napi_ok!(napi_get_value_int32(env, args[0], &mut (*obj).counter)); + + ptr::null_mut() + } + + pub extern "C" fn get_value( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (_args, argc, this) = napi_get_callback_info!(env, info, 0); + assert_eq!(argc, 0); + let mut obj: *mut Self = ptr::null_mut(); + assert_napi_ok!(napi_unwrap( + env, + this, + &mut obj as *mut _ as *mut *mut c_void + )); + + let mut num: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_int32(env, (*obj).counter, &mut num)); + + num + } + + pub extern "C" fn increment( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (_args, argc, this) = napi_get_callback_info!(env, info, 0); + assert_eq!(argc, 0); + let mut obj: *mut Self = ptr::null_mut(); + assert_napi_ok!(napi_unwrap( + env, + this, + &mut obj as *mut _ as *mut *mut c_void + )); + + unsafe { + (*obj).counter += 1; + } + + ptr::null_mut() + } + + pub extern "C" fn factory( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (_args, argc, _this) = napi_get_callback_info!(env, info, 0); + assert_eq!(argc, 0); + + let int64 = 64; + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_int64(env, int64, &mut value)); + value + } +} + +pub fn init(env: napi_env, exports: napi_value) { + let mut static_prop = napi_new_property!(env, "factory", NapiObject::factory); + static_prop.attributes = PropertyAttributes::static_; + + let properties = &[ + napi_new_property!(env, "set_value", NapiObject::set_value), + napi_new_property!(env, "get_value", NapiObject::get_value), + napi_new_property!(env, "increment", NapiObject::increment), + static_prop, + ]; + + let mut cons: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_define_class( + env, + "NapiObject\0".as_ptr() as *mut c_char, + usize::MAX, + Some(NapiObject::new), + ptr::null_mut(), + properties.len(), + properties.as_ptr(), + &mut cons, + )); + + assert_napi_ok!(napi_set_named_property( + env, + exports, + "NapiObject\0".as_ptr() as *const c_char, + cons, + )); +} diff --git a/tests/napi/src/primitives.rs b/tests/napi/src/primitives.rs new file mode 100644 index 000000000..28fb8ec3d --- /dev/null +++ b/tests/napi/src/primitives.rs @@ -0,0 +1,30 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_new_property; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_get_undefined( + env: napi_env, + _: napi_callback_info, +) -> napi_value { + let mut result = ptr::null_mut(); + assert_napi_ok!(napi_get_undefined(env, &mut result)); + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[napi_new_property!( + env, + "test_get_undefined", + test_get_undefined + )]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/promise.rs b/tests/napi/src/promise.rs new file mode 100644 index 000000000..82cd7a160 --- /dev/null +++ b/tests/napi/src/promise.rs @@ -0,0 +1,74 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::*; +use std::ptr; + +static mut CURRENT_DEFERRED: napi_deferred = ptr::null_mut(); + +extern "C" fn test_promise_new( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_promise(env, &mut CURRENT_DEFERRED, &mut value)); + value +} + +extern "C" fn test_promise_resolve( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + assert_napi_ok!(napi_resolve_deferred(env, CURRENT_DEFERRED, args[0])); + unsafe { CURRENT_DEFERRED = ptr::null_mut() }; + ptr::null_mut() +} + +extern "C" fn test_promise_reject( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + assert_napi_ok!(napi_reject_deferred(env, CURRENT_DEFERRED, args[0])); + unsafe { CURRENT_DEFERRED = ptr::null_mut() }; + ptr::null_mut() +} + +extern "C" fn test_promise_is( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut is_promise: bool = false; + assert_napi_ok!(napi_is_promise(env, args[0], &mut is_promise)); + + let mut result: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_boolean(env, is_promise, &mut result)); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_promise_new", test_promise_new), + napi_new_property!(env, "test_promise_resolve", test_promise_resolve), + napi_new_property!(env, "test_promise_reject", test_promise_reject), + napi_new_property!(env, "test_promise_is", test_promise_is), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/properties.rs b/tests/napi/src/properties.rs new file mode 100644 index 000000000..43bef1949 --- /dev/null +++ b/tests/napi/src/properties.rs @@ -0,0 +1,113 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::cstr; +use napi_sys::PropertyAttributes::*; +use napi_sys::*; +use std::ptr; + +static NICE: i64 = 69; + +fn init_constants(env: napi_env) -> napi_value { + let mut constants: napi_value = ptr::null_mut(); + let mut value: napi_value = ptr::null_mut(); + + assert_napi_ok!(napi_create_object(env, &mut constants)); + assert_napi_ok!(napi_create_int64(env, NICE, &mut value)); + assert_napi_ok!(napi_set_named_property( + env, + constants, + cstr!("nice"), + value + )); + constants +} + +pub fn init(env: napi_env, exports: napi_value) { + let mut number: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_double(env, 1.0, &mut number)); + + // Key name as napi_value representing `v8::String` + let mut name_value: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("key_v8_string"), + usize::MAX, + &mut name_value, + )); + + // Key symbol + let mut symbol_description: napi_value = ptr::null_mut(); + let mut name_symbol: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("key_v8_symbol"), + usize::MAX, + &mut symbol_description, + )); + assert_napi_ok!(napi_create_symbol( + env, + symbol_description, + &mut name_symbol + )); + + let properties = &[ + napi_property_descriptor { + utf8name: cstr!("test_simple_property"), + name: ptr::null_mut(), + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable | writable, + value: init_constants(env), + }, + napi_property_descriptor { + utf8name: cstr!("test_property_rw"), + name: ptr::null_mut(), + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable | writable, + value: number, + }, + napi_property_descriptor { + utf8name: cstr!("test_property_r"), + name: ptr::null_mut(), + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable, + value: number, + }, + napi_property_descriptor { + utf8name: ptr::null(), + name: name_value, + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable, + value: number, + }, + napi_property_descriptor { + utf8name: ptr::null(), + name: name_symbol, + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable, + value: number, + }, + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/strings.rs b/tests/napi/src/strings.rs new file mode 100644 index 000000000..301ab23df --- /dev/null +++ b/tests/napi/src/strings.rs @@ -0,0 +1,49 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_string; +use napi_sys::*; + +extern "C" fn test_utf8(env: napi_env, info: napi_callback_info) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_string); + + args[0] +} + +extern "C" fn test_utf16( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_string); + + args[0] +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + // utf8 + napi_new_property!(env, "test_utf8", test_utf8), + // utf16 + napi_new_property!(env, "test_utf16", test_utf16), + // latin1 + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/symbol.rs b/tests/napi/src/symbol.rs new file mode 100644 index 000000000..6387d449f --- /dev/null +++ b/tests/napi/src/symbol.rs @@ -0,0 +1,39 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::ValueType::napi_string; +use napi_sys::*; + +extern "C" fn symbol_new( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + + let mut description: napi_value = std::ptr::null_mut(); + + if argc >= 1 { + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_string); + description = args[0]; + } + + let mut symbol: napi_value = std::ptr::null_mut(); + assert_napi_ok!(napi_create_symbol(env, description, &mut symbol)); + + symbol +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[napi_new_property!(env, "symbolNew", symbol_new)]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/src/tsfn.rs b/tests/napi/src/tsfn.rs new file mode 100644 index 000000000..dabc96f83 --- /dev/null +++ b/tests/napi/src/tsfn.rs @@ -0,0 +1,108 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// This test performs initialization similar to napi-rs. +// https://github.com/napi-rs/napi-rs/commit/a5a04a4e545f268769cc78e2bd6c45af4336aac3 + +use napi_sys as sys; +use std::ffi::c_char; +use std::ffi::c_void; +use std::ptr; + +macro_rules! check_status_or_panic { + ($code:expr, $msg:expr) => {{ + let c = $code; + match c { + sys::Status::napi_ok => {} + _ => panic!($msg), + } + }}; +} + +fn create_custom_gc(env: sys::napi_env) { + let mut custom_gc_fn = ptr::null_mut(); + check_status_or_panic!( + unsafe { + sys::napi_create_function( + env, + "custom_gc".as_ptr() as *const c_char, + 9, + Some(empty), + ptr::null_mut(), + &mut custom_gc_fn, + ) + }, + "Create Custom GC Function in napi_register_module_v1 failed" + ); + let mut async_resource_name = ptr::null_mut(); + check_status_or_panic!( + unsafe { + sys::napi_create_string_utf8( + env, + "CustomGC".as_ptr() as *const c_char, + 8, + &mut async_resource_name, + ) + }, + "Create async resource string in napi_register_module_v1 napi_register_module_v1" + ); + let mut custom_gc_tsfn = ptr::null_mut(); + check_status_or_panic!( + unsafe { + sys::napi_create_threadsafe_function( + env, + custom_gc_fn, + ptr::null_mut(), + async_resource_name, + 0, + 1, + ptr::null_mut(), + Some(custom_gc_finalize), + ptr::null_mut(), + Some(custom_gc), + &mut custom_gc_tsfn, + ) + }, + "Create Custom GC ThreadsafeFunction in napi_register_module_v1 failed" + ); + check_status_or_panic!( + unsafe { sys::napi_unref_threadsafe_function(env, custom_gc_tsfn) }, + "Unref Custom GC ThreadsafeFunction in napi_register_module_v1 failed" + ); +} + +unsafe extern "C" fn empty( + _env: sys::napi_env, + _info: sys::napi_callback_info, +) -> sys::napi_value { + ptr::null_mut() +} + +unsafe extern "C" fn custom_gc_finalize( + _env: sys::napi_env, + _finalize_data: *mut c_void, + _finalize_hint: *mut c_void, +) { +} + +extern "C" fn custom_gc( + env: sys::napi_env, + _js_callback: sys::napi_value, + _context: *mut c_void, + data: *mut c_void, +) { + let mut ref_count = 0; + check_status_or_panic!( + unsafe { + sys::napi_reference_unref(env, data as sys::napi_ref, &mut ref_count) + }, + "Failed to unref Buffer reference in Custom GC" + ); + check_status_or_panic!( + unsafe { sys::napi_delete_reference(env, data as sys::napi_ref) }, + "Failed to delete Buffer reference in Custom GC" + ); +} + +pub fn init(env: sys::napi_env, _exports: sys::napi_value) { + create_custom_gc(env); +} diff --git a/tests/napi/src/typedarray.rs b/tests/napi/src/typedarray.rs new file mode 100644 index 000000000..b512bd32f --- /dev/null +++ b/tests/napi/src/typedarray.rs @@ -0,0 +1,157 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use core::ffi::c_void; +use napi_sys::Status::napi_ok; +use napi_sys::TypedarrayType; +use napi_sys::ValueType::napi_number; +use napi_sys::ValueType::napi_object; +use napi_sys::*; +use std::os::raw::c_char; +use std::ptr; + +extern "C" fn test_multiply( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 2); + assert_eq!(argc, 2); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[0], &mut ty)); + assert_eq!(ty, napi_object); + + let input_array = args[0]; + let mut is_typed_array = false; + assert!( + unsafe { napi_is_typedarray(env, input_array, &mut is_typed_array) } + == napi_ok + ); + + let mut ty = -1; + assert_napi_ok!(napi_typeof(env, args[1], &mut ty)); + assert_eq!(ty, napi_number); + + let mut multiplier: f64 = 0.0; + assert_napi_ok!(napi_get_value_double(env, args[1], &mut multiplier)); + + let mut ty = -1; + let mut input_buffer = ptr::null_mut(); + let mut byte_offset = 0; + let mut length = 0; + + assert_napi_ok!(napi_get_typedarray_info( + env, + input_array, + &mut ty, + &mut length, + ptr::null_mut(), + &mut input_buffer, + &mut byte_offset, + )); + + let mut data = ptr::null_mut(); + let mut byte_length = 0; + + assert_napi_ok!(napi_get_arraybuffer_info( + env, + input_buffer, + &mut data, + &mut byte_length + )); + + let mut output_buffer = ptr::null_mut(); + let mut output_ptr = ptr::null_mut(); + assert_napi_ok!(napi_create_arraybuffer( + env, + byte_length, + &mut output_ptr, + &mut output_buffer, + )); + + let mut output_array: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_typedarray( + env, + ty, + length, + output_buffer, + byte_offset, + &mut output_array, + )); + + if ty == TypedarrayType::uint8_array { + let input_bytes = unsafe { (data as *mut u8).offset(byte_offset as isize) }; + let output_bytes = output_ptr as *mut u8; + for i in 0..length { + unsafe { + *output_bytes.offset(i as isize) = + (*input_bytes.offset(i as isize) as f64 * multiplier) as u8; + } + } + } else if ty == TypedarrayType::float64_array { + let input_doubles = + unsafe { (data as *mut f64).offset(byte_offset as isize) }; + let output_doubles = output_ptr as *mut f64; + for i in 0..length { + unsafe { + *output_doubles.offset(i as isize) = + *input_doubles.offset(i as isize) * multiplier; + } + } + } else { + assert_napi_ok!(napi_throw_error( + env, + ptr::null(), + "Typed array was of a type not expected by test.".as_ptr() + as *const c_char, + )); + return ptr::null_mut(); + } + + output_array +} + +extern "C" fn test_external( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut arraybuffer: napi_value = ptr::null_mut(); + let mut external: Box<[u8; 4]> = Box::new([0, 1, 2, 3]); + assert_napi_ok!(napi_create_external_arraybuffer( + env, + external.as_mut_ptr() as *mut c_void, + external.len(), + None, + ptr::null_mut(), + &mut arraybuffer, + )); + + let mut typedarray: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_typedarray( + env, + TypedarrayType::uint8_array, + external.len(), + arraybuffer, + 0, + &mut typedarray, + )); + + std::mem::forget(external); // Leak into JS land + typedarray +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "test_external", test_external), + napi_new_property!(env, "test_multiply", test_multiply), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/strings_test.js b/tests/napi/strings_test.js new file mode 100644 index 000000000..45cb133b2 --- /dev/null +++ b/tests/napi/strings_test.js @@ -0,0 +1,15 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const strings = loadTestLibrary(); + +Deno.test("napi string utf8", function () { + assertEquals(strings.test_utf8(""), ""); + assertEquals(strings.test_utf8("🦕"), "🦕"); +}); + +Deno.test("napi string", function () { + assertEquals(strings.test_utf16(""), ""); + assertEquals(strings.test_utf16("🦕"), "🦕"); +}); diff --git a/tests/napi/symbol_test.js b/tests/napi/symbol_test.js new file mode 100644 index 000000000..d8edec023 --- /dev/null +++ b/tests/napi/symbol_test.js @@ -0,0 +1,49 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assert, assertEquals, loadTestLibrary } from "./common.js"; + +const testSymbol = loadTestLibrary(); + +Deno.test("napi symbol1", () => { + const sym = testSymbol.symbolNew("test"); + assertEquals(sym.toString(), "Symbol(test)"); + + const myObj = {}; + const fooSym = testSymbol.symbolNew("foo"); + const otherSym = testSymbol.symbolNew("bar"); + myObj.foo = "bar"; + myObj[fooSym] = "baz"; + myObj[otherSym] = "bing"; + assertEquals(myObj.foo, "bar"); + assertEquals(myObj[fooSym], "baz"); + assertEquals(myObj[otherSym], "bing"); +}); + +Deno.test("napi symbol2", () => { + const sym = testSymbol.symbolNew("test"); + assertEquals(sym.toString(), "Symbol(test)"); + + const myObj = {}; + const fooSym = testSymbol.symbolNew("foo"); + myObj.foo = "bar"; + myObj[fooSym] = "baz"; + + assertEquals(Object.keys(myObj), ["foo"]); + assertEquals(Object.getOwnPropertyNames(myObj), ["foo"]); + assertEquals(Object.getOwnPropertySymbols(myObj), [fooSym]); +}); + +Deno.test("napi symbol3", () => { + assert(testSymbol.symbolNew() !== testSymbol.symbolNew()); + assert(testSymbol.symbolNew("foo") !== testSymbol.symbolNew("foo")); + assert(testSymbol.symbolNew("foo") !== testSymbol.symbolNew("bar")); + + const foo1 = testSymbol.symbolNew("foo"); + const foo2 = testSymbol.symbolNew("foo"); + const object = { + [foo1]: 1, + [foo2]: 2, + }; + assertEquals(object[foo1], 1); + assertEquals(object[foo2], 2); +}); diff --git a/tests/napi/tests/napi_tests.rs b/tests/napi/tests/napi_tests.rs new file mode 100644 index 000000000..671699651 --- /dev/null +++ b/tests/napi/tests/napi_tests.rs @@ -0,0 +1,87 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::process::Command; +use test_util::deno_cmd; +use test_util::deno_config_path; +use test_util::env_vars_for_npm_tests; +use test_util::http_server; +use test_util::napi_tests_path; + +#[cfg(debug_assertions)] +const BUILD_VARIANT: &str = "debug"; + +#[cfg(not(debug_assertions))] +const BUILD_VARIANT: &str = "release"; + +fn build() { + let mut build_plugin_base = Command::new("cargo"); + let mut build_plugin = + build_plugin_base.arg("build").arg("-p").arg("test_napi"); + if BUILD_VARIANT == "release" { + build_plugin = build_plugin.arg("--release"); + } + let build_plugin_output = build_plugin.output().unwrap(); + assert!(build_plugin_output.status.success()); + + // cc module.c -undefined dynamic_lookup -shared -Wl,-no_fixup_chains -dynamic -o module.dylib + #[cfg(not(target_os = "windows"))] + { + let out = if cfg!(target_os = "macos") { + "module.dylib" + } else { + "module.so" + }; + + let mut cc = Command::new("cc"); + + #[cfg(not(target_os = "macos"))] + let c_module = cc.arg("module.c").arg("-shared").arg("-o").arg(out); + + #[cfg(target_os = "macos")] + let c_module = { + cc.arg("module.c") + .arg("-undefined") + .arg("dynamic_lookup") + .arg("-shared") + .arg("-Wl,-no_fixup_chains") + .arg("-dynamic") + .arg("-o") + .arg(out) + }; + let c_module_output = c_module.output().unwrap(); + assert!(c_module_output.status.success()); + } +} + +#[test] +fn napi_tests() { + build(); + + let _http_guard = http_server(); + let output = deno_cmd() + .current_dir(napi_tests_path()) + .env("RUST_BACKTRACE", "1") + .arg("test") + .arg("--allow-read") + .arg("--allow-env") + .arg("--allow-ffi") + .arg("--allow-run") + .arg("--config") + .arg(deno_config_path()) + .arg("--no-lock") + .arg(".") + .envs(env_vars_for_npm_tests()) + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); + let stdout = std::str::from_utf8(&output.stdout).unwrap(); + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + + if !output.status.success() { + eprintln!("exit code {:?}", output.status.code()); + println!("stdout {stdout}"); + println!("stderr {stderr}"); + } + assert!(output.status.success()); +} diff --git a/tests/napi/typedarray_test.js b/tests/napi/typedarray_test.js new file mode 100644 index 000000000..25729754a --- /dev/null +++ b/tests/napi/typedarray_test.js @@ -0,0 +1,39 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assert, assertEquals, loadTestLibrary } from "./common.js"; + +const typedarray = loadTestLibrary(); + +Deno.test("napi typedarray uint8", function () { + const byteArray = new Uint8Array([0, 1, 2]); + assertEquals(byteArray.length, 3); + + const byteResult = typedarray.test_multiply(byteArray, 3); + assert(byteResult instanceof Uint8Array); + assertEquals(byteResult.length, 3); + assertEquals(byteResult[0], 0); + assertEquals(byteResult[1], 3); + assertEquals(byteResult[2], 6); +}); + +Deno.test("napi typedarray float64", function () { + const doubleArray = new Float64Array([0.0, 1.1, 2.2]); + assertEquals(doubleArray.length, 3); + + const doubleResult = typedarray.test_multiply(doubleArray, -3); + assert(doubleResult instanceof Float64Array); + assertEquals(doubleResult.length, 3); + assertEquals(doubleResult[0], -0); + assertEquals(Math.round(10 * doubleResult[1]) / 10, -3.3); + assertEquals(Math.round(10 * doubleResult[2]) / 10, -6.6); +}); + +// TODO(bartlomieju): this test causes segfaults when used with jemalloc. +// Node documentation provides a hint that this function is not supported by +// other runtime like electron. +// Deno.test("napi typedarray external", function () { +// assertEquals( +// new Uint8Array(typedarray.test_external()), +// new Uint8Array([0, 1, 2, 3]), +// ); +// }); |