Skip to content

Commit

Permalink
Add a test for dyncall
Browse files Browse the repository at this point in the history
  • Loading branch information
filmor committed Aug 5, 2024
1 parent adcecd0 commit 70c426a
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"rustler_tests/native/rustler_serde_test",
"rustler_tests/native/dynamic_load",
"rustler_tests/native/rustler_compile_tests",
"rustler_tests/native/resource_dyncall",
"rustler_benchmarks/native/benchmark",
]
default-members = [
Expand Down
4 changes: 4 additions & 0 deletions rustler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ pub mod serde;

#[cfg(feature = "serde")]
pub use crate::serde::SerdeTerm;

pub mod sys {
pub use rustler_sys::*;
}
11 changes: 11 additions & 0 deletions rustler_tests/lib/resource_dyncall.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
if RustlerTest.Helper.has_nif_version("2.16") do
defmodule ResourceDyncall do
use Rustler,
otp_app: :rustler_test,
crate: :resource_dyncall

def new(_), do: err()

defp err(), do: :erlang.nif_error(:nif_not_loaded)
end
end
28 changes: 17 additions & 11 deletions rustler_tests/lib/rustler_test.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ defmodule NifNotLoadedError do
defexception message: "nif not loaded"
end

defmodule RustlerTest.Helper do
def nif_feature_from_running_version() do
[major, minor | _] =
:erlang.system_info(:nif_version)
|> to_string
|> String.split(".")

"nif_version_#{major}_#{minor}"
defmodule RustlerTest do
defmodule Helper do
@nif_version Version.parse!("#{:erlang.system_info(:nif_version)}.0")

def nif_feature_from_running_version() do
"nif_version_#{@nif_version.major}_#{@nif_version.minor}"
end

def has_nif_version(version) do
req = Version.parse_requirement!("~> #{version}")
Version.match?(@nif_version, req)
end
end
end

defmodule RustlerTest do
use Rustler,
otp_app: :rustler_test,
crate: :rustler_test,
features: [RustlerTest.Helper.nif_feature_from_running_version()]
features: [Helper.nif_feature_from_running_version()]

defp err, do: :erlang.nif_error(:nif_not_loaded)

Expand Down Expand Up @@ -145,4 +147,8 @@ defmodule RustlerTest do
def greeting_person_from_tuple(_tuple), do: err()

def append_to_path(_path, _to_append), do: err()

if Helper.has_nif_version("2.16") do
def perform_dyncall(_res, _a, _b, _c), do: err()
end
end
11 changes: 11 additions & 0 deletions rustler_tests/native/resource_dyncall/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "resource_dyncall"
version = "0.1.0"
edition = "2021"

[lib]
name = "resource_dyncall"
crate-type = ["cdylib"]

[dependencies]
rustler = { path = "../../../rustler", features = ["allocator", "nif_version_2_16"] }
32 changes: 32 additions & 0 deletions rustler_tests/native/resource_dyncall/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use rustler::{Env, LocalPid, Resource, ResourceArc};

#[repr(C)]
struct Params {
a: i64,
b: i64,
c: i64,
sent_to: Option<LocalPid>,
}

struct ResourceWithDyncall {
pid: LocalPid,
}

#[rustler::resource_impl(name = "resource_with_dyncall")]
impl Resource for ResourceWithDyncall {
unsafe fn dyncall<'a>(&'a self, env: Env<'a>, call_data: *mut rustler::sys::c_void) {
let p = &mut *(call_data as *mut Params);
if let Ok(()) = env.send(&self.pid, (p.a, p.b, p.c)) {
p.sent_to = Some(self.pid);
} else {
p.sent_to = None
}
}
}

#[rustler::nif]
fn new(pid: LocalPid) -> ResourceArc<ResourceWithDyncall> {
ResourceWithDyncall { pid }.into()
}

rustler::init!("Elixir.ResourceDyncall");
2 changes: 2 additions & 0 deletions rustler_tests/native/rustler_test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ mod test_atom;
mod test_binary;
mod test_codegen;
mod test_dirty;
#[cfg(feature = "nif_version_2_16")]
mod test_dyncall;
mod test_env;
mod test_error;
mod test_list;
Expand Down
37 changes: 37 additions & 0 deletions rustler_tests/native/rustler_test/src/test_dyncall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use rustler::{Env, LocalPid, Term};

rustler::atoms! {
module = "Elixir.ResourceDyncall",
resource_name = "resource_with_dyncall",
}

#[repr(C)]
struct Params {
a: i64,
b: i64,
c: i64,
sent_to: Option<LocalPid>,
}

#[rustler::nif]
pub fn perform_dyncall<'a>(
env: Env<'a>,
resource: Term<'a>,
a: i64,
b: i64,
c: i64,
) -> Option<LocalPid> {
let mut params = Params {
a,
b,
c,
sent_to: None,
};

unsafe {
let params_ptr = std::ptr::addr_of_mut!(params) as *mut rustler::sys::c_void;
let _ = env.dynamic_resource_call(module(), resource_name(), resource, params_ptr);
}

params.sent_to
}
22 changes: 22 additions & 0 deletions rustler_tests/test/resource_dyncall_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
if RustlerTest.Helper.has_nif_version("2.16") do
defmodule RustlerTest.ResourceDyncallTest do
use ExUnit.Case, async: true

test "perform dyncall" do
pid = self()

res = ResourceDyncall.new(pid)

call_res = RustlerTest.perform_dyncall(res, 1, 2, 3)

assert call_res == pid

receive do
{1, 2, 3} -> true
after
50 ->
raise "fail"
end
end
end
end
3 changes: 3 additions & 0 deletions rustler_tests/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Raise an error if RustlerTest can't be loaded
Code.ensure_loaded!(RustlerTest)

ExUnit.start()

defmodule SerdeRustlerTests.Helpers do
Expand Down

0 comments on commit 70c426a

Please sign in to comment.