From 1c8d20a12fde6cb53a424b699e4dabfb00ca2cec Mon Sep 17 00:00:00 2001 From: arnav-hiwarkar Date: Thu, 12 Nov 2026 14:19:24 +0530 Subject: [PATCH] #3534: test a key 0.24.1 saved while the footage was offline; the frames key doc says what is read in full --- crates/engine/src/analysis/frames_key.rs | 11 +++--- crates/engine/src/tests/analysis_signature.rs | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/analysis/frames_key.rs b/crates/engine/src/analysis/frames_key.rs index 1b141c53e..44eb93d57 100644 --- a/crates/engine/src/analysis/frames_key.rs +++ b/crates/engine/src/analysis/frames_key.rs @@ -1,10 +1,13 @@ //! What an analysis of a layer is made from, as one key shared by the Warp Stabilizer, the 3D //! Camera Tracker and Roto Brush / Object Matte. //! -//! The key hashes a text this module writes field by field. Every struct and enum it writes is -//! destructured or matched in full, and each variant is written as a fixed word. A new field or -//! variant doesn't compile until it is decided here: written if it changes the layer's frames, -//! left out (with a reason) if it doesn't. Renaming a field or variant doesn't change keys. The +//! The key hashes a text this module writes field by field. The source item (footage and its +//! settings, solid), the Time Remap property and the masks and effect groups (their properties, +//! keyframes and values) are destructured or matched in full, and each variant is written as a +//! fixed word: a new field or variant doesn't compile until it is decided here, written if it +//! changes the layer's frames, left out (with a reason) if it doesn't. Renaming a field or variant +//! doesn't change keys. Of the layer and of a precomp, only the fields named in `layer_frames` are +//! read (a precomp's contents are #3535). The //! text leaves out observations (whether footage is available) and data that never changes pixels //! (names, labels, UI hints, markers), so they never drop saved analyses (#3534, #3536). //! diff --git a/crates/engine/src/tests/analysis_signature.rs b/crates/engine/src/tests/analysis_signature.rs index 58a930998..96323e6be 100644 --- a/crates/engine/src/tests/analysis_signature.rs +++ b/crates/engine/src/tests/analysis_signature.rs @@ -198,3 +198,39 @@ fn offline_footage_keeps_analyses() { s.execute_checked("file.interpretFootage", json!({"items": [1], "invertAlpha": true})).unwrap(); assert!(saved(&s).iter().all(|(data, _)| data.is_empty())); } + +/// A key 0.24.1 saved while the footage was offline (it hashed the availability) matches too, +/// online or offline. 0.24.1 gave the Roto Brush this Input Key when the fixture's footage went +/// offline and an edit cleared the other two analyses. +#[test] +fn keys_saved_by_0_24_while_offline_still_match() { + const ROTO_INPUT_0_24_OFFLINE: &str = "6183fdd46b514259"; + let mut s = open(ANALYSED_0_24); + s.edit("offline", None, |p, _| { + if let Some(effectcraft_project::ItemKind::Footage(f)) = p.item_mut(ItemId(1)).map(|i| &mut i.kind) { + f.missing = true; + } + let l = p.comp_mut(COMP).unwrap().layer_mut(CLIP).unwrap(); + // What 0.24.1's edit cleared then. + use effectcraft_effects::camera_tracker::{AVERAGE_ERROR, METHOD_USED, SOLVE, SOLVE_KEY}; + for (uid, id) in + [(WARP, ANALYSIS), (WARP, ANALYSIS_KEY), (TRACKER, SOLVE), (TRACKER, SOLVE_KEY), (TRACKER, METHOD_USED), (TRACKER, TRACKS), (TRACKER, TRACKS_KEY)] + { + l.props.find_group_mut(uid).unwrap().prop_mut(id).unwrap().value = Value::Str(String::new()); + } + l.props.find_group_mut(TRACKER).unwrap().prop_mut(AVERAGE_ERROR).unwrap().value = Value::Scalar(0.0); + let g = l.props.find_group_mut(ROTO).unwrap(); + g.get_mut(INPUT_KEY).unwrap().value = Value::Str(ROTO_INPUT_0_24_OFFLINE.into()); + Ok(()) + }) + .unwrap(); + let roto = |s: &Session| saved(s)[2].clone(); + assert_eq!(roto(&s), ("saved frozen".into(), ROTO_INPUT_0_24_OFFLINE.into())); + for missing in [true, false] { + if let Some(effectcraft_project::ItemKind::Footage(f)) = std::sync::Arc::make_mut(&mut s.project).item_mut(ItemId(1)).map(|i| &mut i.kind) { + f.missing = missing; + } + add_to_render_queue(&mut s); + assert_eq!(roto(&s), ("saved frozen".into(), ROTO_INPUT_0_24_OFFLINE.into())); + } +}