extern crate mrubyedge;
mod helpers;
use helpers::*;
#[test]
fn module_definition_test() {
let script = r#"
module TestModule
def module_method
42
end
end
TestModule
"#;
let binary = mrbc_compile("module_def", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
assert!(matches!(result.tt, mrubyedge::yamrb::value::RType::Module));
}
#[test]
fn class_can_include_module_method() {
let script = r#"
module Printable
def greet
"hello"
end
end
class User
include Printable
end
User.new.greet
"#;
let binary = mrbc_compile("module_include", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: String = result
.as_ref()
.try_into()
.expect("greet should return string");
assert_eq!(value, "hello");
}
#[test]
fn modules_can_be_used_as_namespace() {
let script = r#"
module Outer
module Printable
def greet
"hello"
end
end
class User
include Printable
end
end
Outer::User.new.greet
"#;
let binary = mrbc_compile("module_include_ns", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: String = result
.as_ref()
.try_into()
.expect("greet should return string");
assert_eq!(value, "hello");
}
#[test]
fn module_can_include_module_method() {
let script = r#"
module Core
def core_value
123
end
end
module Superset
include Core
end
class Wrapper
include Superset
end
Wrapper.new.core_value
"#;
let binary = mrbc_compile("module_include_module", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result
.as_ref()
.try_into()
.expect("core_value should return integer");
assert_eq!(value, 123);
}
#[test]
fn module_can_include_module_method_2() {
let script = r#"
module Core
def core_value
123
end
end
module Superset
include Core
def core_value
super + 1
end
end
class Wrapper
include Superset
end
Wrapper.new.core_value
"#;
let binary = mrbc_compile("module_include_module_2", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result
.as_ref()
.try_into()
.expect("core_value should return integer");
assert_eq!(value, 124);
}
#[test]
fn nested_define_module_preserves_existing_methods() {
use mrubyedge::yamrb::helpers::mrb_define_module_cmethod;
use mrubyedge::yamrb::value::RObject;
use std::rc::Rc;
let mut vm = mrubyedge::yamrb::vm::VM::empty();
let outer = vm.define_module("Outer", None);
mrb_define_module_cmethod(
&mut vm,
outer.clone(),
"foo",
Box::new(|_vm, _args| Ok(RObject::integer(42).to_refcount_assigned())),
);
let inner = vm.define_module("Inner", Some(outer.clone()));
mrb_define_module_cmethod(
&mut vm,
inner.clone(),
"bar",
Box::new(|_vm, _args| Ok(RObject::integer(99).to_refcount_assigned())),
);
let outer2 = vm.define_module("Outer", None);
assert!(
Rc::ptr_eq(&outer, &outer2),
"define_module should return the existing module"
);
assert!(
outer2.procs.borrow().contains_key("foo"),
"existing cmethod 'foo' should be preserved after re-opening"
);
let inner2 = vm.define_module("Inner", Some(outer.clone()));
assert!(
Rc::ptr_eq(&inner, &inner2),
"nested define_module should return the existing module"
);
assert!(
inner2.procs.borrow().contains_key("bar"),
"existing cmethod 'bar' should be preserved after re-opening nested module"
);
}
#[test]
fn include_nested_module_and_call_method() {
use mrubyedge::yamrb::helpers::mrb_define_module_cmethod;
use mrubyedge::yamrb::value::RObject;
let mut vm = mrubyedge::yamrb::vm::VM::empty();
let outer = vm.define_module("Outer", None);
mrb_define_module_cmethod(
&mut vm,
outer.clone(),
"foo",
Box::new(|_vm, _args| Ok(RObject::integer(42).to_refcount_assigned())),
);
let inner = vm.define_module("Inner", Some(outer.clone()));
mrb_define_module_cmethod(
&mut vm,
inner.clone(),
"greet",
Box::new(|_vm, _args| {
Ok(RObject::string("hello from Inner".to_string()).to_refcount_assigned())
}),
);
let script = r#"
class MyClass
include Outer::Inner
end
MyClass.new.greet
"#;
let binary = mrbc_compile("include_nested_module", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let result = vm.eval_rite(&mut rite).unwrap();
let value: String = result
.as_ref()
.try_into()
.expect("greet should return string");
assert_eq!(value, "hello from Inner");
}
#[test]
fn setmcnst_module_const_test() {
let code = r#"
module M
end
M::X = 42
def setmcnst_module_const
M::X
end
"#;
let binary = mrbc_compile("setmcnst_module_const", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
vm.run().unwrap();
let args = vec![];
let result = mrb_funcall(&mut vm, None, "setmcnst_module_const", &args).unwrap();
let result: i64 = result.as_ref().try_into().unwrap();
assert_eq!(result, 42);
}
#[test]
fn setmcnst_overwrite_test() {
let code = r#"
module N
end
N::X = 1
N::X = 2
def setmcnst_overwrite
N::X
end
"#;
let binary = mrbc_compile("setmcnst_overwrite", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
vm.run().unwrap();
let args = vec![];
let result = mrb_funcall(&mut vm, None, "setmcnst_overwrite", &args).unwrap();
let result: i64 = result.as_ref().try_into().unwrap();
assert_eq!(result, 2);
}
#[test]
fn setmcnst_nested_path_test() {
let code = r#"
module X
module Y
end
end
X::Y::Z = 9
def setmcnst_nested_path
X::Y::Z
end
"#;
let binary = mrbc_compile("setmcnst_nested_path", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
vm.run().unwrap();
let args = vec![];
let result = mrb_funcall(&mut vm, None, "setmcnst_nested_path", &args).unwrap();
let result: i64 = result.as_ref().try_into().unwrap();
assert_eq!(result, 9);
}
#[test]
fn setmcnst_class_const_test() {
let code = r#"
class C
end
C::K = 5
def setmcnst_class_const
C::K
end
"#;
let binary = mrbc_compile("setmcnst_class_const", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
vm.run().unwrap();
let args = vec![];
let result = mrb_funcall(&mut vm, None, "setmcnst_class_const", &args).unwrap();
let result: i64 = result.as_ref().try_into().unwrap();
assert_eq!(result, 5);
}
#[test]
fn setmcnst_top_level_const_test() {
let code = r#"
::G = 7
def setmcnst_top_level
G
end
"#;
let binary = mrbc_compile("setmcnst_top_level", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
vm.run().unwrap();
let args = vec![];
let result = mrb_funcall(&mut vm, None, "setmcnst_top_level", &args).unwrap();
let result: i64 = result.as_ref().try_into().unwrap();
assert_eq!(result, 7);
}
#[test]
fn constants_inside_module_are_scoped_to_the_module() {
let script = r#"
module CptnTiles
Grass = 0
Earth = 1
end
CptnTiles::Earth
"#;
let binary = mrbc_compile("module_const_scoped", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().unwrap();
assert_eq!(value, 1);
}
#[test]
fn nested_module_constants_resolve_through_the_path() {
let script = r#"
module Outer
module Inner
V = 7
end
end
Outer::Inner::V
"#;
let binary = mrbc_compile("module_const_nested", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().unwrap();
assert_eq!(value, 7);
}
#[test]
fn class_constants_resolve_qualified() {
let script = r#"
class C
W = 10
end
C::W
"#;
let binary = mrbc_compile("class_const_qualified", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().unwrap();
assert_eq!(value, 10);
}
#[test]
fn class_constants_stay_readable_from_instance_methods() {
let script = r#"
class C
W = 10
def read
W
end
end
C.new.read
"#;
let binary = mrbc_compile("class_const_from_method", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().unwrap();
assert_eq!(value, 10);
}
#[test]
fn class_constants_do_not_leak_to_top_level_bare_reads() {
let script = r#"
class C
X = 1
end
X
"#;
let binary = mrbc_compile("class_const_no_leak", script);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
assert!(
vm.run().is_err(),
"bare top-level X should not resolve C::X"
);
}