215 lines
8.0 KiB
Rust
215 lines
8.0 KiB
Rust
use super::*;
|
|
|
|
#[test]
|
|
fn walkthrough_uses_packaged_bundle_before_development_and_reports_missing_assets() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let packaged = temp
|
|
.path()
|
|
.join("application/examples/release1/project.arcadia");
|
|
let development = temp.path().join("checkout");
|
|
let checkout_entry = development.join("examples/release1/project.arcadia");
|
|
let executable = temp.path().join("application/arcadia-studio.exe");
|
|
fs::create_dir_all(checkout_entry.parent().unwrap()).unwrap();
|
|
fs::write(&checkout_entry, "development source").unwrap();
|
|
assert_eq!(
|
|
resolve_walkthrough(&executable, Some(&development)).unwrap(),
|
|
checkout_entry
|
|
);
|
|
fs::create_dir_all(packaged.parent().unwrap()).unwrap();
|
|
fs::write(&packaged, "packaged source").unwrap();
|
|
assert_eq!(
|
|
resolve_walkthrough(&executable, Some(&development)).unwrap(),
|
|
packaged
|
|
);
|
|
assert_eq!(resolve_walkthrough(&executable, None).unwrap(), packaged);
|
|
fs::remove_file(&packaged).unwrap();
|
|
let error = resolve_walkthrough(&executable, None).unwrap_err();
|
|
assert!(error.contains("complete examples/release1 folder"));
|
|
assert!(
|
|
error
|
|
.replace('\\', "/")
|
|
.contains(&packaged.display().to_string().replace('\\', "/")),
|
|
"{error}"
|
|
);
|
|
assert_eq!(
|
|
fs::read_to_string(checkout_entry).unwrap(),
|
|
"development source"
|
|
);
|
|
}
|
|
|
|
fn model(directory: &Path, name: &str) -> PathBuf {
|
|
let path = directory.join(name);
|
|
fs::write(&path, b"model contents must remain untouched").unwrap();
|
|
path
|
|
}
|
|
|
|
#[test]
|
|
fn loading_missing_history_does_not_create_files_or_directories() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let store = temp.path().join("not-created/recent-models.json");
|
|
let state = State::load(store.clone());
|
|
assert!(state.recent.is_empty());
|
|
assert!(state.notice.is_none());
|
|
assert!(!store.parent().unwrap().exists());
|
|
}
|
|
|
|
#[test]
|
|
fn successful_local_history_is_bounded_ordered_deduplicated_and_durable() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let store = temp.path().join("settings/recent-models.json");
|
|
let mut state = State::load(store.clone());
|
|
let files: Vec<_> = (0..15)
|
|
.map(|i| model(temp.path(), &format!("model-{i}.arcadia")))
|
|
.collect();
|
|
for (i, path) in files.iter().enumerate() {
|
|
state.remember(path, &format!("Model {i}")).unwrap();
|
|
}
|
|
assert_eq!(state.recent.len(), MAX_RECENTS);
|
|
assert_eq!(state.recent[0].entry.name, "Model 14");
|
|
assert_eq!(state.recent[11].entry.name, "Model 3");
|
|
state.remember(&files[8], "Renamed model").unwrap();
|
|
assert_eq!(state.recent.len(), MAX_RECENTS);
|
|
assert_eq!(state.recent[0].entry.name, "Renamed model");
|
|
let loaded = State::load(store);
|
|
assert!(loaded.notice.is_none());
|
|
assert_eq!(
|
|
loaded.recent.iter().map(|r| &r.entry).collect::<Vec<_>>(),
|
|
state.recent.iter().map(|r| &r.entry).collect::<Vec<_>>()
|
|
);
|
|
assert!(
|
|
loaded
|
|
.recent
|
|
.iter()
|
|
.all(|r| r.availability == Availability::Available)
|
|
);
|
|
assert_eq!(
|
|
fs::read(&files[8]).unwrap(),
|
|
b"model contents must remain untouched"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn forgotten_and_missing_files_never_change_the_model_or_other_entries() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let store = temp.path().join("recent.json");
|
|
let a = model(temp.path(), "a.arcadia");
|
|
let b = model(temp.path(), "b.json");
|
|
let mut state = State::load(store.clone());
|
|
state.remember(&a, "A").unwrap();
|
|
state.remember(&b, "B").unwrap();
|
|
fs::remove_file(&b).unwrap();
|
|
state.refresh();
|
|
assert_eq!(state.recent[0].availability, Availability::Missing);
|
|
assert_eq!(state.recent.len(), 2);
|
|
assert_eq!(
|
|
State::load(store.clone()).recent[0].availability,
|
|
Availability::Missing
|
|
);
|
|
let old = fs::read(&store).unwrap();
|
|
assert!(state.remember(&b, "Missing file").is_err());
|
|
assert_eq!(fs::read(&store).unwrap(), old);
|
|
state.forget(&a.canonicalize().unwrap()).unwrap();
|
|
assert!(a.exists());
|
|
assert_eq!(state.recent.len(), 1);
|
|
assert_eq!(State::load(store).recent.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn unavailable_location_can_reappear_on_refresh_without_losing_identity() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let path = model(temp.path(), "removable.arcadia");
|
|
let mut state = State::default();
|
|
state.remember(&path, "Portable system").unwrap();
|
|
fs::remove_file(&path).unwrap();
|
|
fs::create_dir(&path).unwrap();
|
|
state.refresh();
|
|
assert!(matches!(
|
|
state.recent[0].availability,
|
|
Availability::Unavailable(_)
|
|
));
|
|
fs::remove_dir(&path).unwrap();
|
|
fs::write(&path, b"restored").unwrap();
|
|
state.refresh();
|
|
assert_eq!(state.recent[0].availability, Availability::Available);
|
|
assert_eq!(state.recent[0].entry.name, "Portable system");
|
|
}
|
|
|
|
#[test]
|
|
fn corrupt_future_and_oversized_history_are_preserved_with_usable_session_history() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let model = model(temp.path(), "model.arcadia");
|
|
let store = temp.path().join("recent.json");
|
|
for bytes in [
|
|
b"not json".to_vec(),
|
|
br#"{"version":99,"entries":[]}"#.to_vec(),
|
|
vec![b' '; MAX_STORE_BYTES as usize + 1],
|
|
] {
|
|
fs::write(&store, &bytes).unwrap();
|
|
let mut state = State::load(store.clone());
|
|
assert!(state.notice.is_some());
|
|
assert!(state.remember(&model, "Session model").is_err());
|
|
assert_eq!(state.recent.len(), 1);
|
|
assert_eq!(fs::read(&store).unwrap(), bytes);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn failed_metadata_replace_keeps_previous_history_and_reports_memory_only_entry() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let a = model(temp.path(), "a.arcadia");
|
|
let b = model(temp.path(), "b.arcadia");
|
|
let parent = temp.path().join("settings");
|
|
let store = parent.join("recent.json");
|
|
let mut state = State::load(store.clone());
|
|
state.remember(&a, "A").unwrap();
|
|
let original = fs::read(&store).unwrap();
|
|
let preserved = temp.path().join("preserved");
|
|
fs::rename(&parent, &preserved).unwrap();
|
|
fs::write(&parent, b"directory unavailable").unwrap();
|
|
assert!(state.remember(&b, "B").is_err());
|
|
assert!(state.notice.is_some());
|
|
assert_eq!(state.recent[0].entry.name, "B");
|
|
assert_eq!(fs::read(preserved.join("recent.json")).unwrap(), original);
|
|
fs::remove_file(&parent).unwrap();
|
|
fs::rename(&preserved, &parent).unwrap();
|
|
state.remember(&b, "B").unwrap();
|
|
assert!(state.notice.is_none());
|
|
assert_eq!(State::load(store).recent[0].entry.name, "B");
|
|
assert_eq!(
|
|
fs::read_dir(parent).unwrap().count(),
|
|
1,
|
|
"no abandoned temp files"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn malformed_entries_and_unknown_fields_never_become_open_requests() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let store = temp.path().join("recent.json");
|
|
for value in [
|
|
serde_json::json!({"version":1,"entries":[{"path":"relative.arcadia","name":"Bad"}]}),
|
|
serde_json::json!({"version":1,"entries":[],"credential":"not accepted"}),
|
|
serde_json::json!({"version":1,"entries":[{"path":temp.path().join("absolute.arcadia"),"name":"x".repeat(161)}]}),
|
|
] {
|
|
fs::write(&store, serde_json::to_vec(&value).unwrap()).unwrap();
|
|
let state = State::load(store.clone());
|
|
assert!(state.notice.is_some());
|
|
assert!(state.recent.is_empty());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn first_and_replaced_metadata_are_parseable_and_contain_no_model_bytes() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let path = model(temp.path(), "a.arcadia");
|
|
let store = temp.path().join("recent.json");
|
|
let mut state = State::load(store.clone());
|
|
for name in ["First", "Updated"] {
|
|
state.remember(&path, name).unwrap();
|
|
let bytes = fs::read(&store).unwrap();
|
|
let parsed: Stored = serde_json::from_slice(&bytes).unwrap();
|
|
assert_eq!(parsed.entries[0].name, name);
|
|
assert!(!String::from_utf8(bytes).unwrap().contains("model contents"));
|
|
}
|
|
}
|