use super::*; fn renamed(bundle: &SourceBundle, entry: &str) -> SourceBundle { let mut out = bundle.clone(); let text = out.files.remove(&out.entry).unwrap(); out.entry = entry.into(); out.files.insert(entry.into(), text); out } fn saved(document: &Document, entry: &str) -> CompiledSourceProject { source_project::compile_bundle(&renamed(document.bundle(), entry), document.limits).unwrap() } #[test] fn save_as_rebases_uniform_history_without_an_undo_step_or_lost_redo() { let mut document = Document::from_project(Project::empty(), ProjectLimits::default()).unwrap(); let original = document.bundle().clone(); let mut comments = original.clone(); comments .files .get_mut(&comments.entry) .unwrap() .push_str("\r\n// exact rationale\r\n"); document.apply_bundle(comments.clone()).unwrap(); document .execute(Command::Rename( document.project().id, "Engineering choice".into(), )) .unwrap(); let future = document.bundle().clone(); document.undo(); let counts = (document.past.len(), document.future.len()); let receipt = saved(&document, "accepted.arcadia"); document.check_save_entry("accepted.arcadia").unwrap(); document .adopt_saved_entry(&receipt, document.revision()) .unwrap(); document.mark_saved(Arc::new(receipt)); assert_eq!((document.past.len(), document.future.len()), counts); assert!(!document.dirty()); assert!(document.undo()); assert_eq!(document.bundle(), &renamed(&original, "accepted.arcadia")); assert!(document.redo()); assert_eq!(document.bundle(), &renamed(&comments, "accepted.arcadia")); assert!(!document.dirty()); assert!(document.redo()); assert_eq!(document.bundle(), &renamed(&future, "accepted.arcadia")); assert!(document.dirty()); let checked = source_project::compile_bundle(document.bundle(), document.limits).unwrap(); assert_eq!(document.compiled().index, checked.index); assert_eq!(document.project(), &checked.decoded.project); } #[test] fn save_as_preserves_explicit_source_entry_changes_in_history() { let mut document = Document::from_project(Project::empty(), ProjectLimits::default()).unwrap(); let original = document.bundle().clone(); let authored = renamed(&original, "authored.arcadia"); document.apply_bundle(authored.clone()).unwrap(); document .execute(Command::Rename( document.project().id, "Later meaning".into(), )) .unwrap(); let receipt = saved(&document, "accepted.arcadia"); document .adopt_saved_entry(&receipt, document.revision()) .unwrap(); assert_eq!(document.past.len(), 2); assert!(document.undo()); assert_eq!(document.bundle(), &authored); assert!(document.undo()); assert_eq!(document.bundle(), &original); assert!(document.redo()); assert_eq!(document.bundle(), &authored); assert!(document.redo()); assert_eq!(document.bundle(), &receipt.bundle); } #[test] fn save_as_rejects_included_filename_collisions_in_past_and_future_without_mutation() { for collision_in_future in [false, true] { let base = source_project::format_bundle(&Project::empty()); let mut included = base.clone(); let text = included.files.get_mut(&included.entry).unwrap(); text.insert_str(text.rfind('}').unwrap(), "\n include \"TAKEN.arcadia\";\n"); included.files.insert( "TAKEN.arcadia".into(), "// retained engineering rationale\n".into(), ); let mut document = if collision_in_future { let mut d = Document::from_bundle(base, ProjectLimits::default()).unwrap(); d.apply_bundle(included).unwrap(); d.undo(); d } else { let mut d = Document::from_bundle(included, ProjectLimits::default()).unwrap(); d.apply_bundle(base).unwrap(); d }; let current = document.snapshot(); let revision = document.revision(); let counts = (document.past.len(), document.future.len()); assert!( document .check_save_entry("taken.arcadia") .unwrap_err() .to_string() .contains("Undo/Redo") ); let receipt = saved(&document, "taken.arcadia"); assert!(document.adopt_saved_entry(&receipt, revision).is_err()); assert!(Arc::ptr_eq(¤t, &document.snapshot())); assert_eq!(document.revision(), revision); assert_eq!((document.past.len(), document.future.len()), counts); } } #[test] fn save_as_rejects_stale_or_content_changing_receipt_atomically() { let mut document = Document::from_project(Project::empty(), ProjectLimits::default()).unwrap(); let mut receipt = saved(&document, "accepted.arcadia"); let revision = document.revision(); document .execute(Command::Rename( document.project().id, "Newer meaning".into(), )) .unwrap(); let current = document.snapshot(); assert!(document.adopt_saved_entry(&receipt, revision).is_err()); assert!( document .adopt_saved_entry(&receipt, document.revision()) .is_err() ); receipt = saved(&document, "accepted.arcadia"); receipt .bundle .files .get_mut(&receipt.bundle.entry) .unwrap() .push_str("// unsaved receipt text\n"); assert!( document .adopt_saved_entry(&receipt, document.revision()) .is_err() ); assert!(Arc::ptr_eq(¤t, &document.snapshot())); assert_eq!(document.past.len(), 1); }