gui: add EguiBox TypeBox plugin (Windows egui stub)\n\n- plugins: add nyash-egui-plugin with TypeBox (resolve/invoke_id), Windows path for real window via eframe; stub on other OS\n- apps: add apps/egui-hello sample (open→uiLabel→run→close)\n- loader: improve Windows DLL resolution (target triples: x86_64/aarch64 msvc) and lib→dll mapping\n- tests: expand TypeBox vs TLV diff tests up to FileBox; all green\n- docs: update CURRENT_TASK checklist (diff tests completed)\n- config: nyash.toml add EguiBox (type_id=70), plugin registry and methods

This commit is contained in:
Moe Charm
2025-09-03 13:58:52 +09:00
parent f939ad0033
commit ceb22b6c18
33 changed files with 3891 additions and 84 deletions

View File

@ -7,3 +7,5 @@ pub mod identical_exec_instance;
pub mod vtable_array_string;
pub mod vtable_strict;
pub mod host_reverse_slot;
pub mod nyash_abi_basic;
pub mod typebox_tlv_diff;

View File

@ -0,0 +1,84 @@
#[cfg(test)]
mod tests {
use crate::runtime::type_registry::{resolve_slot_by_name, known_methods_for};
#[test]
fn type_registry_resolves_core_slots() {
// MapBox
assert_eq!(resolve_slot_by_name("MapBox", "size", 0), Some(200));
assert_eq!(resolve_slot_by_name("MapBox", "len", 0), Some(201));
assert_eq!(resolve_slot_by_name("MapBox", "has", 1), Some(202));
assert_eq!(resolve_slot_by_name("MapBox", "get", 1), Some(203));
assert_eq!(resolve_slot_by_name("MapBox", "set", 2), Some(204));
// ArrayBox
assert_eq!(resolve_slot_by_name("ArrayBox", "get", 1), Some(100));
assert_eq!(resolve_slot_by_name("ArrayBox", "set", 2), Some(101));
assert_eq!(resolve_slot_by_name("ArrayBox", "len", 0), Some(102));
// StringBox
assert_eq!(resolve_slot_by_name("StringBox", "len", 0), Some(300));
// Known methods listing should include representative entries
let mm = known_methods_for("MapBox").expect("map methods");
assert!(mm.contains(&"size"));
assert!(mm.contains(&"get"));
assert!(mm.contains(&"set"));
}
#[test]
#[ignore]
fn vm_vtable_map_set_get_has() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, EffectMask, ConstValue, MirType, ValueId};
// Enable vtable-preferred path
std::env::set_var("NYASH_ABI_VTABLE", "1");
// Program: m = new MapBox(); m.set("k","v"); h = m.has("k"); g = m.get("k"); return g
let mut m = MirModule::new("nyash_abi_map_get".into());
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let mapv = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] });
let k = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) });
let v = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("v".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mapv, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE });
let k2 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k2, value: ConstValue::String("k".into()) });
let hasv = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(hasv), box_val: mapv, method: "has".into(), args: vec![k2], method_id: None, effects: EffectMask::PURE });
let k3 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k3, value: ConstValue::String("k".into()) });
let got = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: mapv, method: "get".into(), args: vec![k3], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) });
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "v");
}
#[test]
fn mapbox_keys_values_return_arrays() {
// Direct Box-level test (not via VM): keys()/values() should return ArrayBox
use crate::boxes::map_box::MapBox;
use crate::box_trait::{NyashBox, StringBox, IntegerBox};
let map = MapBox::new();
map.set(Box::new(StringBox::new("a")), Box::new(IntegerBox::new(1)));
map.set(Box::new(StringBox::new("b")), Box::new(IntegerBox::new(2)));
let keys = map.keys();
let values = map.values();
assert_eq!(keys.type_name(), "ArrayBox");
assert_eq!(values.type_name(), "ArrayBox");
}
}

View File

@ -0,0 +1,433 @@
#[cfg(test)]
mod tests {
use std::env;
use crate::box_trait::{NyashBox, StringBox, IntegerBox};
use crate::boxes::math_box::FloatBox;
use crate::boxes::array::ArrayBox;
use std::fs;
use std::path::PathBuf;
fn ensure_host() {
let _ = crate::runtime::init_global_plugin_host("nyash.toml");
}
fn create_plugin_instance(box_type: &str) -> (String, u32, Box<dyn NyashBox>) {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
let bx = host.create_box(box_type, &[]).expect("create_box");
// Downcast to PluginBoxV2 to get instance_id
if let Some(p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
(box_type.to_string(), p.instance_id(), bx)
} else {
panic!("not a plugin box: {}", bx.type_name());
}
}
#[test]
fn mapbox_get_set_size_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path: disable typebox
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("MapBox");
let out_tlv = {
let h = host.read().unwrap();
// set("k", 42)
let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]).expect("set tlv");
// size()
let sz = h.invoke_instance_method(&bt1, "size", id1, &[]).expect("size tlv").unwrap();
// get("k")
let gv = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("k"))]).expect("get tlv").unwrap();
(sz.to_string_box().value, gv.to_string_box().value)
};
// TypeBox path: enable typebox
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("MapBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]).expect("set tb");
let sz = h.invoke_instance_method(&bt2, "size", id2, &[]).expect("size tb").unwrap();
let gv = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("k"))]).expect("get tb").unwrap();
(sz.to_string_box().value, gv.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match");
}
#[test]
fn arraybox_set_get_len_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("ArrayBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]).expect("set tlv");
let ln = h.invoke_instance_method(&bt1, "len", id1, &[]).expect("len tlv").unwrap();
let gv = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(IntegerBox::new(0))]).expect("get tlv").unwrap();
(ln.to_string_box().value, gv.to_string_box().value)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("ArrayBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]).expect("set tb");
let ln = h.invoke_instance_method(&bt2, "length", id2, &[]).expect("len tb").unwrap();
let gv = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(IntegerBox::new(0))]).expect("get tb").unwrap();
(ln.to_string_box().value, gv.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (ArrayBox)");
}
#[test]
fn stringbox_len_concat_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("StringBox");
let out_tlv = {
let h = host.read().unwrap();
// birth with init string: use fromUtf8 via set of arg in create? Current loader birth() no-arg, so concat
let _ = h.invoke_instance_method(&bt1, "concat", id1, &[Box::new(StringBox::new("ab"))]).expect("concat tlv").unwrap();
let ln = h.invoke_instance_method(&bt1, "length", id1, &[]).expect("len tlv").unwrap();
(ln.to_string_box().value)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("StringBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "concat", id2, &[Box::new(StringBox::new("ab"))]).expect("concat tb").unwrap();
let ln = h.invoke_instance_method(&bt2, "length", id2, &[]).expect("len tb").unwrap();
(ln.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (StringBox)");
}
#[test]
fn integerbox_get_set_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("IntegerBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(123))]).expect("set tlv").unwrap();
let gv = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get tlv").unwrap();
gv.to_string_box().value
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("IntegerBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(123))]).expect("set tb").unwrap();
let gv = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get tb").unwrap();
gv.to_string_box().value
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (IntegerBox)");
}
#[test]
fn consolebox_println_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("ConsoleBox");
let out_tlv_is_none = {
let h = host.read().unwrap();
let rv = h.invoke_instance_method(&bt1, "println", id1, &[Box::new(StringBox::new("hello"))]).expect("println tlv");
rv.is_none()
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("ConsoleBox");
let out_tb_is_none = {
let h = host.read().unwrap();
let rv = h.invoke_instance_method(&bt2, "println", id2, &[Box::new(StringBox::new("hello"))]).expect("println tb");
rv.is_none()
};
assert!(out_tlv_is_none && out_tb_is_none, "println should return void/None in both modes");
}
#[test]
fn mathbox_basic_ops_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("MathBox");
let out_tlv = {
let h = host.read().unwrap();
let s1 = h.invoke_instance_method(&bt1, "sqrt", id1, &[Box::new(IntegerBox::new(9))]).expect("sqrt tlv").unwrap();
let s2 = h.invoke_instance_method(&bt1, "sin", id1, &[Box::new(IntegerBox::new(0))]).expect("sin tlv").unwrap();
let s3 = h.invoke_instance_method(&bt1, "cos", id1, &[Box::new(IntegerBox::new(0))]).expect("cos tlv").unwrap();
let s4 = h.invoke_instance_method(&bt1, "round", id1, &[Box::new(IntegerBox::new(26))]).expect("round tlv").unwrap();
(
s1.to_string_box().value,
s2.to_string_box().value,
s3.to_string_box().value,
s4.to_string_box().value,
)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("MathBox");
let out_tb = {
let h = host.read().unwrap();
let s1 = h.invoke_instance_method(&bt2, "sqrt", id2, &[Box::new(IntegerBox::new(9))]).expect("sqrt tb").unwrap();
let s2 = h.invoke_instance_method(&bt2, "sin", id2, &[Box::new(IntegerBox::new(0))]).expect("sin tb").unwrap();
let s3 = h.invoke_instance_method(&bt2, "cos", id2, &[Box::new(IntegerBox::new(0))]).expect("cos tb").unwrap();
let s4 = h.invoke_instance_method(&bt2, "round", id2, &[Box::new(IntegerBox::new(26))]).expect("round tb").unwrap();
(
s1.to_string_box().value,
s2.to_string_box().value,
s3.to_string_box().value,
s4.to_string_box().value,
)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (MathBox)");
}
#[test]
fn encodingbox_base64_hex_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// Prepare bytes ["hi"] as Array<uint8>
let bytes_array = {
let arr = ArrayBox::new();
let _ = arr.push(Box::new(IntegerBox::new(104))); // 'h'
let _ = arr.push(Box::new(IntegerBox::new(105))); // 'i'
Box::new(arr) as Box<dyn NyashBox>
};
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("EncodingBox");
let out_tlv = {
let h = host.read().unwrap();
let b64 = h.invoke_instance_method(&bt1, "base64Encode", id1, &[Box::new(StringBox::new("hi"))]).expect("b64 tlv").unwrap();
let hex = h.invoke_instance_method(&bt1, "hexEncode", id1, &[Box::new(StringBox::new("hi"))]).expect("hex tlv").unwrap();
(b64.to_string_box().value, hex.to_string_box().value)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("EncodingBox");
let out_tb = {
let h = host.read().unwrap();
let b64 = h.invoke_instance_method(&bt2, "base64Encode", id2, &[Box::new(StringBox::new("hi"))]).expect("b64 tb").unwrap();
let hex = h.invoke_instance_method(&bt2, "hexEncode", id2, &[Box::new(StringBox::new("hi"))]).expect("hex tb").unwrap();
(b64.to_string_box().value, hex.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (EncodingBox)");
}
#[test]
fn regexbox_is_match_find_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("RegexBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))]).expect("compile tlv");
let m = h.invoke_instance_method(&bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))]).expect("isMatch tlv").unwrap();
let f = h.invoke_instance_method(&bt1, "find", id1, &[Box::new(StringBox::new("hello"))]).expect("find tlv").unwrap();
(m.to_string_box().value, f.to_string_box().value)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("RegexBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))]).expect("compile tb");
let m = h.invoke_instance_method(&bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))]).expect("isMatch tb").unwrap();
let f = h.invoke_instance_method(&bt2, "find", id2, &[Box::new(StringBox::new("hello"))]).expect("find tb").unwrap();
(m.to_string_box().value, f.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (RegexBox)");
}
#[test]
fn pathbox_ops_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("PathBox");
let out_tlv = {
let h = host.read().unwrap();
let j = h.invoke_instance_method(&bt1, "join", id1, &[Box::new(StringBox::new("/a/b")), Box::new(StringBox::new("c.txt"))]).expect("join tlv").unwrap();
let d = h.invoke_instance_method(&bt1, "dirname", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("dirname tlv").unwrap();
let b = h.invoke_instance_method(&bt1, "basename", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("basename tlv").unwrap();
let n = h.invoke_instance_method(&bt1, "normalize", id1, &[Box::new(StringBox::new("/a/./b/../b/c"))]).expect("normalize tlv").unwrap();
(j.to_string_box().value, d.to_string_box().value, b.to_string_box().value, n.to_string_box().value)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("PathBox");
let out_tb = {
let h = host.read().unwrap();
let j = h.invoke_instance_method(&bt2, "join", id2, &[Box::new(StringBox::new("/a/b")), Box::new(StringBox::new("c.txt"))]).expect("join tb").unwrap();
let d = h.invoke_instance_method(&bt2, "dirname", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("dirname tb").unwrap();
let b = h.invoke_instance_method(&bt2, "basename", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("basename tb").unwrap();
let n = h.invoke_instance_method(&bt2, "normalize", id2, &[Box::new(StringBox::new("/a/./b/../b/c"))]).expect("normalize tb").unwrap();
(j.to_string_box().value, d.to_string_box().value, b.to_string_box().value, n.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (PathBox)");
}
#[test]
fn tomlbox_parse_get_tojson_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
let toml_text = "[package]\nname=\"nyash\"\n[deps]\nregex=\"1\"\n";
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("TOMLBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "parse", id1, &[Box::new(StringBox::new(toml_text))]).expect("parse tlv").unwrap();
let name = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("package.name"))]).expect("get tlv").unwrap();
let json = h.invoke_instance_method(&bt1, "toJson", id1, &[]).expect("toJson tlv").unwrap();
(name.to_string_box().value, json.to_string_box().value)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("TOMLBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "parse", id2, &[Box::new(StringBox::new(toml_text))]).expect("parse tb").unwrap();
let name = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("package.name"))]).expect("get tb").unwrap();
let json = h.invoke_instance_method(&bt2, "toJson", id2, &[]).expect("toJson tb").unwrap();
(name.to_string_box().value, json.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (TOMLBox)");
}
#[test]
fn timebox_now_tlv_vs_typebox_with_tolerance() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("TimeBox");
let t_tlv = {
let h = host.read().unwrap();
let v = h.invoke_instance_method(&bt1, "now", id1, &[]).expect("now tlv").unwrap();
v.to_string_box().value.parse::<i64>().unwrap_or(0)
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("TimeBox");
let t_tb = {
let h = host.read().unwrap();
let v = h.invoke_instance_method(&bt2, "now", id2, &[]).expect("now tb").unwrap();
v.to_string_box().value.parse::<i64>().unwrap_or(0)
};
let diff = (t_tb - t_tlv).abs();
assert!(diff < 5_000, "TimeBox.now difference too large: {}ms", diff);
}
#[test]
fn counterbox_singleton_delta_increments() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// TLV path: verify get->inc->get increases by 1
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("CounterBox");
let (a1, b1) = {
let h = host.read().unwrap();
let a = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get tlv").unwrap();
let _ = h.invoke_instance_method(&bt1, "inc", id1, &[]).expect("inc tlv");
let b = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get2 tlv").unwrap();
(a.to_string_box().value.parse::<i64>().unwrap_or(0), b.to_string_box().value.parse::<i64>().unwrap_or(0))
};
assert_eq!(b1 - a1, 1, "CounterBox TLV should increment by 1");
// TypeBox path: verify same delta behavior (not comparing absolute values due to singleton)
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("CounterBox");
let (a2, b2) = {
let h = host.read().unwrap();
let a = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get tb").unwrap();
let _ = h.invoke_instance_method(&bt2, "inc", id2, &[]).expect("inc tb");
let b = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get2 tb").unwrap();
(a.to_string_box().value.parse::<i64>().unwrap_or(0), b.to_string_box().value.parse::<i64>().unwrap_or(0))
};
assert_eq!(b2 - a2, 1, "CounterBox TypeBox should increment by 1");
}
#[test]
fn filebox_rw_close_tmpdir_tlv_vs_typebox() {
ensure_host();
let host = crate::runtime::get_global_plugin_host();
// Prepare temp file path
let mut p = std::env::temp_dir();
p.push(format!("nyash_test_{}_{}.txt", std::process::id(), rand_id())) ;
let path_str = p.to_string_lossy().to_string();
// TLV path
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
let (bt1, id1, _hold1) = create_plugin_instance("FileBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "open", id1, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("w"))]).expect("open tlv");
let _ = h.invoke_instance_method(&bt1, "write", id1, &[Box::new(StringBox::new("hello"))]).expect("write tlv");
let _ = h.invoke_instance_method(&bt1, "close", id1, &[]).expect("close tlv");
// reopen and read
let _ = h.invoke_instance_method(&bt1, "open", id1, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("r"))]).expect("open2 tlv");
let rd = h.invoke_instance_method(&bt1, "read", id1, &[]).expect("read tlv").unwrap();
let _ = h.invoke_instance_method(&bt1, "close", id1, &[]).expect("close2 tlv");
rd.to_string_box().value
};
// TypeBox path
env::remove_var("NYASH_DISABLE_TYPEBOX");
let (bt2, id2, _hold2) = create_plugin_instance("FileBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "open", id2, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("w"))]).expect("open tb");
let _ = h.invoke_instance_method(&bt2, "write", id2, &[Box::new(StringBox::new("hello"))]).expect("write tb");
let _ = h.invoke_instance_method(&bt2, "close", id2, &[]).expect("close tb");
let _ = h.invoke_instance_method(&bt2, "open", id2, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("r"))]).expect("open2 tb");
let rd = h.invoke_instance_method(&bt2, "read", id2, &[]).expect("read tb").unwrap();
let _ = h.invoke_instance_method(&bt2, "close", id2, &[]).expect("close2 tb");
rd.to_string_box().value
};
// Cleanup best-effort
let _ = fs::remove_file(&path_str);
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (FileBox)");
}
fn rand_id() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
now.as_micros() as u64
}
}