feat: add Java Platform, e2e test stubs
This commit is contained in:
parent
f00dd85502
commit
af1f249d9a
13
build.sbt
13
build.sbt
@ -26,6 +26,14 @@ lazy val root = project
|
||||
"java.sources" -> baseDirectory.value.toGlob / "src" / "main" / "java" / ** / "*.java",
|
||||
"glue.sources" -> baseDirectory.value.toGlob / "glue" / "src" / ** / "*.rs"
|
||||
),
|
||||
path := Map(
|
||||
"glue.base" -> (baseDirectory.value / "glue").toString,
|
||||
"glue.target" -> (baseDirectory.value / "target" / "glue" / "debug").toString,
|
||||
"java.classTarget" -> (baseDirectory.value / "target" / "scala-3.2.2" / "classes").toString
|
||||
),
|
||||
Test / javaOptions ++= Seq(
|
||||
"-Djava.library.path="++path.value("glue.target"),
|
||||
),
|
||||
Compile / doc / javacOptions ++= Seq(
|
||||
"--enable-preview",
|
||||
"--release",
|
||||
@ -38,11 +46,6 @@ lazy val root = project
|
||||
"-Xlint:unchecked",
|
||||
"-Xlint:deprecation"
|
||||
),
|
||||
path := Map(
|
||||
"glue.base" -> (baseDirectory.value / "glue").toString,
|
||||
"glue.target" -> (baseDirectory.value / "target" / "glue" / "debug").toString,
|
||||
"java.classTarget" -> (baseDirectory.value / "target" / "scala-3.2.2" / "classes").toString
|
||||
),
|
||||
ejectHeaders := {
|
||||
val files =
|
||||
FileTreeView.default.iterator(glob.value("java.sources")).foldLeft("") {
|
||||
|
8
glue/Cargo.lock
generated
8
glue/Cargo.lock
generated
@ -570,6 +570,8 @@ name = "toad-java-glue"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"log",
|
||||
"naan",
|
||||
"nb",
|
||||
"no-std-net",
|
||||
"tinyvec",
|
||||
@ -580,10 +582,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toad-jni"
|
||||
version = "0.9.1"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef61b22c3478d481635b9eb67496d4dc18f22b8321961aca5dc2084f5253ccb7"
|
||||
checksum = "8e6f9b7ff8462ec97df69ef2bb01b485626fb43ce62a8fe33c6179f3537a9d38"
|
||||
dependencies = [
|
||||
"embedded-time",
|
||||
"jni",
|
||||
"nb",
|
||||
"no-std-net",
|
||||
@ -591,6 +594,7 @@ dependencies = [
|
||||
"toad",
|
||||
"toad-array 0.5.0",
|
||||
"toad-len",
|
||||
"toad-msg",
|
||||
"toad-stem",
|
||||
]
|
||||
|
||||
|
@ -15,7 +15,9 @@ e2e = []
|
||||
jni = "0.21.1"
|
||||
nb = "1"
|
||||
toad = "0.17.3"
|
||||
toad-jni = "0.9.1"
|
||||
toad-jni = "0.10.1"
|
||||
no-std-net = "0.6"
|
||||
toad-msg = "0.18.1"
|
||||
tinyvec = {version = "1.5", default_features = false, features = ["rustc_1_55"]}
|
||||
naan = "0.1.32"
|
||||
log = "0.4.17"
|
||||
|
@ -10,6 +10,7 @@ pub use retry_strategy::RetryStrategy;
|
||||
use toad::platform::Platform;
|
||||
use toad::retry::{Attempts, Strategy};
|
||||
use toad::time::Millis;
|
||||
use toad_jni::java::nio::channels::{DatagramChannel, PeekableDatagramChannel};
|
||||
use toad_jni::java::{self, Object};
|
||||
|
||||
use crate::mem::{Shared, SharedMemoryRegion};
|
||||
@ -18,9 +19,10 @@ use crate::Runtime;
|
||||
pub struct Toad(java::lang::Object);
|
||||
|
||||
impl Toad {
|
||||
pub fn new(e: &mut java::Env, cfg: Config) -> Self {
|
||||
static CTOR: java::Constructor<Toad, fn(Config)> = java::Constructor::new();
|
||||
CTOR.invoke(e, cfg)
|
||||
pub fn new(e: &mut java::Env, cfg: Config, channel: PeekableDatagramChannel) -> Self {
|
||||
static CTOR: java::Constructor<Toad, fn(Config, PeekableDatagramChannel)> =
|
||||
java::Constructor::new();
|
||||
CTOR.invoke(e, cfg, channel)
|
||||
}
|
||||
|
||||
pub fn poll_req(&self, e: &mut java::Env) -> Option<msg::ref_::Message> {
|
||||
@ -34,8 +36,8 @@ impl Toad {
|
||||
CONFIG.invoke(e, self)
|
||||
}
|
||||
|
||||
fn init_impl(e: &mut java::Env, cfg: Config) -> i64 {
|
||||
let r = || Runtime::try_new(cfg.addr(e), cfg.to_toad(e)).unwrap();
|
||||
fn init_impl(e: &mut java::Env, cfg: Config, channel: PeekableDatagramChannel) -> i64 {
|
||||
let r = || Runtime::new(cfg.to_toad(e), channel);
|
||||
unsafe { crate::mem::Shared::init(r).addr() as i64 }
|
||||
}
|
||||
|
||||
@ -49,7 +51,8 @@ impl Toad {
|
||||
},
|
||||
| Err(nb::Error::WouldBlock) => java::util::Optional::<msg::ref_::Message>::empty(e),
|
||||
| Err(nb::Error::Other(err)) => {
|
||||
e.throw(format!("{:?}", err)).unwrap();
|
||||
let err = err.downcast_ref(e).to_local(e);
|
||||
e.throw(jni::objects::JThrowable::from(err)).unwrap();
|
||||
java::util::Optional::<msg::ref_::Message>::empty(e)
|
||||
},
|
||||
}
|
||||
@ -70,11 +73,6 @@ impl java::Class for Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn addr(&self, e: &mut java::Env) -> SocketAddr {
|
||||
static ADDRESS: java::Field<Config, java::net::InetSocketAddress> = java::Field::new("addr");
|
||||
ADDRESS.get(e, self).to_std(e)
|
||||
}
|
||||
|
||||
pub fn concurrency(&self, e: &mut java::Env) -> u8 {
|
||||
static RUNTIME_CONFIG_CONCURRENCY: java::Field<Config, ffi::u8> =
|
||||
java::Field::new("concurrency");
|
||||
@ -86,9 +84,8 @@ impl Config {
|
||||
RUNTIME_CONFIG_MSG.invoke(e, self)
|
||||
}
|
||||
|
||||
pub fn new(e: &mut java::Env, c: toad::config::Config, addr: SocketAddr) -> Self {
|
||||
static CTOR: java::Constructor<Config, fn(java::net::InetSocketAddress, ffi::u8, Msg)> =
|
||||
java::Constructor::new();
|
||||
pub fn new(e: &mut java::Env, c: toad::config::Config) -> Self {
|
||||
static CTOR: java::Constructor<Config, fn(ffi::u8, Msg)> = java::Constructor::new();
|
||||
|
||||
let con = Con::new(e,
|
||||
c.msg.con.unacked_retry_strategy,
|
||||
@ -104,9 +101,7 @@ impl Config {
|
||||
|
||||
let concurrency = ffi::u8::from_rust(e, c.max_concurrent_requests);
|
||||
|
||||
let address = java::net::InetSocketAddress::from_std(e, addr);
|
||||
|
||||
let jcfg = CTOR.invoke(e, address, concurrency, msg);
|
||||
let jcfg = CTOR.invoke(e, concurrency, msg);
|
||||
jcfg
|
||||
}
|
||||
|
||||
@ -264,20 +259,20 @@ impl Non {
|
||||
pub extern "system" fn Java_dev_toad_Toad_defaultConfigImpl<'local>(mut env: java::Env<'local>,
|
||||
_: JClass<'local>)
|
||||
-> jobject {
|
||||
Config::new(&mut env,
|
||||
toad::config::Config::default(),
|
||||
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 5683)).yield_to_java(&mut env)
|
||||
Config::new(&mut env, toad::config::Config::default()).yield_to_java(&mut env)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_dev_toad_Toad_init<'local>(mut e: java::Env<'local>,
|
||||
_: JClass<'local>,
|
||||
channel: JObject<'local>,
|
||||
cfg: JObject<'local>)
|
||||
-> i64 {
|
||||
let e = &mut e;
|
||||
let cfg = java::lang::Object::from_local(e, cfg).upcast_to::<Config>(e);
|
||||
let channel = java::lang::Object::from_local(e, channel).upcast_to::<DatagramChannel>(e);
|
||||
|
||||
Toad::init_impl(e, cfg)
|
||||
Toad::init_impl(e, cfg, channel.peekable())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -119,10 +119,10 @@ pub extern "system" fn Java_dev_toad_msg_ref_Message_payload<'local>(mut env: ja
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_dev_toad_msg_ref_Message_type<'local>(mut e: java::Env<'local>,
|
||||
_: JClass<'local>,
|
||||
addr: i64)
|
||||
-> jobject {
|
||||
pub extern "system" fn Java_dev_toad_msg_ref_Message_typ<'local>(mut e: java::Env<'local>,
|
||||
_: JClass<'local>,
|
||||
addr: i64)
|
||||
-> jobject {
|
||||
let msg = unsafe {
|
||||
Shared::deref::<toad_msg::alloc::Message>(addr).as_ref()
|
||||
.unwrap()
|
||||
|
@ -1,18 +1,25 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use no_std_net::SocketAddr;
|
||||
use toad::config::Config;
|
||||
use toad::net::Addrd;
|
||||
use toad::platform::Platform;
|
||||
use toad_jni::java::net::StandardProtocolFamily::INet;
|
||||
use toad_jni::java::nio::channels::{DatagramChannel, PeekableDatagramChannel};
|
||||
use toad_jni::java::{self, Object, Signature};
|
||||
use toad_msg::alloc::Message;
|
||||
use toad_msg::{Code, Id, Token, Type};
|
||||
|
||||
use crate::{dev, Runtime};
|
||||
|
||||
type RustRuntime =
|
||||
toad::std::Platform<toad::std::dtls::N, toad::step::runtime::std::Runtime<toad::std::dtls::N>>;
|
||||
|
||||
#[non_exhaustive]
|
||||
struct State {
|
||||
pub runtime: dev::toad::Toad,
|
||||
pub env: java::Env<'static>,
|
||||
pub client: crate::Runtime,
|
||||
pub client: RustRuntime,
|
||||
pub srv_addr: SocketAddr,
|
||||
}
|
||||
|
||||
@ -20,17 +27,15 @@ fn init() -> State {
|
||||
let mut _env = crate::test::init();
|
||||
let env = &mut _env;
|
||||
|
||||
let cfg =
|
||||
dev::toad::Config::new(env,
|
||||
Config::default(),
|
||||
std::net::SocketAddr::new(std::net::Ipv4Addr::UNSPECIFIED.into(), 5683));
|
||||
let runtime = dev::toad::Toad::new(env, cfg);
|
||||
let client = Runtime::try_new("0.0.0.0:5684", Default::default()).unwrap();
|
||||
let cfg = dev::toad::Config::new(env, Config::default());
|
||||
let sock = <PeekableDatagramChannel as toad::net::Socket>::bind(no_std_net::SocketAddr::new(no_std_net::Ipv4Addr::LOCALHOST.into(), 5683)).unwrap();
|
||||
let runtime = dev::toad::Toad::new(env, cfg, sock);
|
||||
let client = RustRuntime::try_new("127.0.0.1:5684", Default::default()).unwrap();
|
||||
|
||||
State { runtime,
|
||||
env: _env,
|
||||
client,
|
||||
srv_addr: "0.0.0.0:5683".parse().unwrap() }
|
||||
srv_addr: "127.0.0.1:5683".parse().unwrap() }
|
||||
}
|
||||
|
||||
fn runtime_poll_req(State { runtime,
|
||||
@ -43,7 +48,14 @@ fn runtime_poll_req(State { runtime,
|
||||
let request = Message::new(Type::Con, Code::GET, Id(0), Token(Default::default()));
|
||||
client.send_msg(Addrd(request, *srv_addr)).unwrap();
|
||||
|
||||
assert!(runtime.poll_req(env).is_some());
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if Instant::now() - start > Duration::from_millis(10000) {
|
||||
panic!("timed out waiting for DatagramChannel to receive message");
|
||||
} else if runtime.poll_req(env).is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -8,11 +8,16 @@ use mem::SharedMemoryRegion;
|
||||
mod runtime {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use toad::platform::Effect;
|
||||
use toad::std::{dtls, Platform};
|
||||
use toad::step::runtime::std::Runtime as DefaultSteps;
|
||||
use toad::config::Config;
|
||||
use toad::net::Addrd;
|
||||
use toad::platform::{Effect, Platform};
|
||||
use toad::req::Req;
|
||||
use toad::resp::Resp;
|
||||
use toad::step::runtime::Runtime as DefaultSteps;
|
||||
use toad_jni::java::io::IOException;
|
||||
use toad_jni::java::lang::Throwable;
|
||||
use toad_jni::java::nio::channels::PeekableDatagramChannel;
|
||||
use toad_msg::{OptValue, OptNumber};
|
||||
use toad_msg::{OptNumber, OptValue};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PlatformTypes;
|
||||
@ -27,7 +32,49 @@ mod runtime {
|
||||
type Effects = Vec<Effect<Self>>;
|
||||
}
|
||||
|
||||
pub type Runtime = Platform<dtls::N, DefaultSteps<dtls::N>>;
|
||||
type Steps = DefaultSteps<PlatformTypes, naan::hkt::Vec, naan::hkt::BTreeMap>;
|
||||
|
||||
pub struct Runtime {
|
||||
steps: Steps,
|
||||
config: Config,
|
||||
channel: PeekableDatagramChannel,
|
||||
clock: toad::std::Clock,
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
pub fn new(config: Config, channel: PeekableDatagramChannel) -> Self {
|
||||
Self { steps: Default::default(),
|
||||
config,
|
||||
channel,
|
||||
clock: toad::std::Clock::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform<Steps> for Runtime {
|
||||
type Types = PlatformTypes;
|
||||
type Error = IOException;
|
||||
|
||||
fn log(&self, level: log::Level, msg: toad::todo::String<1000>) -> Result<(), Self::Error> {
|
||||
println!("[{}]: {}", level, msg.as_str());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn config(&self) -> toad::config::Config {
|
||||
self.config
|
||||
}
|
||||
|
||||
fn steps(&self) -> &Steps {
|
||||
&self.steps
|
||||
}
|
||||
|
||||
fn socket(&self) -> &PeekableDatagramChannel {
|
||||
&self.channel
|
||||
}
|
||||
|
||||
fn clock(&self) -> &toad::std::Clock {
|
||||
&self.clock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use runtime::Runtime;
|
||||
@ -59,21 +106,35 @@ pub mod e2e;
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::Once;
|
||||
|
||||
use jni::{InitArgsBuilder, JavaVM};
|
||||
use toad::config::Config;
|
||||
use toad::retry::Strategy;
|
||||
use toad::time::Millis;
|
||||
use toad_jni::java;
|
||||
use toad_jni::java::{self, Class, ResultExt};
|
||||
|
||||
use crate::dev;
|
||||
|
||||
pub fn init<'a>() -> java::Env<'a> {
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
let repo_root = Command::new("git").arg("rev-parse")
|
||||
.arg("--show-toplevel")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(repo_root.status.success());
|
||||
|
||||
let lib_path = String::from_utf8(repo_root.stdout).unwrap()
|
||||
.trim()
|
||||
.to_string();
|
||||
let lib_path = PathBuf::from(lib_path).join("target/glue/debug");
|
||||
|
||||
let jvm =
|
||||
JavaVM::new(InitArgsBuilder::new().option("-Djava.library.path=/home/orion/src/toad-lib/toad-java/target/glue/debug/")
|
||||
JavaVM::new(InitArgsBuilder::new().option(format!("-Djava.library.path={}",
|
||||
lib_path.to_string_lossy()))
|
||||
.option("-Djava.class.path=../target/scala-3.2.2/classes")
|
||||
.option("--enable-preview")
|
||||
.build()
|
||||
@ -81,8 +142,13 @@ pub mod test {
|
||||
toad_jni::global::init_with(jvm);
|
||||
});
|
||||
|
||||
toad_jni::global::jvm().attach_current_thread_permanently()
|
||||
.unwrap()
|
||||
let mut env = toad_jni::global::jvm().attach_current_thread_permanently()
|
||||
.unwrap();
|
||||
|
||||
env.call_static_method(crate::dev::toad::Toad::PATH, "loadNativeLib", "()V", &[])
|
||||
.unwrap_java(&mut env);
|
||||
|
||||
env
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -97,9 +163,7 @@ pub mod test {
|
||||
let mut e = init();
|
||||
let e = &mut e;
|
||||
|
||||
let r = dev::toad::Config::new(e,
|
||||
Config::default(),
|
||||
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 5683));
|
||||
let r = dev::toad::Config::new(e, Config::default());
|
||||
assert_eq!(r.to_toad(e), Config::default());
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,10 @@ public final class Toad implements AutoCloseable {
|
||||
}
|
||||
|
||||
static {
|
||||
Toad.loadNativeLib();
|
||||
}
|
||||
|
||||
static void loadNativeLib() {
|
||||
System.loadLibrary("toad_java_glue");
|
||||
}
|
||||
|
||||
@ -32,25 +36,25 @@ public final class Toad implements AutoCloseable {
|
||||
final Config config;
|
||||
final DatagramChannel channel;
|
||||
|
||||
static native long init(Config o);
|
||||
static native long init(DatagramChannel chan, Config o);
|
||||
|
||||
native Optional<dev.toad.msg.ref.Message> pollReq(long ptr);
|
||||
static native Optional<dev.toad.msg.ref.Message> pollReq(long ptr);
|
||||
|
||||
native Optional<dev.toad.msg.ref.Message> pollResp(
|
||||
static native Optional<dev.toad.msg.ref.Message> pollResp(
|
||||
long ptr,
|
||||
dev.toad.msg.Token t,
|
||||
InetSocketAddress n
|
||||
);
|
||||
|
||||
Optional<dev.toad.msg.ref.Message> pollReq() {
|
||||
return this.pollReq(this.ptr.addr());
|
||||
public Optional<dev.toad.msg.ref.Message> pollReq() {
|
||||
return Toad.pollReq(this.ptr.addr());
|
||||
}
|
||||
|
||||
Optional<dev.toad.msg.ref.Message> pollResp(
|
||||
public Optional<dev.toad.msg.ref.Message> pollResp(
|
||||
dev.toad.msg.Token regarding,
|
||||
InetSocketAddress from
|
||||
) {
|
||||
return this.pollResp(this.ptr.addr(), regarding, from);
|
||||
return Toad.pollResp(this.ptr.addr(), regarding, from);
|
||||
}
|
||||
|
||||
public static BuilderRequiresSocket builder() {
|
||||
@ -60,7 +64,7 @@ public final class Toad implements AutoCloseable {
|
||||
Toad(Config o, DatagramChannel channel) {
|
||||
this.config = o;
|
||||
this.channel = channel;
|
||||
this.ptr = Ptr.register(this.getClass(), this.init(o));
|
||||
this.ptr = Ptr.register(this.getClass(), this.init(this.channel, o));
|
||||
}
|
||||
|
||||
public Config config() {
|
||||
|
@ -30,7 +30,7 @@ public final class Message implements dev.toad.msg.Message, AutoCloseable {
|
||||
|
||||
static native Code code(long addr);
|
||||
|
||||
static native Type type(long addr);
|
||||
static native Type typ(long addr);
|
||||
|
||||
static native dev.toad.msg.ref.Option[] opts(long addr);
|
||||
|
||||
@ -63,7 +63,7 @@ public final class Message implements dev.toad.msg.Message, AutoCloseable {
|
||||
}
|
||||
|
||||
public Type type() {
|
||||
return this.type(this.ptr.addr());
|
||||
return this.typ(this.ptr.addr());
|
||||
}
|
||||
|
||||
public dev.toad.msg.ref.Option[] optionRefs() {
|
||||
|
@ -3,6 +3,8 @@ import mock.java.nio.channels.Mock;
|
||||
|
||||
class E2E extends munit.FunSuite {
|
||||
test("foo") {
|
||||
val mock = Mock.Channel();
|
||||
val mock = Mock.Channel()
|
||||
val toad = Toad.builder.channel(mock).build
|
||||
val req = Option.apply(toad.pollReq.get)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user