diff --git a/Cargo.lock b/Cargo.lock index d41bd96..6cadd2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -861,6 +861,7 @@ dependencies = [ name = "lune-roblox" version = "0.5.6" dependencies = [ + "anyhow", "base64 0.21.0", "glam", "mlua", diff --git a/packages/lib-roblox/Cargo.toml b/packages/lib-roblox/Cargo.toml index 5662117..5a799c3 100644 --- a/packages/lib-roblox/Cargo.toml +++ b/packages/lib-roblox/Cargo.toml @@ -26,3 +26,6 @@ rbx_dom_weak = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18 rbx_reflection = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18c813417ad14cc37e5abe281dfb51a" } rbx_reflection_database = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18c813417ad14cc37e5abe281dfb51a" } rbx_xml = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18c813417ad14cc37e5abe281dfb51a" } + +[dev-dependencies] +anyhow = "1.0" diff --git a/packages/lib-roblox/src/datatypes/vector2.rs b/packages/lib-roblox/src/datatypes/vector2.rs index af16b36..4ccb794 100644 --- a/packages/lib-roblox/src/datatypes/vector2.rs +++ b/packages/lib-roblox/src/datatypes/vector2.rs @@ -60,6 +60,7 @@ impl LuaUserData for Vector2 { methods.add_meta_method(LuaMetaMethod::Mul, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector2(this.0 * Vec2::splat(*n as f32))), + LuaValue::Integer(i) => return Ok(Vector2(this.0 * Vec2::splat(*i as f32))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector2(this.0 * vec.0)); @@ -79,6 +80,7 @@ impl LuaUserData for Vector2 { methods.add_meta_method(LuaMetaMethod::Div, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector2(this.0 / Vec2::splat(*n as f32))), + LuaValue::Integer(i) => return Ok(Vector2(this.0 / Vec2::splat(*i as f32))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector2(this.0 / vec.0)); diff --git a/packages/lib-roblox/src/datatypes/vector2int16.rs b/packages/lib-roblox/src/datatypes/vector2int16.rs index 5117b3c..1de284b 100644 --- a/packages/lib-roblox/src/datatypes/vector2int16.rs +++ b/packages/lib-roblox/src/datatypes/vector2int16.rs @@ -41,6 +41,7 @@ impl LuaUserData for Vector2int16 { methods.add_meta_method(LuaMetaMethod::Mul, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector2int16(this.0 * IVec2::splat(*n as i32))), + LuaValue::Integer(i) => return Ok(Vector2int16(this.0 * IVec2::splat(*i))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector2int16(this.0 * vec.0)); @@ -60,6 +61,7 @@ impl LuaUserData for Vector2int16 { methods.add_meta_method(LuaMetaMethod::Div, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector2int16(this.0 / IVec2::splat(*n as i32))), + LuaValue::Integer(i) => return Ok(Vector2int16(this.0 / IVec2::splat(*i))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector2int16(this.0 / vec.0)); diff --git a/packages/lib-roblox/src/datatypes/vector3.rs b/packages/lib-roblox/src/datatypes/vector3.rs index 32d4b76..bcf0377 100644 --- a/packages/lib-roblox/src/datatypes/vector3.rs +++ b/packages/lib-roblox/src/datatypes/vector3.rs @@ -71,6 +71,7 @@ impl LuaUserData for Vector3 { methods.add_meta_method(LuaMetaMethod::Mul, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector3(this.0 * Vec3::splat(*n as f32))), + LuaValue::Integer(i) => return Ok(Vector3(this.0 * Vec3::splat(*i as f32))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector3(this.0 * vec.0)); @@ -90,6 +91,7 @@ impl LuaUserData for Vector3 { methods.add_meta_method(LuaMetaMethod::Div, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector3(this.0 / Vec3::splat(*n as f32))), + LuaValue::Integer(i) => return Ok(Vector3(this.0 / Vec3::splat(*i as f32))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector3(this.0 / vec.0)); diff --git a/packages/lib-roblox/src/datatypes/vector3int16.rs b/packages/lib-roblox/src/datatypes/vector3int16.rs index 42c5893..488fcca 100644 --- a/packages/lib-roblox/src/datatypes/vector3int16.rs +++ b/packages/lib-roblox/src/datatypes/vector3int16.rs @@ -41,6 +41,7 @@ impl LuaUserData for Vector3int16 { methods.add_meta_method(LuaMetaMethod::Mul, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector3int16(this.0 * IVec3::splat(*n as i32))), + LuaValue::Integer(i) => return Ok(Vector3int16(this.0 * IVec3::splat(*i))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector3int16(this.0 * vec.0)); @@ -60,6 +61,7 @@ impl LuaUserData for Vector3int16 { methods.add_meta_method(LuaMetaMethod::Div, |_, this, rhs: LuaValue| { match &rhs { LuaValue::Number(n) => return Ok(Vector3int16(this.0 / IVec3::splat(*n as i32))), + LuaValue::Integer(i) => return Ok(Vector3int16(this.0 / IVec3::splat(*i))), LuaValue::UserData(ud) => { if let Ok(vec) = ud.borrow::() { return Ok(Vector3int16(this.0 / vec.0)); diff --git a/packages/lib-roblox/src/lib.rs b/packages/lib-roblox/src/lib.rs index 1c32e7f..8fad922 100644 --- a/packages/lib-roblox/src/lib.rs +++ b/packages/lib-roblox/src/lib.rs @@ -4,6 +4,9 @@ pub mod datatypes; pub mod document; pub mod instance; +#[cfg(test)] +mod tests; + use datatypes::types::*; use datatypes::DatatypeTable; @@ -18,15 +21,18 @@ where } #[rustfmt::skip] -pub fn module(lua: &Lua) -> LuaResult { - let datatypes = vec![ +fn make_all_datatypes(lua: &Lua) -> LuaResult> { + Ok(vec![ ("Vector2", make_dt(lua, Vector2::make_dt_table)?), ("Vector2int16", make_dt(lua, Vector2int16::make_dt_table)?), ("Vector3", make_dt(lua, Vector3::make_dt_table)?), ("Vector3int16", make_dt(lua, Vector3int16::make_dt_table)?), - ]; + ]) +} + +pub fn module(lua: &Lua) -> LuaResult { let exports = lua.create_table()?; - for (name, tab) in datatypes { + for (name, tab) in make_all_datatypes(lua)? { exports.set(name, tab)?; } Ok(exports) diff --git a/packages/lib-roblox/src/tests.rs b/packages/lib-roblox/src/tests.rs new file mode 100644 index 0000000..cb581c0 --- /dev/null +++ b/packages/lib-roblox/src/tests.rs @@ -0,0 +1,45 @@ +use std::{env::set_current_dir, fs::read_to_string, path::PathBuf}; + +use anyhow::{Context, Result}; +use mlua::prelude::*; + +use super::make_all_datatypes; + +macro_rules! create_tests { + ($($test_name:ident: $file_path:expr,)*) => { $( + #[test] + fn $test_name() -> Result<()> { + // NOTE: This path is relative to the lib + // package, not the cwd or workspace root, + // so we need to cd to the repo root first + let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let root_dir = crate_dir.join("../../").canonicalize()?; + set_current_dir(root_dir)?; + // Create all datatypes as globals + let lua = Lua::new(); + let env = lua.globals(); + for (name, tab) in make_all_datatypes(&lua)? { + env.set(name, tab)?; + } + // The rest of the test logic can continue as normal + let full_name = format!("tests/roblox/{}.luau", $file_path); + let script = read_to_string(full_name) + .with_context(|| format!( + "Failed to read test file '{}'", + $file_path + ))?; + lua.load(&script) + .set_name($file_path)? + .set_environment(env)? + .exec()?; + Ok(()) + } + )* } +} + +create_tests! { + datatypes_vector2: "datatypes/Vector2", + datatypes_vector2int16: "datatypes/Vector2int16", + datatypes_vector3: "datatypes/Vector3", + datatypes_vector3int16: "datatypes/Vector3int16", +} diff --git a/tests/roblox/datatypes/Vector2.luau b/tests/roblox/datatypes/Vector2.luau new file mode 100644 index 0000000..033412d --- /dev/null +++ b/tests/roblox/datatypes/Vector2.luau @@ -0,0 +1,40 @@ +-- HACK: Make luau happy, with the mlua rust +-- crate all globals are also present in _G +local Vector2 = _G.Vector2 + +-- Constructors + +Vector2.new() +Vector2.new(0) +Vector2.new(0, 0) +Vector2.new(0 / 0, 0 / 0) + +assert(not pcall(function() + return Vector2.new(false) +end)) +assert(not pcall(function() + return Vector2.new("", "") +end)) +assert(not pcall(function() + return Vector2.new(newproxy(true)) +end)) + +-- Constants + +assert(Vector2.one == Vector2.new(1, 1)) +assert(Vector2.zero == Vector2.new(0, 0)) +assert(Vector2.xAxis == Vector2.new(1, 0)) +assert(Vector2.yAxis == Vector2.new(0, 1)) +assert(Vector2.zAxis == nil) + +-- Ops + +assert(Vector2.new(2, 4) + Vector2.new(1, 1) == Vector2.new(3, 5)) +assert(Vector2.new(2, 4) - Vector2.new(1, 1) == Vector2.new(1, 3)) +assert(Vector2.new(2, 4) * Vector2.new(1, 2) == Vector2.new(2, 8)) +assert(Vector2.new(2, 4) / Vector2.new(1, 2) == Vector2.new(2, 2)) + +assert(Vector2.new(2, 4) * 2 == Vector2.new(4, 8)) +assert(Vector2.new(2, 4) / 2 == Vector2.new(1, 2)) + +-- TODO: Vector math diff --git a/tests/roblox/datatypes/Vector2int16.luau b/tests/roblox/datatypes/Vector2int16.luau new file mode 100644 index 0000000..663f44a --- /dev/null +++ b/tests/roblox/datatypes/Vector2int16.luau @@ -0,0 +1,32 @@ +-- HACK: Make luau happy, with the mlua rust +-- crate all globals are also present in _G +local Vector2int16 = _G.Vector2int16 + +-- Constructors + +Vector2int16.new() +Vector2int16.new(0) +Vector2int16.new(0, 0) + +assert(not pcall(function() + return Vector2int16.new(999_999, 999_999) +end)) +assert(not pcall(function() + return Vector2int16.new(false) +end)) +assert(not pcall(function() + return Vector2int16.new("", "") +end)) +assert(not pcall(function() + return Vector2int16.new(newproxy(true)) +end)) + +-- Ops + +assert(Vector2int16.new(2, 4) + Vector2int16.new(1, 1) == Vector2int16.new(3, 5)) +assert(Vector2int16.new(2, 4) - Vector2int16.new(1, 1) == Vector2int16.new(1, 3)) +assert(Vector2int16.new(2, 4) * Vector2int16.new(1, 2) == Vector2int16.new(2, 8)) +assert(Vector2int16.new(2, 4) / Vector2int16.new(1, 2) == Vector2int16.new(2, 2)) + +assert(Vector2int16.new(2, 4) * 2 == Vector2int16.new(4, 8)) +assert(Vector2int16.new(2, 4) / 2 == Vector2int16.new(1, 2)) diff --git a/tests/roblox/datatypes/Vector3.luau b/tests/roblox/datatypes/Vector3.luau new file mode 100644 index 0000000..71aff32 --- /dev/null +++ b/tests/roblox/datatypes/Vector3.luau @@ -0,0 +1,42 @@ +-- HACK: Make luau happy, with the mlua rust +-- crate all globals are also present in _G +local Vector3 = _G.Vector3 + +-- Constructors + +Vector3.new() +Vector3.new(0) +Vector3.new(0, 0) +Vector3.new(0, 0, 0) +Vector3.new(0 / 0, 0 / 0) +Vector3.new(0 / 0, 0 / 0, 0 / 0) + +assert(not pcall(function() + return Vector3.new(false) +end)) +assert(not pcall(function() + return Vector3.new("", "") +end)) +assert(not pcall(function() + return Vector3.new(newproxy(true)) +end)) + +-- Constants + +assert(Vector3.one == Vector3.new(1, 1, 1)) +assert(Vector3.zero == Vector3.new(0, 0, 0)) +assert(Vector3.xAxis == Vector3.new(1, 0, 0)) +assert(Vector3.yAxis == Vector3.new(0, 1, 0)) +assert(Vector3.zAxis == Vector3.new(0, 0, 1)) + +-- Ops + +assert(Vector3.new(2, 4, 8) + Vector3.new(1, 1, 1) == Vector3.new(3, 5, 9)) +assert(Vector3.new(2, 4, 8) - Vector3.new(1, 1, 1) == Vector3.new(1, 3, 7)) +assert(Vector3.new(2, 4, 8) * Vector3.new(1, 1, 2) == Vector3.new(2, 4, 16)) +assert(Vector3.new(2, 4, 8) / Vector3.new(1, 1, 2) == Vector3.new(2, 4, 4)) + +assert(Vector3.new(2, 4, 8) * 2 == Vector3.new(4, 8, 16)) +assert(Vector3.new(2, 4, 8) / 2 == Vector3.new(1, 2, 4)) + +-- TODO: Vector math diff --git a/tests/roblox/datatypes/Vector3int16.luau b/tests/roblox/datatypes/Vector3int16.luau new file mode 100644 index 0000000..66400b9 --- /dev/null +++ b/tests/roblox/datatypes/Vector3int16.luau @@ -0,0 +1,33 @@ +-- HACK: Make luau happy, with the mlua rust +-- crate all globals are also present in _G +local Vector3int16 = _G.Vector3int16 + +-- Constructors + +Vector3int16.new() +Vector3int16.new(0) +Vector3int16.new(0, 0) +Vector3int16.new(0, 0, 0) + +assert(not pcall(function() + return Vector3int16.new(999_999, 999_999, 999_999) +end)) +assert(not pcall(function() + return Vector3int16.new(false) +end)) +assert(not pcall(function() + return Vector3int16.new("", "") +end)) +assert(not pcall(function() + return Vector3int16.new(newproxy(true)) +end)) + +-- Ops + +assert(Vector3int16.new(2, 4, 8) + Vector3int16.new(1, 1, 1) == Vector3int16.new(3, 5, 9)) +assert(Vector3int16.new(2, 4, 8) - Vector3int16.new(1, 1, 1) == Vector3int16.new(1, 3, 7)) +assert(Vector3int16.new(2, 4, 8) * Vector3int16.new(1, 1, 2) == Vector3int16.new(2, 4, 16)) +assert(Vector3int16.new(2, 4, 8) / Vector3int16.new(1, 1, 2) == Vector3int16.new(2, 4, 4)) + +assert(Vector3int16.new(2, 4, 8) * 2 == Vector3int16.new(4, 8, 16)) +assert(Vector3int16.new(2, 4, 8) / 2 == Vector3int16.new(1, 2, 4))