Skip to content

Commit

Permalink
chore(neon): Add tests for neon::export
Browse files Browse the repository at this point in the history
  • Loading branch information
kjvalencik committed Apr 3, 2024
1 parent fdd31f1 commit 9e38acd
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 0 deletions.
96 changes: 96 additions & 0 deletions test/napi/lib/export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
const assert = require("assert");

const addon = require("..");

describe("neon::export macro", () => {
describe("globals", globals);
describe("functions", functions);
});

function globals() {
it("values", () => {
assert.strictEqual(addon.NUMBER, 42);
assert.strictEqual(addon.STRING, "Hello, World!");
assert.strictEqual(addon.renamedString, "Hello, World!");
});

it("json", () => {
assert.deepStrictEqual(addon.MESSAGES, ["hello", "neon"]);
assert.deepStrictEqual(addon.renamedMessages, ["hello", "neon"]);
});
}

function functions() {
it("void function", () => {
assert.strictEqual(addon.no_args_or_return(), undefined);
});

it("add - sync", () => {
assert.strictEqual(addon.simple_add(1, 2), 3);
assert.strictEqual(addon.renamedAdd(1, 2), 3);
});

it("add - task", async () => {
const p1 = addon.add_task(1, 2);
const p2 = addon.renamedAddTask(1, 2);

assert.ok(p1 instanceof Promise);
assert.ok(p2 instanceof Promise);

assert.strictEqual(await p1, 3);
assert.strictEqual(await p2, 3);
});

it("json sort", () => {
const arr = ["b", "c", "a"];
const expected = [...arr].sort();

assert.deepStrictEqual(addon.json_sort(arr), expected);
assert.deepStrictEqual(addon.renamedJsonSort(arr), expected);
});

it("json sort - task", async () => {
const arr = ["b", "c", "a"];
const expected = [...arr].sort();
const p1 = addon.json_sort_task(arr);
const p2 = addon.renamedJsonSortTask(arr);

assert.ok(p1 instanceof Promise);
assert.ok(p2 instanceof Promise);

assert.deepStrictEqual(await p1, expected);
assert.deepStrictEqual(await p2, expected);
});

it("can use context and handles", () => {
const actual = addon.concat_with_cx_and_handle("Hello,", " World!");
const expected = "Hello, World!";

assert.strictEqual(actual, expected);
});

it("error conversion", () => {
const msg = "Oh, no!";
const expected = new Error(msg);

assert.throws(() => addon.fail_with_throw(msg), expected);
});

it("tasks are concurrent", async () => {
const time = 100;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const start = process.hrtime.bigint();

await Promise.all([addon.sleep_task(time), sleep(time)]);

const end = process.hrtime.bigint();
const duration = end - start;

// If `addon.sleep_task` blocks the thread, the tasks will run sequentially
// and take a minimum of 2x `time`. Since they are run concurrently, we
// expect the time to be closer to 1x `time`.
const maxExpected = 2000000n * BigInt(time);

assert.ok(duration < maxExpected);
});
}
90 changes: 90 additions & 0 deletions test/napi/src/js/export.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use neon::{prelude::*, types::extract::Error};

#[neon::export]
const NUMBER: u8 = 42;

#[neon::export]
static STRING: &str = "Hello, World!";

#[neon::export(name = "renamedString")]
static RENAMED_STRING: &str = STRING;

#[neon::export(json)]
static MESSAGES: &[&str] = &["hello", "neon"];

#[neon::export(name = "renamedMessages", json)]
static RENAMED_MESSAGES: &[&str] = MESSAGES;

#[neon::export]
fn no_args_or_return() {}

#[neon::export]
fn simple_add(a: f64, b: f64) -> f64 {
a + b
}

#[neon::export(name = "renamedAdd")]
fn renamed_add(a: f64, b: f64) -> f64 {
simple_add(a, b)
}

#[neon::export(task)]
fn add_task(a: f64, b: f64) -> f64 {
simple_add(a, b)
}

#[neon::export(task, name = "renamedAddTask")]
fn renamed_add_task(a: f64, b: f64) -> f64 {
add_task(a, b)
}

#[neon::export(json)]
fn json_sort(mut items: Vec<String>) -> Vec<String> {
items.sort();
items
}

#[neon::export(json, name = "renamedJsonSort")]
fn renamed_json_sort(items: Vec<String>) -> Vec<String> {
json_sort(items)
}

#[neon::export(json, task)]
fn json_sort_task(items: Vec<String>) -> Vec<String> {
json_sort(items)
}

#[neon::export(json, name = "renamedJsonSortTask", task)]
fn renamed_json_sort_task(items: Vec<String>) -> Vec<String> {
json_sort(items)
}

#[neon::export]
fn concat_with_cx_and_handle<'cx>(
cx: &mut FunctionContext<'cx>,
a: String,
b: Handle<'cx, JsString>,
) -> Handle<'cx, JsString> {
let b = b.value(cx);

cx.string(a + &b)
}

#[neon::export]
fn fail_with_throw(msg: String) -> Result<(), Error> {
fn always_fails(msg: String) -> Result<(), String> {
Err(msg)
}

// `?` converts `String` into `Error`
always_fails(msg)?;

Ok(())
}

#[neon::export(task)]
fn sleep_task(ms: f64) {
use std::{thread, time::Duration};

thread::sleep(Duration::from_millis(ms as u64));
}
5 changes: 5 additions & 0 deletions test/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod js {
pub mod coercions;
pub mod date;
pub mod errors;
pub mod export;
pub mod extract;
pub mod functions;
pub mod futures;
Expand All @@ -26,6 +27,10 @@ mod js {

#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
neon::registered().export(&mut cx)?;

assert!(neon::registered().into_iter().next().is_some());

let greeting = cx.string("Hello, World!");
let greeting_copy = greeting.value(&mut cx);
let greeting_copy = cx.string(greeting_copy);
Expand Down

0 comments on commit 9e38acd

Please sign in to comment.