Explain protected source destination collisions during Save As
This commit is contained in:
@@ -1,5 +1,148 @@
|
||||
use super::*;
|
||||
|
||||
fn saved_multifile_app(directory: &std::path::Path) -> (App, Id, PathBuf) {
|
||||
let mut project = Project::empty();
|
||||
let activity = project
|
||||
.add_element(Kind::Activity, Layer::Oa, "Assess incident", None)
|
||||
.unwrap();
|
||||
let mut app = App::with_project(project);
|
||||
let path = directory.join("project.arcadia");
|
||||
complete_save(&mut app, path.clone());
|
||||
assert!(app.document.bundle().files.len() > 1);
|
||||
(app, activity, path)
|
||||
}
|
||||
|
||||
fn rejected_save_preserves_editor(app: &mut App, path: PathBuf) -> String {
|
||||
let snapshot = app.document.snapshot();
|
||||
let revision = app.document.revision();
|
||||
let undo = app.document.can_undo();
|
||||
let redo = app.document.can_redo();
|
||||
let draft = app.source_workspace.text().to_owned();
|
||||
let original_path = app.path.clone();
|
||||
let dirty = app.dirty();
|
||||
let result = write_snapshot(
|
||||
app.canvas_document,
|
||||
revision,
|
||||
snapshot.clone(),
|
||||
path,
|
||||
app.disk_project.clone(),
|
||||
);
|
||||
let error = match result {
|
||||
Err(error) => error,
|
||||
Ok(_) => panic!("Expected collision refusal"),
|
||||
};
|
||||
let _ = app.update(Message::Saved(Err(error.clone())));
|
||||
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
||||
assert_eq!(app.document.revision(), revision);
|
||||
assert_eq!(app.document.can_undo(), undo);
|
||||
assert_eq!(app.document.can_redo(), redo);
|
||||
assert_eq!(app.source_workspace.text(), draft);
|
||||
assert_eq!(app.path, original_path);
|
||||
assert_eq!(app.dirty(), dirty);
|
||||
error.message
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_root_new_entry_collision_preserves_files_draft_and_history() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let (mut app, activity, original) = saved_multifile_app(directory.path());
|
||||
let bytes: Vec<_> = app
|
||||
.document
|
||||
.bundle()
|
||||
.files
|
||||
.keys()
|
||||
.map(|p| (p.clone(), std::fs::read(directory.path().join(p)).unwrap()))
|
||||
.collect();
|
||||
app.execute(Command::Rename(activity, "Review incident".into()));
|
||||
app.execute(Command::Rename(activity, "Evaluate incident".into()));
|
||||
let _ = app.update(Message::Undo);
|
||||
assert!(app.document.can_undo() && app.document.can_redo());
|
||||
let cursor = app.source_workspace.files[&app.source_workspace.entry].cursor;
|
||||
app.source_workspace.edit(
|
||||
format!("{}\n// retained draft", app.source_workspace.text()),
|
||||
cursor,
|
||||
);
|
||||
let destination = directory.path().join("renamed.arcadia");
|
||||
let error = rejected_save_preserves_editor(&mut app, destination.clone());
|
||||
assert!(error.contains("already exist") && error.contains("Save All"));
|
||||
assert!(!error.contains("changed outside"));
|
||||
assert!(!destination.exists());
|
||||
assert_eq!(app.path, Some(original));
|
||||
for (path, expected) in bytes {
|
||||
assert_eq!(
|
||||
std::fs::read(directory.path().join(path)).unwrap(),
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fresh_root_existing_include_is_protected_before_any_bundle_write() {
|
||||
let original = tempfile::tempdir().unwrap();
|
||||
let destination = tempfile::tempdir().unwrap();
|
||||
let (mut app, activity, _) = saved_multifile_app(original.path());
|
||||
app.execute(Command::Rename(activity, "Review incident".into()));
|
||||
let fragment = app.document.compiled().index.declarations[&activity]
|
||||
.path
|
||||
.clone();
|
||||
let collision = destination.path().join(&fragment);
|
||||
std::fs::create_dir_all(collision.parent().unwrap()).unwrap();
|
||||
std::fs::write(&collision, b"// independent destination work").unwrap();
|
||||
let target = destination.path().join("copy.arcadia");
|
||||
let error = rejected_save_preserves_editor(&mut app, target.clone());
|
||||
assert!(error.contains(&fragment) && error.contains("fresh directory"));
|
||||
assert!(!error.contains("changed outside"));
|
||||
assert!(!target.exists());
|
||||
for path in app.document.bundle().files.keys() {
|
||||
let file = destination.path().join(path);
|
||||
if path == &fragment {
|
||||
assert_eq!(
|
||||
std::fs::read(file).unwrap(),
|
||||
b"// independent destination work"
|
||||
);
|
||||
} else {
|
||||
assert!(!file.exists(), "Unexpected write: {path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn observed_dependency_change_remains_external_conflict_and_normal_saves_work() {
|
||||
let original = tempfile::tempdir().unwrap();
|
||||
let fresh = tempfile::tempdir().unwrap();
|
||||
let (mut app, activity, path) = saved_multifile_app(original.path());
|
||||
app.execute(Command::Rename(activity, "Review incident".into()));
|
||||
complete_save(&mut app, path.clone());
|
||||
assert!(!app.dirty());
|
||||
let fragment = app.document.compiled().index.declarations[&activity]
|
||||
.path
|
||||
.clone();
|
||||
let entry_before = std::fs::read(&path).unwrap();
|
||||
let original_fragment = std::fs::read(original.path().join(&fragment)).unwrap();
|
||||
std::fs::write(original.path().join(&fragment), b"// external edit").unwrap();
|
||||
app.execute(Command::Rename(activity, "Evaluate incident".into()));
|
||||
let error = rejected_save_preserves_editor(&mut app, path.clone());
|
||||
assert!(error.contains("changed outside") && error.contains(&fragment));
|
||||
assert!(!error.contains("fresh directory"));
|
||||
assert_eq!(std::fs::read(&path).unwrap(), entry_before);
|
||||
assert_eq!(
|
||||
std::fs::read(original.path().join(&fragment)).unwrap(),
|
||||
b"// external edit"
|
||||
);
|
||||
// Restore the observed bytes, then exercise the legitimate existing Save All.
|
||||
std::fs::write(original.path().join(&fragment), original_fragment).unwrap();
|
||||
complete_save(&mut app, path);
|
||||
assert!(!app.dirty());
|
||||
let model = app.project().clone();
|
||||
let target = fresh.path().join("copy.arcadia");
|
||||
complete_save(&mut app, target.clone());
|
||||
let saved = source_files::load_project(&target, LIMITS).unwrap();
|
||||
let compiled = source_project::compile_bundle(&saved.bundle, LIMITS).unwrap();
|
||||
assert_eq!(compiled.decoded.project, model);
|
||||
assert_eq!(&saved.bundle, app.document.bundle());
|
||||
assert!(!app.dirty());
|
||||
}
|
||||
|
||||
fn complete_save(app: &mut App, path: PathBuf) {
|
||||
let receipt = write_snapshot(
|
||||
app.canvas_document,
|
||||
|
||||
Reference in New Issue
Block a user