use std::{ borrow::Cow, sync::{atomic::AtomicBool, Arc}, }; use super::gen_action; use loro::{cursor::CannotFindRelativePosition, ExportMode, Frontiers, LoroDoc, ID}; #[test] fn test_gc() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; gen_action(&doc, 123, 32); doc.commit(); let frontiers = doc.oplog_frontiers(); gen_action(&doc, 123, 10); doc.commit(); let shallow_bytes = doc.export(loro::ExportMode::shallow_snapshot(&frontiers)); let new_doc = LoroDoc::new(); new_doc.import(&shallow_bytes.unwrap())?; assert_eq!(doc.get_deep_value(), new_doc.get_deep_value()); Ok(()) } #[test] fn test_shallow_1() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; doc.get_text("text").insert(0, "1")?; doc.get_text("text").insert(0, "2")?; doc.get_text("text").insert(0, "3")?; doc.commit(); let frontiers = doc.oplog_frontiers(); doc.get_text("text").insert(3, "4")?; doc.commit(); let shallow_bytes = doc.export(loro::ExportMode::shallow_snapshot(&frontiers)); let new_doc = LoroDoc::new(); new_doc.import(&shallow_bytes.unwrap())?; assert_eq!(doc.get_deep_value(), new_doc.get_deep_value()); Ok(()) } #[test] fn test_checkout_to_text_that_were_created_before_gc() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; doc.get_text("text").insert(0, "0")?; doc.get_text("text").insert(0, "1")?; doc.get_text("text").insert(0, "2")?; doc.get_text("text").insert(1, "3")?; doc.commit(); let frontiers = doc.oplog_frontiers(); doc.get_text("text").delete(0, 3)?; let bytes = doc.export(loro::ExportMode::shallow_snapshot(&frontiers)); let new_doc = LoroDoc::new(); new_doc.import(&bytes.unwrap())?; new_doc.checkout(&frontiers)?; assert_eq!(new_doc.get_text("text").to_string(), *"2310"); Ok(()) } #[test] fn test_checkout_to_list_that_were_created_before_gc() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; doc.get_list("list").insert(0, 0)?; doc.get_list("list").insert(1, 1)?; doc.get_list("list").insert(2, 2)?; doc.get_list("list").insert(1, 3)?; doc.commit(); let frontiers = doc.oplog_frontiers(); doc.get_list("list").delete(0, 3)?; let bytes = doc.export(loro::ExportMode::shallow_snapshot(&frontiers)); let new_doc = LoroDoc::new(); new_doc.import(&bytes.unwrap())?; new_doc.checkout(&frontiers)?; assert_eq!( new_doc.get_list("list").to_vec(), vec![0.into(), 3.into(), 1.into(), 2.into()] ); Ok(()) } #[test] fn test_checkout_to_movable_list_that_were_created_before_gc() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; doc.get_movable_list("list").insert(0, 0)?; doc.get_movable_list("list").insert(1, 1)?; doc.get_movable_list("list").insert(2, 2)?; doc.get_movable_list("list").insert(1, 3)?; doc.commit(); let frontiers = doc.oplog_frontiers(); doc.get_movable_list("list").delete(0, 3)?; let bytes = doc.export(loro::ExportMode::shallow_snapshot(&frontiers)); let new_doc = LoroDoc::new(); new_doc.import(&bytes.unwrap())?; new_doc.checkout(&frontiers)?; assert_eq!( new_doc.get_movable_list("list").to_vec(), vec![0.into(), 3.into(), 1.into(), 2.into()] ); Ok(()) } #[test] fn shallow_on_the_given_version_when_feasible() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; gen_action(&doc, 123, 64); doc.commit(); let bytes = doc.export(loro::ExportMode::shallow_snapshot_since(ID::new(1, 31))); let new_doc = LoroDoc::new(); new_doc.import(&bytes.unwrap())?; assert_eq!(new_doc.shallow_since_vv().get(&1).copied().unwrap(), 31); Ok(()) } #[test] fn export_snapshot_on_a_shallow_doc() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; gen_action(&doc, 123, 32); doc.commit(); // Get the current frontiers let frontiers = doc.oplog_frontiers(); let old_value = doc.get_deep_value(); gen_action(&doc, 123, 32); doc.commit(); // Export using shallowSnapshot mode let bytes = doc.export(loro::ExportMode::shallow_snapshot(&frontiers)); // Import into a new document let shallow_doc = LoroDoc::new(); shallow_doc.import(&bytes.unwrap())?; assert_eq!(shallow_doc.shallow_since_vv().get(&1).copied().unwrap(), 31); let new_snapshot = shallow_doc.export(loro::ExportMode::Snapshot); let new_doc = LoroDoc::new(); new_doc.import(&new_snapshot.unwrap())?; assert_eq!(new_doc.shallow_since_vv().get(&1).copied().unwrap(), 31); assert_eq!(new_doc.get_deep_value(), doc.get_deep_value()); new_doc.checkout(&frontiers)?; assert_eq!(new_doc.get_deep_value(), old_value); new_doc.checkout_to_latest(); assert_eq!(new_doc.get_deep_value(), doc.get_deep_value()); Ok(()) } #[test] fn test_richtext_gc() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; let text = doc.get_text("text"); text.insert(0, "1")?; // 0 text.insert(0, "2")?; // 1 text.insert(0, "3")?; // 2 text.mark(0..2, "bold", "value")?; // 3, 4 doc.commit(); text.insert(3, "456")?; // 5, 6, 7 let bytes = doc.export(loro::ExportMode::shallow_snapshot_since(ID::new(1, 3))); let new_doc = LoroDoc::new(); new_doc.import(&bytes.unwrap())?; new_doc.checkout(&Frontiers::from(ID::new(1, 4)))?; assert_eq!(new_doc.get_text("text").to_string(), "321"); new_doc.checkout_to_latest(); assert_eq!(new_doc.get_text("text").to_string(), "321456"); Ok(()) } #[test] fn import_updates_depend_on_shallow_history_should_raise_error() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; gen_action(&doc, 123, 4); doc.commit(); let doc2 = doc.fork(); doc2.get_text("text").insert(0, "1")?; doc2.commit(); gen_action(&doc, 123, 2); doc.commit(); let shallow_snapshot = doc.export(loro::ExportMode::shallow_snapshot(&doc.oplog_frontiers())); doc.get_text("hello").insert(0, "world").unwrap(); doc2.import( &doc.export(loro::ExportMode::Updates { from: Cow::Borrowed(&doc2.oplog_vv()), }) .unwrap(), ) .unwrap(); let new_doc = LoroDoc::new(); new_doc.import(&shallow_snapshot.unwrap()).unwrap(); let ran = Arc::new(AtomicBool::new(false)); let ran_clone = ran.clone(); let _sub = new_doc.subscribe_root(Arc::new(move |e| { ran_clone.store(true, std::sync::atomic::Ordering::Relaxed); assert!(e.events.len() == 1); match e.events[0].diff { loro::event::Diff::Text(_) => {} _ => { unreachable!() } } })); let result = new_doc.import( &doc2 .export(loro::ExportMode::updates_owned(new_doc.oplog_vv())) .unwrap(), ); assert!(result.is_err()); // But updates from doc should be fine ("hello": "world") assert_eq!(new_doc.get_text("hello").to_string(), *"world"); assert!(ran.load(std::sync::atomic::Ordering::Relaxed)); Ok(()) } #[test] fn the_vv_on_shallow_doc() -> anyhow::Result<()> { let doc = LoroDoc::new(); gen_action(&doc, 0, 10); doc.commit(); let snapshot = doc.export(loro::ExportMode::shallow_snapshot(&doc.oplog_frontiers())); let new_doc = LoroDoc::new(); new_doc.import(&snapshot.unwrap()).unwrap(); assert!(!new_doc.shallow_since_vv().is_empty()); assert_eq!(new_doc.oplog_vv(), new_doc.state_vv()); assert_eq!(new_doc.oplog_vv(), doc.state_vv()); assert_eq!(new_doc.oplog_frontiers(), doc.oplog_frontiers()); assert_eq!(new_doc.oplog_frontiers(), new_doc.state_frontiers()); assert_eq!(new_doc.get_deep_value(), doc.get_deep_value()); gen_action(&doc, 0, 10); doc.commit(); let bytes = doc.export(ExportMode::all_updates()); new_doc.import(&bytes.unwrap()).unwrap(); assert_eq!(new_doc.oplog_vv(), new_doc.state_vv()); assert_eq!(new_doc.oplog_vv(), doc.state_vv()); assert_eq!(new_doc.oplog_frontiers(), doc.oplog_frontiers()); assert_eq!(new_doc.oplog_frontiers(), new_doc.state_frontiers()); assert_eq!(new_doc.get_deep_value(), doc.get_deep_value()); Ok(()) } #[test] fn no_event_when_exporting_shallow_snapshot() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; gen_action(&doc, 0, 10); doc.commit(); let _id = doc.subscribe_root(Arc::new(|_diff| { panic!("should not emit event"); })); let _snapshot = doc.export(loro::ExportMode::shallow_snapshot_since(ID::new(1, 3))); Ok(()) } #[test] fn test_cursor_that_cannot_be_found_when_exporting_shallow_snapshot() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; doc.get_text("text").insert(0, "Hello world")?; let c = doc .get_text("text") .get_cursor(3, loro::cursor::Side::Left) .unwrap(); doc.get_text("text").delete(0, 5)?; doc.commit(); let snapshot = doc.export(loro::ExportMode::shallow_snapshot(&doc.oplog_frontiers())); let new_doc = LoroDoc::new(); new_doc.import(&snapshot.unwrap())?; let result = new_doc.get_cursor_pos(&c); match result { Ok(v) => { dbg!(v); unreachable!() } Err(CannotFindRelativePosition::HistoryCleared) => {} Err(x) => { dbg!(x); unreachable!() } } Ok(()) } #[test] fn test_cursor_that_can_be_found_when_exporting_shallow_snapshot() -> anyhow::Result<()> { let doc = LoroDoc::new(); doc.set_peer_id(1)?; doc.get_text("text").insert(0, "Hello world")?; doc.commit(); let c = doc .get_text("text") .get_cursor(3, loro::cursor::Side::Left) .unwrap(); doc.get_text("text").delete(0, 5)?; doc.commit(); let snapshot = doc.export(loro::ExportMode::shallow_snapshot_since(ID::new(1, 10))); let new_doc = LoroDoc::new(); new_doc.import(&snapshot.unwrap())?; let result = new_doc.get_cursor_pos(&c); match result { Ok(v) => { assert_eq!(v.current.pos, 0); } Err(x) => { dbg!(x); unreachable!() } } Ok(()) } #[test] fn test_export_shallow_snapshot_from_shallow_doc() -> anyhow::Result<()> { // Create and populate the original document let doc = LoroDoc::new(); doc.set_peer_id(1)?; gen_action(&doc, 123, 32); doc.commit(); // Get the current frontiers and create some more actions let frontiers = doc.oplog_frontiers(); gen_action(&doc, 123, 32); doc.commit(); // Export using shallowSnapshot mode let shallow_bytes = doc.export(loro::ExportMode::shallow_snapshot(&frontiers))?; // Import into a new document let shallow_doc = LoroDoc::new(); shallow_doc.import(&shallow_bytes)?; // Attempt to export a shallow snapshot from the shallow document // using frontiers before its shallow version let result = shallow_doc.export(loro::ExportMode::shallow_snapshot_since(ID::new(1, 16))); // The export should fail because the requested frontiers are before the shallow version assert!(result.is_err()); if let Err(e) = result { assert!(matches!(e, loro::LoroEncodeError::FrontiersNotFound(..))); } else { panic!("Expected an error, but got Ok"); } Ok(()) } #[test] #[should_panic] fn test_export_snapshot_from_shallow_doc() { // Create and populate the original document let doc = LoroDoc::new(); doc.set_peer_id(1).unwrap(); gen_action(&doc, 123, 32); doc.commit(); // Get the current frontiers and create some more actions let frontiers = doc.oplog_frontiers(); gen_action(&doc, 123, 32); doc.commit(); // Export using shallowSnapshot mode let shallow_bytes = doc .export(loro::ExportMode::shallow_snapshot(&frontiers)) .unwrap(); // Import into a new document let shallow_doc = LoroDoc::new(); shallow_doc.import(&shallow_bytes).unwrap(); // Attempt to export a shallow snapshot from the shallow document // using frontiers before its shallow version shallow_doc.export_snapshot(); }