use rivet_core::*; fn session() -> Session { let mut s = Session { source: Some(demo_motif()), ..Default::default() }; s.regenerate().unwrap(); s } #[test] fn moves_fork_and_restore_context_without_losing_siblings() { let mut s = session(); s.pin_selected(0).unwrap(); let root = s.save_branch("Original").unwrap(); let original = s.composed_phrase().unwrap(); s.apply_move(CreativeMove::Thin, Some(1)).unwrap(); let thin = s.active_branch.unwrap(); let thinned = s.composed_phrase().unwrap(); assert_ne!(thinned, original); assert_eq!( &thinned.notes[..demo_motif().notes.len()], demo_motif().notes.as_slice() ); s.restore_branch(root).unwrap(); assert_eq!(s.composed_phrase().unwrap(), original); assert_eq!(s.pins.len(), 1); s.apply_move(CreativeMove::Lengthen, Some(2)).unwrap(); let long = s.active_branch.unwrap(); assert_ne!(thin, long); assert_eq!(s.branches.len(), 3); assert_eq!( s.branches.iter().find(|b| b.id == thin).unwrap().parent, Some(root) ); assert_eq!( s.branches.iter().find(|b| b.id == long).unwrap().parent, Some(root) ); s.restore_branch(thin).unwrap(); assert_eq!(s.composed_phrase().unwrap(), thinned); assert!(s.remove_branch(root).is_err()); assert_eq!(Session::from_json(&s.to_json().unwrap()).unwrap(), s); } #[test] fn gesture_recall_and_cross_project_library_preserve_performance() { let mut s = session(); s.performance.mode = RoleMode::CallResponse; s.performance.voices[1].controller.enabled = true; s.performance.voices[1].controller.cc = 20; s.performance.voices[1].controller.shape(2); let id = s.save_gesture("Floating answer", Some(1)).unwrap(); let saved = s.gestures[0].clone(); let bytes = s.export_gesture_library().unwrap(); let mut other = Session { source: Some(demo_motif()), harmony: Harmony::progression(2, Scale::Major), ..Default::default() }; other.save_gesture("Existing", None).unwrap(); let context = other.harmony.clone(); other .import_gesture_library(read_gesture_library(&bytes).unwrap()) .unwrap(); assert_eq!(other.gestures.len(), 2); assert_ne!(other.gestures[0].id, other.gestures[1].id); let imported = other.gestures[1].id; other.recall_gesture(imported).unwrap(); assert_eq!(other.source, Some(saved.phrase)); assert_eq!(other.performance, saved.performance); assert_eq!(other.harmony, context); assert!(other.candidates.is_empty()); assert!(other.pins.is_empty()); assert!(s.gestures.iter().any(|g| g.id == id)); } #[test] fn blocked_moves_and_full_histories_are_atomic() { let mut s = session(); s.settings.preserve_rhythm = true; let before = s.clone(); assert!(s.apply_move(CreativeMove::Thin, None).is_err()); assert_eq!(s, before); s.settings.preserve_rhythm = false; for i in 0..MAX_BRANCHES { s.save_branch(&format!("Idea {i}")).unwrap(); } let before = s.clone(); assert!(s.apply_move(CreativeMove::Thin, Some(1)).is_err()); assert_eq!(s, before); let leaf = s.active_branch.unwrap(); s.remove_branch(leaf).unwrap(); assert_eq!(s.branches.len(), MAX_BRANCHES - 1); } #[test] fn malformed_ancestry_libraries_and_version_two_state_are_handled() { let mut s = session(); let id = s.save_branch("Root").unwrap(); s.branches[0].parent = Some(id); assert!(s.validate().is_err()); assert!(read_gesture_library(br#"{"version":99,"gestures":[]}"#).is_err()); let old = session(); let mut value = serde_json::to_value(&old).unwrap(); value["version"] = 2.into(); for key in [ "performance", "gestures", "branches", "active_branch", "next_id", ] { value.as_object_mut().unwrap().remove(key); } let restored = Session::from_json(&serde_json::to_vec(&value).unwrap()).unwrap(); assert_eq!(restored.version, 3); assert_eq!(restored.source, old.source); assert_eq!(restored.performance, PerformanceSettings::default()); } #[test] fn delay_moves_adjacent_notes_together_and_rejects_protected_collisions() { let mut s = session(); let make_note = |start| Note { start, duration: PPQ, pitch: 60, velocity: 90, ..Default::default() }; s.candidates[s.selected].phrase.notes = vec![make_note(SECTION), make_note(SECTION + PPQ)]; s.apply_move(CreativeMove::Delay, Some(1)).unwrap(); let moved = s.composed_phrase().unwrap(); assert_eq!(moved.notes[0].start, SECTION + PPQ / 4); assert_eq!(moved.notes[1].start, SECTION + PPQ + PPQ / 4); s.candidates[0].phrase.notes[1].rhythm_locked = true; let before = s.clone(); assert!(s.apply_move(CreativeMove::Delay, Some(1)).is_err()); assert_eq!(s, before); }