Node.js optimizes subsequent ThreadsafeFunction invocations to happen during the same event loop tick, but only if the same instance of ThreadsafeFunction is used. The performance improvement is most noticeable when used in Electron, because scheduling a new UV tick in Electron is very costly. With this change EventQueue will use an existing instance of ThreadsafeTrampoline (wrapper around ThreadsafeFunction) if compiled with napi-6 feature, or it will fallback to creating a new ThreadsafeFunction per EventQueue instance. Fix: #727
634 lines
22 KiB
Rust
634 lines
22 KiB
Rust
//! The [Neon][neon] crate provides bindings for writing [Node.js addons][addons]
|
|
//! (i.e., dynamically-loaded binary modules) with a safe and fast Rust API.
|
|
//!
|
|
//! ## Getting Started
|
|
//!
|
|
//! You can conveniently bootstrap a new Neon project with the Neon project
|
|
//! generator. You don't need to install anything special on your machine as
|
|
//! long as you have a [supported version of Node and Rust][supported] on
|
|
//! your system.
|
|
//!
|
|
//! To start a new project, open a terminal in the directory where you would
|
|
//! like to place the project, and run at the command prompt:
|
|
//!
|
|
//! ```text
|
|
//! % npm init neon my-project
|
|
//! ... answer the user prompts ...
|
|
//! ✨ Created Neon project `my-project`. Happy 🦀 hacking! ✨
|
|
//! ```
|
|
//!
|
|
//! where `my-project` can be any name you like for the project. This will
|
|
//! run the Neon project generator, prompting you with a few questions and
|
|
//! placing a simple but working Neon project in a subdirectory called
|
|
//! `my-project` (or whatever name you chose).
|
|
//!
|
|
//! You can then install and build the project by changing into the project
|
|
//! directory and running the standard Node installation command:
|
|
//!
|
|
//! ```text
|
|
//! % cd my-project
|
|
//! % npm install
|
|
//! % node
|
|
//! > require(".").hello()
|
|
//! 'hello node'
|
|
//! ```
|
|
//!
|
|
//! You can look in the project's generated `README.md` for more details on
|
|
//! the project structure.
|
|
//!
|
|
//! ## Example
|
|
//!
|
|
//! The generated `src/lib.rs` contains a function annotated with the
|
|
//! [`#[neon::main]`](main) attribute, marking it as the module's main entry
|
|
//! point to be executed when the module is loaded. This function can have
|
|
//! any name but is conventionally called `main`:
|
|
//!
|
|
//! ```no_run
|
|
//! # #[cfg(feature = "neon-macros")] {
|
|
//! # use neon::prelude::*;
|
|
//! #
|
|
//! # fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
|
|
//! # Ok(cx.string("hello node"))
|
|
//! # }
|
|
//! #
|
|
//! #[neon::main]
|
|
//! fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
|
//! cx.export_function("hello", hello)?;
|
|
//! Ok(())
|
|
//! }
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! The example code generated by `npm init neon` exports a single
|
|
//! function via [`ModuleContext::export_function`](context::ModuleContext::export_function).
|
|
//! The `hello` function is defined just above `main` in `src/lib.rs`:
|
|
//!
|
|
//! ```
|
|
//! # use neon::prelude::*;
|
|
//! #
|
|
//! fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
|
|
//! Ok(cx.string("hello node"))
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! The `hello` function takes a [`FunctionContext`](context::FunctionContext) and
|
|
//! returns a JavaScript string. Because all Neon functions can potentially throw a
|
|
//! JavaScript exception, the return type is wrapped in a [`JsResult`](result::JsResult).
|
|
//!
|
|
//! [neon]: https://www.neon-bindings.com/
|
|
//! [addons]: https://nodejs.org/api/addons.html
|
|
//! [supported]: https://github.com/neon-bindings/neon#platform-support
|
|
|
|
pub mod borrow;
|
|
pub mod context;
|
|
#[cfg(any(
|
|
feature = "event-handler-api",
|
|
all(feature = "napi-4", feature = "event-queue-api")
|
|
))]
|
|
pub mod event;
|
|
pub mod handle;
|
|
pub mod meta;
|
|
pub mod object;
|
|
pub mod prelude;
|
|
#[cfg(feature = "napi-1")]
|
|
pub mod reflect;
|
|
pub mod result;
|
|
#[cfg(feature = "legacy-runtime")]
|
|
pub mod task;
|
|
pub mod types;
|
|
|
|
#[doc(hidden)]
|
|
pub mod macro_internal;
|
|
|
|
#[cfg(feature = "proc-macros")]
|
|
pub use neon_macros::*;
|
|
|
|
#[cfg(feature = "napi-6")]
|
|
mod lifecycle;
|
|
|
|
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
|
|
mod trampoline;
|
|
|
|
#[cfg(all(feature = "legacy-runtime", feature = "napi-1"))]
|
|
compile_error!("Cannot enable both `legacy-runtime` and `napi-*` features.\n\nTo use `napi-*`, disable `legacy-runtime` by setting `default-features` to `false` in Cargo.toml\nor with cargo's --no-default-features flag.");
|
|
|
|
#[cfg(all(feature = "napi-1", not(feature = "legacy-runtime")))]
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! register_module {
|
|
($module:pat, $init:block) => {
|
|
register_module!(|$module| $init);
|
|
};
|
|
|
|
(|$module:pat| $init:block) => {
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn napi_register_module_v1(
|
|
env: $crate::macro_internal::runtime::raw::Env,
|
|
m: $crate::macro_internal::runtime::raw::Local
|
|
) -> $crate::macro_internal::runtime::raw::Local
|
|
{
|
|
// Suppress the default Rust panic hook, which prints diagnostics to stderr.
|
|
#[cfg(not(feature = "default-panic-hook"))]
|
|
::std::panic::set_hook(::std::boxed::Box::new(|_| { }));
|
|
|
|
fn __init_neon_module($module: $crate::context::ModuleContext) -> $crate::result::NeonResult<()> $init
|
|
|
|
$crate::macro_internal::initialize_module(
|
|
env,
|
|
std::mem::transmute(m),
|
|
__init_neon_module,
|
|
);
|
|
|
|
m
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "legacy-runtime")]
|
|
/// Register the current crate as a Node module, providing startup
|
|
/// logic for initializing the module object at runtime.
|
|
///
|
|
/// The first argument is a pattern bound to a `neon::context::ModuleContext`. This
|
|
/// is usually bound to a mutable variable `mut cx`, which can then be used to
|
|
/// pass to Neon APIs that require mutable access to an execution context.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// ```
|
|
/// # #[cfg(feature = "n-api")] {
|
|
/// # use neon::prelude::*;
|
|
/// # fn foo(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
|
|
/// # fn bar(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
|
|
/// # fn baz(mut cx: FunctionContext) -> JsResult<JsUndefined> { Ok(cx.undefined()) }
|
|
/// register_module!(mut cx, {
|
|
/// cx.export_function("foo", foo)?;
|
|
/// cx.export_function("bar", bar)?;
|
|
/// cx.export_function("baz", baz)?;
|
|
/// Ok(())
|
|
/// });
|
|
/// # }
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! register_module {
|
|
($module:pat, $init:block) => {
|
|
// Mark this function as a global constructor (like C++).
|
|
#[allow(improper_ctypes)]
|
|
#[cfg_attr(target_os = "linux", link_section = ".ctors")]
|
|
#[cfg_attr(target_os = "android", link_section = ".ctors")]
|
|
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
|
|
#[cfg_attr(target_os = "ios", link_section = "__DATA,__mod_init_func")]
|
|
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
|
|
#[used]
|
|
pub static __LOAD_NEON_MODULE: extern "C" fn() = {
|
|
fn __init_neon_module($module: $crate::context::ModuleContext) -> $crate::result::NeonResult<()> $init
|
|
|
|
extern "C" fn __load_neon_module() {
|
|
// Put everything else in the ctor fn so the user fn can't see it.
|
|
#[repr(C)]
|
|
struct __NodeModule {
|
|
version: i32,
|
|
flags: u32,
|
|
dso_handle: *mut u8,
|
|
filename: *const u8,
|
|
register_func: Option<extern "C" fn(
|
|
$crate::handle::Handle<$crate::types::JsObject>, *mut u8, *mut u8)>,
|
|
context_register_func: Option<extern "C" fn(
|
|
$crate::handle::Handle<$crate::types::JsObject>, *mut u8, *mut u8, *mut u8)>,
|
|
modname: *const u8,
|
|
priv_data: *mut u8,
|
|
link: *mut __NodeModule
|
|
}
|
|
|
|
// Mark as used during tests to suppress warnings
|
|
#[cfg_attr(test, used)]
|
|
static mut __NODE_MODULE: __NodeModule = __NodeModule {
|
|
version: 0,
|
|
flags: 0,
|
|
dso_handle: 0 as *mut _,
|
|
filename: b"neon_source.rs\0" as *const u8,
|
|
register_func: Some(__register_neon_module),
|
|
context_register_func: None,
|
|
modname: b"neon_module\0" as *const u8,
|
|
priv_data: 0 as *mut _,
|
|
link: 0 as *mut _
|
|
};
|
|
|
|
extern "C" fn __register_neon_module(
|
|
m: $crate::handle::Handle<$crate::types::JsObject>, _: *mut u8, _: *mut u8) {
|
|
$crate::macro_internal::initialize_module(m, __init_neon_module);
|
|
}
|
|
|
|
extern "C" {
|
|
fn node_module_register(module: *mut __NodeModule);
|
|
}
|
|
|
|
// Suppress the default Rust panic hook, which prints diagnostics to stderr.
|
|
#[cfg(not(feature = "default-panic-hook"))]
|
|
::std::panic::set_hook(::std::boxed::Box::new(|_| { }));
|
|
|
|
// During tests, node is not available. Skip module registration.
|
|
#[cfg(not(test))]
|
|
unsafe {
|
|
// Set the ABI version based on the NODE_MODULE_VERSION constant provided by the current node headers.
|
|
__NODE_MODULE.version = $crate::macro_internal::runtime::module::get_version();
|
|
node_module_register(&mut __NODE_MODULE);
|
|
}
|
|
}
|
|
|
|
__load_neon_module
|
|
};
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "legacy-runtime")]
|
|
#[doc(hidden)]
|
|
#[macro_export(local_inner_macros)]
|
|
macro_rules! class_definition {
|
|
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; $mnames:tt ; $mdefs:tt ; init($cx:pat) $body:block $($rest:tt)* ) => {
|
|
class_definition!($cls ;
|
|
$cname ;
|
|
$typ ;
|
|
{
|
|
fn _______allocator_rust_y_u_no_hygienic_items_______($cx: $crate::context::CallContext<$crate::types::JsUndefined>) -> $crate::result::NeonResult<$typ> {
|
|
$body
|
|
}
|
|
|
|
$crate::macro_internal::AllocateCallback(_______allocator_rust_y_u_no_hygienic_items_______)
|
|
} ;
|
|
$call_ctor ;
|
|
$new_ctor ;
|
|
$mnames ;
|
|
$mdefs ;
|
|
$($rest)*);
|
|
};
|
|
|
|
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; ($($mname:tt)*) ; ($($mdef:tt)*) ; method $name:ident($cx:pat) $body:block $($rest:tt)* ) => {
|
|
class_definition!($cls ;
|
|
$cname ;
|
|
$typ ;
|
|
$allocator ;
|
|
$call_ctor ;
|
|
$new_ctor ;
|
|
($($mname)* $name) ;
|
|
($($mdef)* {
|
|
fn _______method_rust_y_u_no_hygienic_items_______($cx: $crate::context::CallContext<$cls>) -> $crate::result::JsResult<$crate::types::JsValue> {
|
|
$body
|
|
}
|
|
|
|
$crate::macro_internal::MethodCallback(_______method_rust_y_u_no_hygienic_items_______)
|
|
}) ;
|
|
$($rest)*);
|
|
};
|
|
|
|
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; $mnames:tt ; $mdefs:tt ; constructor($cx:pat) $body:block $($rest:tt)* ) => {
|
|
class_definition!($cls ;
|
|
$cname ;
|
|
$typ ;
|
|
$allocator ;
|
|
$call_ctor ;
|
|
({
|
|
fn _______constructor_rust_y_u_no_hygienic_items_______($cx: $crate::context::CallContext<$cls>) -> $crate::result::NeonResult<Option<$crate::handle::Handle<$crate::types::JsObject>>> {
|
|
$body
|
|
}
|
|
|
|
$crate::macro_internal::ConstructCallback(_______constructor_rust_y_u_no_hygienic_items_______)
|
|
}) ;
|
|
$mnames ;
|
|
$mdefs ;
|
|
$($rest)*);
|
|
};
|
|
|
|
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; $mnames:tt ; $mdefs:tt ; call($cx:pat) $body:block $($rest:tt)* ) => {
|
|
class_definition!($cls ;
|
|
$cname ;
|
|
$typ ;
|
|
$allocator ;
|
|
({
|
|
fn _______call_rust_y_u_no_hygienic_items_______($cx: $crate::context::CallContext<$crate::types::JsValue>) -> $crate::result::JsResult<$crate::types::JsValue> {
|
|
$body
|
|
}
|
|
|
|
$crate::macro_internal::ConstructorCallCallback(_______call_rust_y_u_no_hygienic_items_______)
|
|
}) ;
|
|
$new_ctor ;
|
|
$mnames ;
|
|
$mdefs ;
|
|
$($rest)*);
|
|
};
|
|
|
|
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:block ; ($($call_ctor:block)*) ; ($($new_ctor:block)*) ; ($($mname:ident)*) ; ($($mdef:block)*) ; $($rest:tt)* ) => {
|
|
impl $crate::object::Class for $cls {
|
|
type Internals = $typ;
|
|
|
|
fn setup<'a, C: $crate::context::Context<'a>>(_: &mut C) -> $crate::result::NeonResult<$crate::object::ClassDescriptor<'a, Self>> {
|
|
::std::result::Result::Ok(Self::describe(neon_stringify!($cname), $allocator)
|
|
$(.construct($new_ctor))*
|
|
$(.call($call_ctor))*
|
|
$(.method(neon_stringify!($mname), $mdef))*)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(feature = "legacy-runtime")]
|
|
#[doc(hidden)]
|
|
#[macro_export(local_inner_macros)]
|
|
macro_rules! impl_managed {
|
|
($cls:ident) => {
|
|
impl $crate::handle::Managed for $cls {
|
|
fn to_raw(self) -> $crate::macro_internal::runtime::raw::Local {
|
|
let $cls(raw) = self;
|
|
raw
|
|
}
|
|
|
|
fn from_raw(
|
|
_env: neon::macro_internal::Env,
|
|
raw: $crate::macro_internal::runtime::raw::Local,
|
|
) -> Self {
|
|
$cls(raw)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(feature = "legacy-runtime")]
|
|
/// Declare custom native JavaScript types with Rust implementations.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// ```rust
|
|
/// # use neon::prelude::*;
|
|
/// # fn main() {}
|
|
/// pub struct Greeter {
|
|
/// greeting: String
|
|
/// }
|
|
///
|
|
/// declare_types! {
|
|
///
|
|
/// /// A class for generating greeting strings.
|
|
/// pub class JsGreeter for Greeter {
|
|
/// init(mut cx) {
|
|
/// # #[cfg(feature = "legacy-runtime")]
|
|
/// let greeting = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value();
|
|
/// # #[cfg(feature = "napi-1")]
|
|
/// # let greeting = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value(&mut cx);
|
|
/// Ok(Greeter {
|
|
/// greeting: greeting
|
|
/// })
|
|
/// }
|
|
///
|
|
/// method hello(mut cx) {
|
|
/// # #[cfg(feature = "legacy-runtime")]
|
|
/// let name = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value();
|
|
/// # #[cfg(feature = "napi-1")]
|
|
/// # let name = cx.argument::<JsString>(0)?.to_string(&mut cx)?.value(&mut cx);
|
|
/// let this = cx.this();
|
|
/// let msg = {
|
|
/// let guard = cx.lock();
|
|
/// let greeter = this.borrow(&guard);
|
|
/// format!("{}, {}!", greeter.greeting, name)
|
|
/// };
|
|
/// Ok(cx.string(&msg[..]).upcast())
|
|
/// }
|
|
/// }
|
|
///
|
|
/// }
|
|
/// ```
|
|
#[macro_export(local_inner_macros)]
|
|
macro_rules! declare_types {
|
|
{ $(#[$attr:meta])* pub class $cls:ident for $typ:ident { $($body:tt)* } $($rest:tt)* } => {
|
|
declare_types! { $(#[$attr])* pub class $cls as $typ for $typ { $($body)* } $($rest)* }
|
|
};
|
|
|
|
{ $(#[$attr:meta])* class $cls:ident for $typ:ident { $($body:tt)* } $($rest:tt)* } => {
|
|
declare_types! { $(#[$attr])* class $cls as $typ for $typ { $($body)* } $($rest)* }
|
|
};
|
|
|
|
{ $(#[$attr:meta])* pub class $cls:ident as $cname:ident for $typ:ty { $($body:tt)* } $($rest:tt)* } => {
|
|
#[derive(Copy, Clone)]
|
|
#[repr(C)]
|
|
$(#[$attr])*
|
|
pub struct $cls($crate::macro_internal::runtime::raw::Local);
|
|
|
|
impl_managed!($cls);
|
|
|
|
class_definition!($cls ; $cname ; $typ ; () ; () ; () ; () ; () ; $($body)*);
|
|
|
|
declare_types! { $($rest)* }
|
|
};
|
|
|
|
{ $(#[$attr:meta])* class $cls:ident as $cname:ident for $typ:ty { $($body:tt)* } $($rest:tt)* } => {
|
|
#[derive(Copy, Clone)]
|
|
#[repr(C)]
|
|
$(#[$attr])*
|
|
struct $cls($crate::macro_internal::runtime::raw::Local);
|
|
|
|
impl_managed!($cls);
|
|
|
|
class_definition!($cls ; $cname ; $typ ; () ; () ; () ; () ; () ; $($body)*);
|
|
|
|
declare_types! { $($rest)* }
|
|
};
|
|
|
|
{ } => { };
|
|
}
|
|
|
|
#[cfg(feature = "legacy-runtime")]
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! neon_stringify {
|
|
($($inner:tt)*) => {
|
|
stringify! { $($inner)* }
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use lazy_static::lazy_static;
|
|
use semver::Version;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
use std::sync::Mutex;
|
|
|
|
// Create a mutex to enforce sequential running of the tests.
|
|
lazy_static! {
|
|
static ref TEST_MUTEX: Mutex<()> = Mutex::new(());
|
|
}
|
|
|
|
fn project_root() -> PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf()
|
|
}
|
|
|
|
fn log(test_name: &str) {
|
|
eprintln!("======================================================");
|
|
eprintln!("Neon test: {}", test_name);
|
|
eprintln!("======================================================");
|
|
}
|
|
|
|
fn run(cmd: &str, dir: &Path) {
|
|
let (shell, command_flag) = if cfg!(windows) {
|
|
("cmd", "/C")
|
|
} else {
|
|
("sh", "-c")
|
|
};
|
|
|
|
eprintln!("Running Neon test: {} {} {}", shell, command_flag, cmd);
|
|
|
|
assert!(Command::new(&shell)
|
|
.current_dir(dir)
|
|
.args(&[&command_flag, cmd])
|
|
.status()
|
|
.unwrap_or_else(|_| panic!(
|
|
"failed to execute test command: {} {} {}",
|
|
shell, command_flag, cmd
|
|
))
|
|
.success());
|
|
}
|
|
|
|
fn cli_setup() {
|
|
let cli = project_root().join("cli");
|
|
|
|
run("npm install", &cli);
|
|
run("npm run transpile", &cli);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_test() {
|
|
let _guard = TEST_MUTEX.lock();
|
|
|
|
log("cli_test");
|
|
|
|
cli_setup();
|
|
|
|
let test_cli = project_root().join("test").join("cli");
|
|
run("npm install", &test_cli);
|
|
run("npm run transpile", &test_cli);
|
|
run("npm test", &test_cli);
|
|
}
|
|
|
|
fn static_test_impl() {
|
|
let _guard = TEST_MUTEX.lock();
|
|
|
|
log("static_test");
|
|
|
|
run("cargo test", &project_root().join("test").join("static"));
|
|
}
|
|
|
|
// Only run the static tests in Beta. This will catch changes to error reporting
|
|
// and any associated usability regressions before a new Rust version is shipped
|
|
// but will have more stable results than Nightly.
|
|
#[rustversion::beta]
|
|
#[cfg(feature = "enable-static-tests")]
|
|
#[test]
|
|
fn static_test() {
|
|
static_test_impl()
|
|
}
|
|
|
|
#[rustversion::beta]
|
|
#[cfg(not(feature = "enable-static-tests"))]
|
|
#[test]
|
|
#[ignore]
|
|
fn static_test() {
|
|
static_test_impl()
|
|
}
|
|
|
|
#[rustversion::not(beta)]
|
|
#[cfg(feature = "enable-static-tests")]
|
|
compile_error!(
|
|
"The `enable-static-tests` feature can only be enabled with the Rust beta toolchain."
|
|
);
|
|
|
|
#[rustversion::not(beta)]
|
|
#[test]
|
|
#[ignore]
|
|
fn static_test() {
|
|
static_test_impl()
|
|
}
|
|
|
|
#[test]
|
|
fn dynamic_test() {
|
|
let _guard = TEST_MUTEX.lock();
|
|
|
|
log("dynamic_test");
|
|
|
|
cli_setup();
|
|
|
|
let test_dynamic = project_root().join("test").join("dynamic");
|
|
run("npm install", &test_dynamic);
|
|
run("npm test", &test_dynamic);
|
|
}
|
|
|
|
#[test]
|
|
fn dynamic_cargo_test() {
|
|
let _guard = TEST_MUTEX.lock();
|
|
|
|
log("dynamic_cargo_test");
|
|
|
|
let test_dynamic_cargo = project_root().join("test").join("dynamic").join("native");
|
|
run("cargo test", &test_dynamic_cargo);
|
|
}
|
|
|
|
#[test]
|
|
fn electron_test() {
|
|
let _guard = TEST_MUTEX.lock();
|
|
|
|
log("electron_test");
|
|
|
|
cli_setup();
|
|
|
|
let test_electron = project_root().join("test").join("electron");
|
|
run("npm install", &test_electron);
|
|
run("npm test", &test_electron);
|
|
}
|
|
|
|
// Once we publish versions of neon-sys that match the versions of the other
|
|
// neon crates, `cargo package` can succeed again.
|
|
#[test]
|
|
#[ignore]
|
|
fn package_test() {
|
|
let _guard = TEST_MUTEX.lock();
|
|
|
|
log("package_test");
|
|
|
|
let test_package = project_root().join("crates").join("neon-runtime");
|
|
|
|
// Allow uncommitted changes outside of CI
|
|
if std::env::var("CI") == Ok("true".to_string()) {
|
|
run("cargo package", &test_package);
|
|
} else {
|
|
run("cargo package --allow-dirty", &test_package);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn napi_test() {
|
|
let _guard = TEST_MUTEX.lock();
|
|
|
|
log("napi_test");
|
|
|
|
cli_setup();
|
|
|
|
let node_version_output = Command::new("node")
|
|
.arg("--version")
|
|
.output()
|
|
.expect("failed to get Node version")
|
|
.stdout;
|
|
|
|
// Chop off the 'v' prefix.
|
|
let node_version_bytes = &node_version_output[1..];
|
|
let node_version_str = std::str::from_utf8(node_version_bytes).unwrap();
|
|
let node_version = Version::parse(node_version_str).unwrap();
|
|
|
|
let v10 = Version::parse("10.0.0").unwrap();
|
|
|
|
if node_version <= v10 {
|
|
eprintln!("N-API tests only run on Node 10 or later.");
|
|
return;
|
|
}
|
|
|
|
let test_napi = project_root().join("test").join("napi");
|
|
run("npm install", &test_napi);
|
|
run("npm test", &test_napi);
|
|
}
|
|
}
|