use once_cell::sync::Lazy;
use hapi_rs::parameter::ParmType;
use hapi_rs::{
parameter::{KeyFrame, Parameter, ParmBaseTrait},
session::{quick_session, Session, SessionOptionsBuilder},
Result,
};
static SESSION: Lazy<Session> = Lazy::new(|| {
env_logger::init();
let session = quick_session(None).expect("Could not create test session");
session
.load_asset_file("otls/hapi_parms.hda")
.expect("load asset");
session
});
#[test]
fn parameters_get_set() {
let node = SESSION
.create_node("Object/hapi_parms")
.expect("create_node");
for p in node.parameters().unwrap() {
assert!(p.name().is_ok());
}
if let Parameter::Float(p) = node.parameter("color").unwrap() {
let val = p.get_array().unwrap();
assert_eq!(&val, &[0.55f32, 0.75, 0.95]);
p.set_array([0.7, 0.5, 0.3]).unwrap();
let val = p.get_array().unwrap();
assert_eq!(&val, &[0.7f32, 0.5, 0.3]);
}
if let Parameter::String(p) = node.parameter("multi_string").unwrap() {
let value = p.get_array().unwrap();
assert_eq!(vec!["foo 1", "bar 2", "baz 3"], value);
p.set(1, "cheese").unwrap();
assert_eq!("cheese", p.get(1).unwrap());
}
let menu_folder = node.parameter("folder1_0").unwrap();
assert_eq!(menu_folder.info().parm_type(), ParmType::Folder);
if let ref parm @ Parameter::Int(ref p) = node.parameter("ord_menu").unwrap() {
assert_eq!(
parm.parent().unwrap().unwrap().id(),
menu_folder.info().id()
);
assert!(p.is_menu());
assert_eq!(p.get(0).unwrap(), 0);
if let Some(items) = p.menu_items().unwrap() {
assert_eq!(items[0].value().unwrap(), "foo");
assert_eq!(items[0].label().unwrap(), "Foo");
}
}
if let Parameter::String(p) = node.parameter("script_menu").unwrap() {
assert!(p.is_menu());
assert_eq!(p.get(0).unwrap(), "rs");
if let Some(items) = p.menu_items().unwrap() {
assert_eq!(items[0].value().unwrap(), "rs");
assert_eq!(items[0].label().unwrap(), "Rust");
}
}
if let Parameter::Int(p) = node.parameter("toggle").unwrap() {
assert_eq!(p.get(0).unwrap(), 0);
p.set(0, 1).unwrap();
assert_eq!(p.get(0).unwrap(), 1);
}
if let Parameter::Button(ip) = node.parameter("button").unwrap() {
if let Parameter::String(sp) = node.parameter("single_string").unwrap() {
assert_eq!(sp.get(0).unwrap(), "hello");
ip.press_button().unwrap();
assert_eq!(sp.get(0).unwrap(), "set from callback");
}
}
}
#[test]
fn parameters_set_anim_expression() {
let node = SESSION.create_node("Object/null").unwrap();
if let Ok(Parameter::Float(p)) = node.parameter("scale") {
let keys = vec![
KeyFrame {
time: 0.0,
value: 0.0,
in_tangent: 0.0,
out_tangent: 0.0,
},
KeyFrame {
time: 1.0,
value: 3.0,
in_tangent: 0.0,
out_tangent: 0.0,
},
];
p.set_anim_curve(0, &keys).expect("set_anim_curve");
assert!(p.has_expression(0).unwrap());
SESSION.set_time(1.0).unwrap();
assert_eq!(p.get(0).unwrap(), 3.0);
p.remove_expression(0).unwrap();
assert!(!p.has_expression(0).unwrap());
assert_eq!(p.expression(0).unwrap(), None);
p.set_expression("$T", 0).unwrap();
assert_eq!(p.expression(0).unwrap().as_deref(), Some("$T"));
SESSION.set_time(10.0).unwrap();
assert_eq!(p.get(0).unwrap(), 10.0);
p.remove_expression(0).unwrap();
assert_eq!(p.expression(0).unwrap(), None);
}
}
#[test]
fn parameters_reset_to_default() {
let node = SESSION
.create_node("Object/hapi_parms")
.expect("create_node");
let parm = node.parameter("single_float").unwrap();
if let Parameter::Float(p) = parm {
let default = p.get(0).unwrap();
p.set(0, 0.01).unwrap();
p.revert_to_default(0).unwrap();
assert_eq!(p.get(0).unwrap(), default);
}
}
#[test]
fn parameters_save_parm_file() {
let node = SESSION
.create_node("Object/hapi_parms")
.expect("create_node");
if let Parameter::String(geo_parm) = node.parameter("geo_file").unwrap() {
let dir = tempfile::TempDir::new().unwrap();
let dir = dir.into_path();
let filename = "geo.bgeo.sc";
geo_parm.save_parm_file(&dir, filename).unwrap();
let filesize = std::fs::metadata(dir.join(filename)).unwrap().len();
assert!(filesize > 1024);
std::fs::remove_dir_all(dir).unwrap();
}
}
#[test]
fn parameters_concurrent_access() -> Result<()> {
fn set_parm_value(parm: &Parameter) -> Result<()> {
match parm {
Parameter::Float(parm) => {
let val: [f32; 3] = std::array::from_fn(|_| fastrand::f32());
parm.set_array(val)?
}
Parameter::Int(parm) => {
let values: Vec<_> = std::iter::repeat_with(|| fastrand::i32(0..10))
.take(parm.size() as usize)
.collect();
parm.set_array(values)?
}
Parameter::String(parm) => {
let values: Vec<String> = (0..parm.size())
.into_iter()
.map(|_| {
std::iter::repeat_with(fastrand::alphanumeric)
.take(10)
.collect()
})
.collect();
parm.set_array(values)?
}
Parameter::Button(parm) => parm.press_button()?,
Parameter::Other(_) => {}
};
Ok(())
}
fn get_parm_value(parm: &Parameter) -> Result<()> {
match parm {
Parameter::Float(parm) => {
parm.get(0)?;
}
Parameter::Int(parm) => {
parm.get(0)?;
}
Parameter::String(parm) => {
parm.get(0)?;
}
Parameter::Button(_) => {}
Parameter::Other(_) => {}
};
Ok(())
}
let session = quick_session(Some(
&SessionOptionsBuilder::default().threaded(true).build(),
))?;
let node = session
.load_asset_file("otls/hapi_parms.hda")?
.try_create_first()?;
node.cook_blocking()?;
let parameters = node.parameters()?;
std::thread::scope(|scope| {
let mut handles = Vec::new();
for _ in 0..3 {
let handle = scope.spawn(|| -> Result<()> {
for _ in 0..parameters.len() {
let i = fastrand::usize(..parameters.len());
let parm = ¶meters[i];
if fastrand::bool() {
set_parm_value(parm)?;
node.cook()?;
} else {
get_parm_value(parm)?;
node.cook()?;
}
}
Ok(())
});
handles.push(handle);
}
for h in handles {
h.join().unwrap().unwrap();
}
});
Ok(())
}