Implement tests for getting & setting roblox instance properties

Also fix some related bugs
This commit is contained in:
Filip Tibell 2023-03-22 13:29:39 +01:00
parent afdde26a18
commit 201f38d44e
No known key found for this signature in database
3 changed files with 91 additions and 28 deletions

View file

@ -663,6 +663,11 @@ impl LuaUserData for Instance {
.to_lua(lua) .to_lua(lua)
} else if let Some(prop_default) = info.value_default { } else if let Some(prop_default) = info.value_default {
Ok(LuaValue::dom_value_to_lua(lua, prop_default)?) Ok(LuaValue::dom_value_to_lua(lua, prop_default)?)
} else if info.value_type.is_some() {
Err(LuaError::RuntimeError(format!(
"Failed to get property '{}' - missing default value",
prop_name
)))
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"Failed to get property '{}' - malformed property info", "Failed to get property '{}' - malformed property info",
@ -730,13 +735,13 @@ impl LuaUserData for Instance {
if let Some(enum_name) = info.enum_name { if let Some(enum_name) = info.enum_name {
match EnumItem::from_lua(prop_value, lua) { match EnumItem::from_lua(prop_value, lua) {
Ok(given_enum) if given_enum.name == enum_name => { Ok(given_enum) if given_enum.parent.desc.name == enum_name => {
this.set_property(prop_name, DomValue::Enum(given_enum.into())); this.set_property(prop_name, DomValue::Enum(given_enum.into()));
Ok(()) Ok(())
} }
Ok(given_enum) => Err(LuaError::RuntimeError(format!( Ok(given_enum) => Err(LuaError::RuntimeError(format!(
"Failed to set property '{}' - expected Enum.{}, got Enum.{}", "Failed to set property '{}' - expected Enum.{}, got Enum.{}",
prop_name, enum_name, given_enum.name prop_name, enum_name, given_enum.parent.desc.name
))), ))),
Err(e) => Err(e), Err(e) => Err(e),
} }

View file

@ -13,6 +13,9 @@ pub(crate) struct PropertyInfo {
/** /**
Finds the info of a property of the given class. Finds the info of a property of the given class.
This will also check superclasses if the property
was not directly found for the given class.
Returns `None` if the class or property does not exist. Returns `None` if the class or property does not exist.
*/ */
pub(crate) fn find_property_info( pub(crate) fn find_property_info(
@ -20,14 +23,18 @@ pub(crate) fn find_property_info(
property_name: impl AsRef<str>, property_name: impl AsRef<str>,
) -> Option<PropertyInfo> { ) -> Option<PropertyInfo> {
let db = rbx_reflection_database::get(); let db = rbx_reflection_database::get();
let class = db.classes.get(instance_class.as_ref())?;
let instance_class = instance_class.as_ref();
let property_name = property_name.as_ref(); let property_name = property_name.as_ref();
let prop_definition = class.properties.get(property_name)?;
let prop_default = class.default_properties.get(property_name);
match &prop_definition.data_type { let mut current_class = Cow::Borrowed(instance_class);
DataType::Enum(enum_name) => Some(PropertyInfo { while let Some(class) = db.classes.get(current_class.as_ref()) {
if let Some(prop_definition) = class.properties.get(property_name) {
// We found a property, we should map it to a property
// info containing name/type and default property value
let prop_default = class.default_properties.get(property_name);
return Some(match &prop_definition.data_type {
DataType::Enum(enum_name) => PropertyInfo {
enum_name: Some(Cow::Borrowed(enum_name)), enum_name: Some(Cow::Borrowed(enum_name)),
enum_default: prop_default.and_then(|default| match default { enum_default: prop_default.and_then(|default| match default {
DomValue::Enum(enum_default) => Some(enum_default.to_u32()), DomValue::Enum(enum_default) => Some(enum_default.to_u32()),
@ -35,20 +42,29 @@ pub(crate) fn find_property_info(
}), }),
value_type: None, value_type: None,
value_default: None, value_default: None,
}), },
DataType::Value(value_type) => Some(PropertyInfo { DataType::Value(value_type) => PropertyInfo {
enum_name: None, enum_name: None,
enum_default: None, enum_default: None,
value_type: Some(*value_type), value_type: Some(*value_type),
value_default: prop_default, value_default: prop_default,
}), },
_ => Some(PropertyInfo { _ => PropertyInfo {
enum_name: None, enum_name: None,
enum_default: None, enum_default: None,
value_type: None, value_type: None,
value_default: None, value_default: None,
}), },
});
} else if let Some(sup) = &class.superclass {
// No property found, we should look at the superclass
current_class = Cow::Borrowed(sup)
} else {
break;
} }
}
None
} }
/** /**

View file

@ -0,0 +1,42 @@
local roblox = require("@lune/roblox") :: any
local BrickColor = roblox.BrickColor
local Instance = roblox.Instance
local Vector3 = roblox.Vector3
local CFrame = roblox.CFrame
local Enum = roblox.Enum
local part = Instance.new("Part")
-- Primitive type properties should work (note that these are inherited from BasePart)
part.Anchored = true
part.CanCollide = true
part.CanQuery = false
assert(part.Anchored == true)
assert(part.CanCollide == true)
assert(part.CanQuery == false)
-- More complex types like Vector3 should work
part.Size = Vector3.one
part.CFrame = CFrame.identity
part.BrickColor = BrickColor.Red()
assert(part.Size == Vector3.one)
assert(part.CFrame == CFrame.identity)
assert(part.BrickColor == BrickColor.Red())
-- Enums should work (note that these are specific to Part and not on BasePart)
part.Shape = Enum.PartType.Ball
assert(part.Shape == Enum.PartType.Ball)
-- Properties that don't exist for a class should error
local meshPart = Instance.new("MeshPart")
assert(not pcall(function()
meshPart.Shape = Enum.PartType.Ball
end))