From 82c386fd0f287a12cb6a9a27ccc50c12a9b81ea3 Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 24 Jun 2023 23:02:43 -0700 Subject: [PATCH] [oden-js] Fix bug with repeated arguments --- oden-js/Cargo.lock | 7 + oden-js/Cargo.toml | 5 +- oden-js/src/conversion/function.rs | 292 ++++++++++++++++++++++++++++- 3 files changed, 297 insertions(+), 7 deletions(-) diff --git a/oden-js/Cargo.lock b/oden-js/Cargo.lock index 00f5674c..ad7d556c 100644 --- a/oden-js/Cargo.lock +++ b/oden-js/Cargo.lock @@ -8,6 +8,12 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "bindgen" version = "0.63.0" @@ -141,6 +147,7 @@ name = "oden-js" version = "0.1.0" dependencies = [ "anyhow", + "assert_matches", "bitflags", "oden-js-sys", "thiserror", diff --git a/oden-js/Cargo.toml b/oden-js/Cargo.toml index cf4bb98a..c9dcd5c5 100644 --- a/oden-js/Cargo.toml +++ b/oden-js/Cargo.toml @@ -9,4 +9,7 @@ edition = "2021" anyhow = "1" bitflags = "1" thiserror = "1" -oden-js-sys = {path = "../oden-js-sys"} \ No newline at end of file +oden-js-sys = {path = "../oden-js-sys"} + +[dev-dependencies] +assert_matches = "1.5.0" diff --git a/oden-js/src/conversion/function.rs b/oden-js/src/conversion/function.rs index cd3e9878..98123db5 100644 --- a/oden-js/src/conversion/function.rs +++ b/oden-js/src/conversion/function.rs @@ -216,7 +216,7 @@ where let vc = C::try_from_value(args[2], &context)?; let vd = D::try_from_value(args[3], &context)?; let ve = E::try_from_value(args[4], &context)?; - let vf = F::try_from_value(args[4], &context)?; + let vf = F::try_from_value(args[5], &context)?; let res = self(context, va, vb, vc, vd, ve, vf); res.into_res(context) } @@ -252,8 +252,8 @@ where let vc = C::try_from_value(args[2], &context)?; let vd = D::try_from_value(args[3], &context)?; let ve = E::try_from_value(args[4], &context)?; - let vf = F::try_from_value(args[4], &context)?; - let vg = G::try_from_value(args[4], &context)?; + let vf = F::try_from_value(args[5], &context)?; + let vg = G::try_from_value(args[6], &context)?; let res = self(context, va, vb, vc, vd, ve, vf, vg); res.into_res(context) } @@ -290,10 +290,290 @@ where let vc = C::try_from_value(args[2], &context)?; let vd = D::try_from_value(args[3], &context)?; let ve = E::try_from_value(args[4], &context)?; - let vf = F::try_from_value(args[4], &context)?; - let vg = G::try_from_value(args[4], &context)?; - let vh = H::try_from_value(args[4], &context)?; + let vf = F::try_from_value(args[5], &context)?; + let vg = G::try_from_value(args[6], &context)?; + let vh = H::try_from_value(args[7], &context)?; let res = self(context, va, vb, vc, vd, ve, vf, vg, vh); res.into_res(context) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Context, Error, EvalFlags, Result, Runtime}; + use assert_matches::assert_matches; + + #[test] + fn no_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn(|_: &ContextRef| -> Result { Ok(0.0) }) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function()", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("0"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn one_argument() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn(|_: &ContextRef, a: f32| -> Result { Ok(a) }) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("1"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function()", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn two_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn(|_: &ContextRef, a: f32, b: f32| -> Result { Ok(a + b) }) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1,2)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("3"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2,3)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn three_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn(|_: &ContextRef, a: f32, b: f32, c: f32| -> Result { Ok(a + b + c) }) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1,2,3)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("6"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1,2)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2,3,4)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn four_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn( + |_: &ContextRef, a: f32, b: f32, c: f32, d: f32| -> Result { + Ok(a + b + c + d) + }, + ) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1,2,3,4)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("10"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1,2,3)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2,3,4,5)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn five_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn( + |_: &ContextRef, a: f32, b: f32, c: f32, d: f32, e: f32| -> Result { + Ok(a + b + c + d + e) + }, + ) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1,2,3,4,5)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("15"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1,2,3,4)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2,3,4,5,6)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn six_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn( + |_: &ContextRef, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32| -> Result { + Ok(a + b + c + d + e + f) + }, + ) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1,2,3,4,5,6)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("21"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1,2,3,4,5)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2,3,4,5,6,7)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn seven_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn( + |_: &ContextRef, + a: f32, + b: f32, + c: f32, + d: f32, + e: f32, + f: f32, + g: f32| + -> Result { Ok(a + b + c + d + e + f + g) }, + ) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1,2,3,4,5,6,7)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("28"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1,2,3,4,5,6)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2,3,4,5,6,7,8)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } + + #[test] + fn eight_arguments() { + let rt = Runtime::new(); + let ctx = Context::new(rt); + + let the_function = ctx + .new_fn( + |_: &ContextRef, + a: f32, + b: f32, + c: f32, + d: f32, + e: f32, + f: f32, + g: f32, + h: f32| + -> Result { Ok(a + b + c + d + e + f + g + h) }, + ) + .unwrap(); + + ctx.global_object() + .unwrap() + .set_property(&ctx, "the_function", &the_function) + .expect("Should be able to set the function"); + + let val = ctx + .eval("the_function(1,2,3,4,5,6,7,8)", "script", EvalFlags::NONE) + .unwrap(); + + assert_eq!(String::from("36"), val.to_string(&ctx).unwrap()); + + let val = ctx.eval("the_function(1,2,3,4,5,6,7)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + + let val = ctx.eval("the_function(1,2,3,4,5,6,7,8,9)", "script", EvalFlags::NONE); + assert_matches!(val, Err(Error::Exception(..))); + } +}