use aster_core::{ObjectData, Value, Vm, VmError}; #[test] fn bootstrap_is_unique_and_root_has_no_parent() { let mut vm = Vm::new().unwrap(); let root = vm.root_class(); assert_eq!(vm.bootstrap(), root); assert_eq!(vm.bootstrap(), root); assert_eq!(vm.object_count(), 2); assert_eq!(vm.class(root).unwrap().name(), "Object"); assert_eq!(vm.class(root).unwrap().parent(), None); } #[test] fn bootstrap_types_have_headers_and_distinct_inheritance_links() { let vm = Vm::new().unwrap(); let root = vm.root_class(); let type_class = vm.type_class(); assert_ne!(root, type_class); assert_eq!(vm.class(type_class).unwrap().name(), "Type"); assert_eq!(vm.class(type_class).unwrap().parent(), Some(root)); assert_eq!(vm.type_of(root.into()).unwrap(), type_class); assert_eq!(vm.type_of(type_class.into()).unwrap(), type_class); for class in [root, type_class] { let object = vm.object(class.into()).unwrap(); assert_eq!(object.header().class(), type_class); assert!(matches!(object.data(), ObjectData::Class(_))); assert_eq!(vm.as_class(class.into()).unwrap(), class); assert_eq!(vm.as_instance(class.into()), Err(VmError::ExpectedInstance)); } } #[test] fn factories_preserve_class_links_and_distinct_identity() { let mut vm = Vm::new().unwrap(); let root = vm.root_class(); let class = vm.create_class("Point", root).unwrap(); let child = vm.create_class("ColoredPoint", class).unwrap(); let first = vm.create_instance(class).unwrap(); let second = vm.create_instance(class).unwrap(); let plain = vm.create_instance(root).unwrap(); assert_ne!(first, second); assert_eq!(vm.class(child).unwrap().parent(), Some(class)); assert_eq!(vm.type_of(class.into()).unwrap(), vm.type_class()); assert_eq!(vm.type_of(child.into()).unwrap(), vm.type_class()); assert_eq!(vm.type_of(first.into()).unwrap(), class); assert_eq!(vm.type_of(plain.into()).unwrap(), root); let object = vm.object(first.into()).unwrap(); assert_eq!(object.header().class(), class); assert!(matches!(object.data(), ObjectData::Instance(_))); vm.set_field(first, "x", Value::Integer(1)).unwrap(); vm.set_field(first, "next", Value::Object(second.into())) .unwrap(); assert_eq!( vm.instance(first).unwrap().field("x"), Some(&Value::Integer(1)) ); assert_eq!(vm.instance(second).unwrap().field("x"), None); assert_eq!(vm.as_class(class.into()).unwrap(), class); assert_eq!(vm.as_instance(first.into()).unwrap(), first); assert_eq!(vm.as_instance(class.into()), Err(VmError::ExpectedInstance)); assert_eq!(vm.as_class(first.into()), Err(VmError::ExpectedClass)); } #[test] fn foreign_handles_are_rejected_without_mutation() { let mut vm = Vm::new().unwrap(); let mut other = Vm::new().unwrap(); let local = vm.create_instance(vm.root_class()).unwrap(); let foreign = other.create_instance(other.root_class()).unwrap(); let count = vm.object_count(); assert_eq!( vm.create_class("Alien", other.root_class()), Err(VmError::ForeignHandle) ); assert_eq!( vm.create_instance(other.root_class()), Err(VmError::ForeignHandle) ); assert!(matches!( vm.object(foreign.into()), Err(VmError::ForeignHandle) )); assert!(matches!(vm.instance(foreign), Err(VmError::ForeignHandle))); for reference in [ foreign.into(), other.root_class().into(), other.type_class().into(), ] { assert_eq!(vm.type_of(reference), Err(VmError::ForeignHandle)); } assert_eq!( vm.set_field(foreign, "x", Value::Nil), Err(VmError::ForeignHandle) ); vm.set_field(local, "x", Value::Integer(7)).unwrap(); assert_eq!( vm.set_field(local, "x", Value::Object(foreign.into())), Err(VmError::ForeignHandle) ); assert_eq!( vm.instance(local).unwrap().field("x"), Some(&Value::Integer(7)) ); assert_eq!(vm.object_count(), count); } #[test] fn handles_outliving_a_vm_cannot_alias_a_new_vm() { let old = { let vm = Vm::new().unwrap(); vm.root_class() }; let vm = Vm::new().unwrap(); assert_ne!(old, vm.root_class()); assert!(matches!(vm.class(old), Err(VmError::ForeignHandle))); } #[test] fn invalid_names_do_not_allocate_and_names_are_not_a_namespace() { let mut vm = Vm::new().unwrap(); assert_eq!( vm.create_class(" ", vm.root_class()), Err(VmError::EmptyClassName) ); for name in ["Object", "Type"] { assert_eq!( vm.create_class(name, vm.root_class()), Err(VmError::ReservedClassName) ); } assert_eq!(vm.object_count(), 2); let a = vm.create_class("Label", vm.root_class()).unwrap(); let b = vm.create_class("Label", vm.root_class()).unwrap(); assert_ne!(a, b); } #[test] fn type_cannot_be_used_to_bypass_the_class_factory() { let mut vm = Vm::new().unwrap(); let type_class = vm.type_class(); let count = vm.object_count(); assert_eq!( vm.create_instance(type_class), Err(VmError::UnsupportedMetaclass) ); assert_eq!( vm.create_class("CustomType", type_class), Err(VmError::UnsupportedMetaclass) ); assert_eq!(vm.object_count(), count); assert_eq!(vm.type_of(type_class.into()).unwrap(), type_class); } #[test] fn handles_remain_valid_when_the_arena_grows() { let mut vm = Vm::new().unwrap(); let root = vm.root_class(); let first = vm.create_instance(root).unwrap(); vm.set_field(first, "stable", Value::Bool(true)).unwrap(); for _ in 0..4096 { vm.create_instance(root).unwrap(); } assert_eq!(vm.type_of(first.into()).unwrap(), root); assert_eq!(vm.type_of(root.into()).unwrap(), vm.type_class()); assert_eq!(vm.type_of(vm.type_class().into()).unwrap(), vm.type_class()); assert_eq!( vm.instance(first).unwrap().field("stable"), Some(&Value::Bool(true)) ); assert_eq!(vm.class(root).unwrap().name(), "Object"); }