use std::cell::RefCell;
use std::collections::HashMap;
use std::fs;
use std::path::Path as FsPath;
use std::rc::Rc;
use openusd::Result;
use openusd::usd::{InitialLoadSet, LoadPolicy, Stage};
use openusd::{sdf, usd};
fn agreed(stage: &Stage, query: &usd::AttributeQuery, path: &str, time: f64) -> Result<Option<f64>> {
let replayed = query.get_at::<f64>(usd::TimeCode::new(time))?;
let resolved = stage.attribute(path)?.get_at::<f64>(usd::TimeCode::new(time))?;
assert_eq!(replayed, resolved, "the replayed source diverged from a fresh read");
Ok(replayed)
}
const CLIP_BODY: &str = "#usda 1.0\n\ndef \"Clip\"\n{\n double size.timeSamples = {\n 0: 42.0,\n }\n}\n";
fn layer_by_leaf(stage: &Stage, leaf: &str) -> String {
stage
.layer_stack()
.into_iter()
.find(|id| FsPath::new(id).ends_with(leaf))
.expect("layer is loaded")
}
fn leaf_of(identifier: &str) -> String {
FsPath::new(identifier)
.file_name()
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_default()
}
#[test]
fn clip_join_flips_source() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
def "Model" (
clips = {
dictionary default = {
asset[] assetPaths = [@./clip.usda@]
string primPath = "/Clip"
double2[] active = [(0, 0)]
}
}
)
{
double size
}
def "Holder" (
payload = @./clip.usda@</Clip>
)
{
}
"#,
)?;
let stage = Stage::builder()
.load(InitialLoadSet::LoadNone)
.open(dir.path().join("root.usda").to_str().unwrap())?;
let query = stage.attribute_query("/Model.size")?;
assert_eq!(
agreed(&stage, &query, "/Model.size", 0.0)?,
None,
"the clip cannot be opened, so nothing sources the attribute"
);
fs::write(dir.path().join("clip.usda"), CLIP_BODY)?;
stage.load("/Holder", LoadPolicy::WithDescendants)?;
stage.prim("/Holder")?.type_name()?;
assert_eq!(
agreed(&stage, &query, "/Model.size", 0.0)?,
Some(42.0),
"the joined layer is the clip the set named, so the clip now sources the value"
);
Ok(())
}
#[test]
fn clip_layer_edit_reaches() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
(
subLayers = [
@./clip.usda@
]
)
def "Model" (
clips = {
dictionary default = {
asset[] assetPaths = [@./clip.usda@]
string primPath = "/Clip"
double2[] active = [(0, 0)]
}
}
)
{
double size
}
"#,
)?;
fs::write(dir.path().join("clip.usda"), CLIP_BODY)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let query = stage.attribute_query("/Model.size")?;
assert_eq!(agreed(&stage, &query, "/Model.size", 0.0)?, Some(42.0));
let resynced: Rc<RefCell<Vec<sdf::Path>>> = Rc::new(RefCell::new(Vec::new()));
let _token = {
let resynced = resynced.clone();
stage.add_sink(move |_stage: &Stage, change: &usd::CommittedChange<'_>| {
resynced.borrow_mut().extend(change.resynced.iter().cloned());
})
};
let clip_identifier = layer_by_leaf(&stage, "clip.usda");
stage
.layer_mut(&clip_identifier)
.expect("just found in the layer stack")
.edit(|edit| {
edit.attribute_mut(&sdf::path("/Clip.size")?)
.expect("the clip layer parsed")
.expect("the clip authors the attribute")
.set_time_sample(0.0, sdf::Value::Double(7.0))?;
Ok(())
})?;
assert_eq!(
agreed(&stage, &query, "/Model.size", 0.0)?,
Some(7.0),
"the edited clip layer is the one the clip set reads"
);
assert!(
resynced.borrow().contains(&sdf::path("/Model")?),
"the clip set's anchor must be reported, got {:?}",
resynced.borrow()
);
Ok(())
}
#[test]
fn clip_manifest_regenerates() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
(
subLayers = [
@./clip.usda@
]
)
def "Model" (
clips = {
dictionary default = {
asset[] assetPaths = [@./clip.usda@]
string primPath = "/Clip"
double2[] active = [(0, 0)]
}
}
)
{
double size
double other
}
"#,
)?;
fs::write(dir.path().join("clip.usda"), CLIP_BODY)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let query = stage.attribute_query("/Model.other")?;
assert_eq!(
agreed(&stage, &query, "/Model.other", 0.0)?,
None,
"the synthesized manifest declares only what the clip carries"
);
let clip_identifier = layer_by_leaf(&stage, "clip.usda");
stage
.layer_mut(&clip_identifier)
.expect("just found in the layer stack")
.edit(|edit| {
sdf::AttributeSpec::new(
edit.data_mut(),
"/Clip.other",
"double",
sdf::Variability::Varying,
false,
)?
.set_time_sample(0.0, sdf::Value::Double(7.0))?;
Ok(())
})?;
assert_eq!(
agreed(&stage, &query, "/Model.other", 0.0)?,
Some(7.0),
"the manifest regenerates, so the clip now declares — and sources — the attribute"
);
Ok(())
}
#[test]
fn value_edit_reaches_referrer() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
def "Source"
{
def "Inner"
{
double x = 1
}
}
def "Ref" (
references = </Source>
)
{
}
"#,
)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let query = stage.attribute_query("/Ref/Inner.x")?;
assert_eq!(agreed(&stage, &query, "/Ref/Inner.x", 0.0)?, Some(1.0));
stage.attribute("/Source/Inner.x")?.set(sdf::Value::Double(5.0))?;
assert_eq!(
agreed(&stage, &query, "/Ref/Inner.x", 0.0)?,
Some(5.0),
"the referrer composes the edited site"
);
Ok(())
}
#[test]
fn relocated_value_edit_reaches() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
(
relocates = {
</Ref/Inner>: </Ref/Moved>
}
)
def "Source"
{
def "Inner"
{
double x = 1
}
}
def "Ref" (
references = </Source>
)
{
}
"#,
)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let query = stage.attribute_query("/Ref/Moved.x")?;
assert_eq!(
agreed(&stage, &query, "/Ref/Moved.x", 0.0)?,
Some(1.0),
"the relocate moved the referenced prim, and its value came with it"
);
stage.attribute("/Source/Inner.x")?.set(sdf::Value::Double(5.0))?;
assert_eq!(
agreed(&stage, &query, "/Ref/Moved.x", 0.0)?,
Some(5.0),
"the edited site composes at the relocated path"
);
Ok(())
}
#[test]
fn variant_authoring_reaches_stripped() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
def "P" (
variants = {
string v = "x"
}
prepend variantSets = "v"
)
{
variantSet "v" = {
"x" {
def "Child"
{
double size
}
}
}
}
"#,
)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let query = stage.attribute_query("/P/Child.size")?;
assert_eq!(agreed(&stage, &query, "/P/Child.size", 0.0)?, None);
let root = stage.root_layer().identifier().to_owned();
stage.layer_mut(&root).expect("loaded").edit(|edit| {
edit.attribute_mut(&sdf::path("/P{v=x}Child.size")?)
.expect("the root layer parsed")
.expect("the variant declares the attribute")
.set_default(sdf::Value::Double(3.0))?;
Ok(())
})?;
assert_eq!(
agreed(&stage, &query, "/P/Child.size", 0.0)?,
Some(3.0),
"the variant-authored opinion composes at the stripped path"
);
Ok(())
}
#[test]
fn stack_mutations_keep_current() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("root.usda"),
"#usda 1.0\n(\n subLayers = [\n @./weak.usda@\n ]\n)\n\ndef \"B\"\n{\n double y = 2\n}\n",
)?;
fs::write(
dir.path().join("weak.usda"),
"#usda 1.0\n\ndef \"A\"\n{\n double x = 1\n}\n",
)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let a = stage.attribute_query("/A.x")?;
let b = stage.attribute_query("/B.y")?;
assert_eq!(agreed(&stage, &a, "/A.x", 0.0)?, Some(1.0));
assert_eq!(agreed(&stage, &b, "/B.y", 0.0)?, Some(2.0));
let weak = layer_by_leaf(&stage, "weak.usda");
stage.mute_layer(weak.clone());
assert_eq!(
agreed(&stage, &a, "/A.x", 0.0)?,
None,
"the muted layer's opinion is gone"
);
assert_eq!(agreed(&stage, &b, "/B.y", 0.0)?, Some(2.0));
stage.unmute_layer(&weak);
assert_eq!(agreed(&stage, &a, "/A.x", 0.0)?, Some(1.0));
stage.create_attribute("/A.x", "double")?.set(sdf::Value::Double(3.0))?;
assert_eq!(agreed(&stage, &a, "/A.x", 0.0)?, Some(3.0));
stage.set_time_codes_per_second(48.0)?;
assert_eq!(agreed(&stage, &a, "/A.x", 0.0)?, Some(3.0));
stage.set_expression_variables(HashMap::from([(
"WHICH".to_string(),
sdf::Value::String("b".to_string()),
)]))?;
assert_eq!(agreed(&stage, &a, "/A.x", 0.0)?, Some(3.0));
assert_eq!(agreed(&stage, &b, "/B.y", 0.0)?, Some(2.0));
Ok(())
}
#[test]
fn reclaimed_stack_replays_safely() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("payload.usda"),
"#usda 1.0\n\ndef \"Inner\"\n{\n double x = 1\n}\n",
)?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
def "Holder" (
payload = @./payload.usda@</Inner>
)
{
}
def "Other"
{
double y = 2
}
"#,
)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let held = stage.attribute_query("/Holder.x")?;
let other = stage.attribute_query("/Other.y")?;
assert_eq!(agreed(&stage, &held, "/Holder.x", 0.0)?, Some(1.0));
assert_eq!(agreed(&stage, &other, "/Other.y", 0.0)?, Some(2.0));
stage.unload("/Holder")?;
assert_eq!(
agreed(&stage, &held, "/Holder.x", 0.0)?,
None,
"the payload's opinion is unloaded, and replaying must not read the retired stack"
);
assert_eq!(
agreed(&stage, &other, "/Other.y", 0.0)?,
Some(2.0),
"an unrelated query keeps replaying across the reclamation"
);
Ok(())
}
#[test]
fn unculled_dependent_resyncs() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(dir.path().join("source.usda"), "#usda 1.0\n\ndef \"Source\"\n{\n}\n")?;
fs::write(
dir.path().join("root.usda"),
"#usda 1.0\n\ndef \"Ref\" (\n references = @./source.usda@</Source>\n)\n{\n}\n",
)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
assert!(!stage.prim("/Ref/Child")?.is_valid()?, "nothing composes there yet");
let resynced: Rc<RefCell<Vec<sdf::Path>>> = Rc::new(RefCell::new(Vec::new()));
let _token = {
let resynced = resynced.clone();
stage.add_sink(move |_stage: &Stage, change: &usd::CommittedChange<'_>| {
resynced.borrow_mut().extend(change.resynced.iter().cloned());
})
};
let source = stage
.prim("/Ref")?
.prim_stack()?
.into_iter()
.map(|site| site.layer)
.find(|id| FsPath::new(id).ends_with("source.usda"))
.expect("the reference target contributes a spec");
stage.layer_mut(&source).expect("loaded").edit(|edit| {
sdf::PrimSpec::new(edit.data_mut(), "/Source/Child", sdf::Specifier::Over, "")?;
Ok(())
})?;
assert!(
stage.prim("/Ref/Child")?.is_valid()?,
"the referrer's child composes once the site carries a spec"
);
assert!(
resynced.borrow().contains(&sdf::path("/Ref/Child")?),
"the dependent whose existence moved must be resynced, got {:?}",
resynced.borrow()
);
Ok(())
}
#[test]
fn spec_edit_matches_reopen() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("source.usda"),
"#usda 1.0\n\ndef \"Src\"\n{\n double x = 1\n}\n",
)?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
(
subLayers = [
@./weak.usda@
]
)
def "Ref" (
references = @./source.usda@</Src>
)
{
}
"#,
)?;
fs::write(dir.path().join("weak.usda"), "#usda 1.0\n")?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let _ = stage.prim("/Ref")?.prim_stack()?;
let weak = layer_by_leaf(&stage, "weak.usda");
stage.layer_mut(&weak).expect("loaded").edit(|edit| {
sdf::PrimSpec::new(edit.data_mut(), "/Ref", sdf::Specifier::Over, "")?;
Ok(())
})?;
let sites = |stage: &Stage| -> Result<Vec<(String, sdf::Path)>> {
Ok(stage
.prim("/Ref")?
.prim_stack()?
.into_iter()
.map(|site| (leaf_of(&site.layer), site.path))
.collect())
};
let spliced = sites(&stage)?;
stage.layer_mut(&weak).expect("loaded").save()?;
let reopened = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let fresh = sites(&reopened)?;
assert_eq!(spliced, fresh, "the spliced stack must be what a fresh build composes");
assert!(
spliced.iter().any(|(layer, _)| layer == "weak.usda"),
"the authored `over` joined the stack, got {spliced:?}",
);
assert!(
spliced.iter().any(|(layer, _)| layer == "source.usda"),
"the referenced node kept its entry, got {spliced:?}",
);
Ok(())
}
#[test]
fn relocated_proxy_restales() -> Result<()> {
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("source.usda"),
"#usda 1.0\n\ndef \"Source\"\n{\n def \"Inner\"\n {\n double x = 1\n }\n}\n",
)?;
fs::write(
dir.path().join("model.usda"),
r#"#usda 1.0
(
relocates = {
</Model/Inner>: </Model/Moved>
}
)
def "Model" (
references = @./source.usda@</Source>
)
{
}
"#,
)?;
fs::write(
dir.path().join("root.usda"),
r#"#usda 1.0
def "Inst" (
references = @./model.usda@</Model>
instanceable = true
)
{
}
"#,
)?;
let stage = Stage::open(dir.path().join("root.usda").to_str().unwrap())?;
let moved = stage.prim("/Inst/Moved")?;
assert!(
moved.is_instance_proxy()?,
"the query must resolve through a prototype for this to test the redirect"
);
let query = stage.attribute_query("/Inst/Moved.x")?;
assert_eq!(agreed(&stage, &query, "/Inst/Moved.x", 0.0)?, Some(1.0));
let attribute = stage.attribute("/Inst/Moved.x")?;
assert!(
attribute.resolve_info()?.node().is_some(),
"an authored opinion resolves through a composition node"
);
let source = moved
.prim_stack()?
.into_iter()
.map(|site| site.layer)
.find(|id| leaf_of(id) == "source.usda")
.expect("the reference target contributes a spec");
stage.layer_mut(&source).expect("loaded").edit(|edit| {
edit.attribute_mut(&sdf::path("/Source/Inner.x")?)
.expect("the source layer parsed")
.expect("the attribute is authored there")
.set_default(sdf::Value::Double(7.0))?;
Ok(())
})?;
assert_eq!(
agreed(&stage, &query, "/Inst/Moved.x", 0.0)?,
Some(7.0),
"the warmed proxy query must see the edit, not replay the prototype's old source"
);
Ok(())
}