extern crate mrubyedge;
mod helpers;
use helpers::*;
use std::rc::Rc;
use mrubyedge::yamrb::helpers::{mrb_define_singleton_cmethod, mrb_funcall};
use mrubyedge::yamrb::value::{RObject, RValue};
use mrubyedge::yamrb::vm::VM;
#[test]
fn def_self_in_module_defines_callable_singleton() {
let code = r#"
module M1
def self.answer
42
end
end
M1.answer
"#;
let binary = mrbc_compile("singleton_mod_self", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().expect("answer must be integer");
assert_eq!(value, 42);
}
#[test]
fn def_explicit_module_receiver_singleton() {
let code = r#"
module M2
end
def M2.answer
7
end
M2.answer
"#;
let binary = mrbc_compile("singleton_mod_explicit", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().expect("answer must be integer");
assert_eq!(value, 7);
}
#[test]
fn singleton_class_sugar_inside_module() {
let code = r#"
module M3
class << self
def answer
9
end
end
end
M3.answer
"#;
let binary = mrbc_compile("singleton_mod_sclass", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().expect("answer must be integer");
assert_eq!(value, 9);
}
#[test]
fn native_singleton_then_ruby_singleton_coexist() {
let binary1 = mrbc_compile("smod_native_1", "module Q\nend\n");
let mut rite1 = mrubyedge::rite::load(&binary1).unwrap();
let mut vm = VM::open(&mut rite1);
vm.run().unwrap();
let q_wrapper = vm.get_const_by_name("Q").expect("Q must be defined");
mrb_define_singleton_cmethod(
&mut vm,
q_wrapper,
"native_answer",
Box::new(|_vm, _args| Ok(Rc::new(RObject::integer(5)))),
);
let binary2 = mrbc_compile(
"smod_native_2",
r#"
module Q
def self.ruby_answer
native_answer * 2
end
end
"#,
);
let mut rite2 = mrubyedge::rite::load(&binary2).unwrap();
vm.eval_rite(&mut rite2).unwrap();
let q2 = vm.get_const_by_name("Q").expect("Q still defined");
let n = mrb_funcall(&mut vm, Some(q2.clone()), "native_answer", &[]).unwrap();
let n: i64 = n.as_ref().try_into().expect("native answer integer");
assert_eq!(n, 5);
let r = mrb_funcall(&mut vm, Some(q2), "ruby_answer", &[]).unwrap();
let r: i64 = r.as_ref().try_into().expect("ruby answer integer");
assert_eq!(r, 10);
}
#[test]
fn module_reopen_preserves_ruby_singletons_and_adds_more() {
let binary1 = mrbc_compile(
"smod_reopen_1",
r#"
module R
def self.alpha
1
end
end
"#,
);
let mut rite1 = mrubyedge::rite::load(&binary1).unwrap();
let mut vm = VM::open(&mut rite1);
vm.run().unwrap();
let binary2 = mrbc_compile(
"smod_reopen_2",
r#"
module R
def self.beta
alpha + 1
end
end
"#,
);
let mut rite2 = mrubyedge::rite::load(&binary2).unwrap();
vm.eval_rite(&mut rite2).unwrap();
let r = vm.get_const_by_name("R").expect("R defined");
let a = mrb_funcall(&mut vm, Some(r.clone()), "alpha", &[]).unwrap();
let a: i64 = a.as_ref().try_into().expect("alpha integer");
assert_eq!(a, 1);
let b = mrb_funcall(&mut vm, Some(r), "beta", &[]).unwrap();
let b: i64 = b.as_ref().try_into().expect("beta integer");
assert_eq!(b, 2);
}
#[test]
fn second_wrapper_shares_singleton_state() {
let binary = mrbc_compile("smod_dual", "module W\nend\n");
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = VM::open(&mut rite);
vm.run().unwrap();
let w1 = vm.get_const_by_name("W").expect("W defined");
mrb_define_singleton_cmethod(
&mut vm,
w1,
"tag",
Box::new(|_vm, _args| Ok(Rc::new(RObject::integer(3)))),
);
let canonical = vm.get_const_by_name("W").expect("W defined");
let inner: Rc<mrubyedge::yamrb::value::RModule> = match &canonical.value {
RValue::Module(m) => m.clone(),
_ => panic!("expected module value"),
};
let w2: Rc<RObject> = Rc::new(RObject::module(inner));
let res = mrb_funcall(&mut vm, Some(w2), "tag", &[]).unwrap();
let v: i64 = res.as_ref().try_into().expect("tag integer");
assert_eq!(v, 3);
}
#[test]
fn class_singleton_regression() {
let code = r#"
class C1
def self.answer
11
end
end
C1.answer
"#;
let binary = mrbc_compile("singleton_class_reg", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = VM::open(&mut rite);
let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().expect("answer must be integer");
assert_eq!(value, 11);
}
#[test]
fn instance_extend_regression() {
let code = r#"
module E1
def hello
"hi"
end
end
obj = Object.new
obj.extend(E1)
obj.hello
"#;
let binary = mrbc_compile("singleton_extend_reg", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = VM::open(&mut rite);
let result = vm.run().unwrap();
let value: String = result.as_ref().try_into().expect("hello returns string");
assert_eq!(value, "hi");
}