LR-VIEW-PHOTOGRID: follow test over five seeds; comment the mode switch and the names

The random-edit test now runs five seeds instead of one. A grid-mode change
re-reading every photo, and the group names growing unpruned, are now said in
the comments (the "a day or two at most" claim was wrong for capture-time edits).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nathaniel Ledford
2026-10-21 13:17:39 -04:00
co-authored by Claude Opus 5.5
parent d184b3ef50
commit 53ee38431d
2 changed files with 73 additions and 66 deletions
+5 -1
View File
@@ -150,6 +150,8 @@ impl ListRead {
/// than photos of its own, such as a stack): read it whole then.
fn follow(self, stats: &mut GridStats, cat: &Catalog, ids: &Arc<[PhotoId]>, dates: Option<(SortKey, GroupBy)>, shaped: bool) -> Option<ListRead> {
if self.dates != dates || self.shapes.is_some() != shaped {
// (a switch between the photo grid and the square grid reads every photo once, on
// purpose: the other mode's data was never read, so there is nothing to follow)
return None;
}
let touched: HashSet<PhotoId> = cat.touched_since(self.revision)?.into_iter().collect();
@@ -157,7 +159,9 @@ impl ListRead {
return None;
}
let mut r = ListRead::new(ids.clone(), cat.revision, dates, shaped);
// (the groups' names go on: an import adds a day or two at most)
// (the groups' names go on, unpruned: pruning would renumber every photo's group; they are
// only reset by a whole read, and grow by a short string per day or month an edit puts a
// photo in that no photo was in before)
r.names = self.names;
r.named = self.named;
let mut was = 0;
+68 -65
View File
@@ -285,74 +285,77 @@ fn a_batch_of_new_photos_or_an_edit_to_one_is_read_photo_by_photo() {
/// the list changed by the photos the edit touched, else read whole.
#[test]
fn a_grid_followed_through_edits_shows_what_a_grid_made_afresh_shows() {
let mut h = grid(ViewMode::PhotoGrid);
let mut dice = 11u64;
let mut roll = |sides: u64| {
dice = lightcraft_catalog::mix64(dice);
dice % sides
};
for step in 0..120 {
let vis = h.app.session.visible().to_vec();
if vis.is_empty() {
// (no picks shown: back to every photo)
h.app.session.filter.flag = None;
continue;
}
let id = vis[roll(vis.len() as u64) as usize].0;
let n = roll(1000);
match roll(12) {
0 => {
let ops: Vec<Op> =
(0..1 + roll(20)).map(|k| photo(&mut h, n * 100 + k, &format!("2026-{:02}-{:02}T10:00:00", 1 + n % 12, 1 + k % 28))).collect();
h.app.session.commit("Import", Op::Batch { ops }).unwrap();
for seed in [11u64, 12, 13, 14, 15] {
let mut h = grid(ViewMode::PhotoGrid);
let mut dice = seed;
let mut roll = |sides: u64| {
dice = lightcraft_catalog::mix64(dice);
dice % sides
};
for step in 0..120 {
let vis = h.app.session.visible().to_vec();
if vis.is_empty() {
// (no picks shown: back to every photo)
h.app.session.filter.flag = None;
continue;
}
1 => {
let _ = h.app.session.commit("Delete", Op::SetDeleted { id: PhotoId(id), deleted: true });
}
2 => {
let _ = h
.app
.session
.commit("Capture time", Op::SetCaptured { id: PhotoId(id), captured: Some(format!("2026-{:02}-03T10:00:00", 1 + n % 12)) });
}
3 => {
h.app.session.execute("library.select", &json!({"ids": [id]})).unwrap();
let r = h.app.session.execute("photo.rotateRight", &json!({}));
assert!(r.is_ok(), "{r:?}");
}
4 => {
let mut d = (*h.app.session.catalog.photo(PhotoId(id)).unwrap().develop).clone();
d.crop.geometry.rect = lightcraft_geom::Rect::new(0.1, 0.0, 0.9 - 0.1 * (n % 4) as f64, 1.0);
h.app
.session
.commit("Crop", Op::SetDevelop { id: PhotoId(id), settings: std::sync::Arc::new(d), label: "Crop".into(), edited: None })
.unwrap();
}
5 => h.app.session.commit("Rating", Op::SetRating { id: PhotoId(id), rating: (n % 6) as u8 }).unwrap(),
6 => {
let key = ["captureDate", "fileName", "importDate"][n as usize % 3];
let r = h.app.session.execute("library.sort", &json!({"key": key, "ascending": n.is_multiple_of(2)}));
assert!(r.is_ok(), "{r:?}");
}
7 => {
// a stack: an edit to more than its photos, read whole
let other = vis[n as usize % vis.len()].0;
let _ = h.app.session.execute("stack.group", &json!({"ids": [id, other], "top": id, "collapsed": n.is_multiple_of(2)}));
}
8 => {
let _ = h.app.session.execute("edit.undo", &json!({}));
}
9 => {
// picks, for the filter below to show some
for k in 0..1 + n % 40 {
let p = vis[(n as usize * 7 + k as usize * 13) % vis.len()];
h.app.session.commit("Flag", Op::SetFlag { id: p, flag: lightcraft_catalog::Flag::Pick }).unwrap();
let id = vis[roll(vis.len() as u64) as usize].0;
let n = roll(1000);
match roll(12) {
0 => {
let ops: Vec<Op> = (0..1 + roll(20))
.map(|k| photo(&mut h, n * 100 + k, &format!("2026-{:02}-{:02}T10:00:00", 1 + n % 12, 1 + k % 28)))
.collect();
h.app.session.commit("Import", Op::Batch { ops }).unwrap();
}
1 => {
let _ = h.app.session.commit("Delete", Op::SetDeleted { id: PhotoId(id), deleted: true });
}
2 => {
let _ = h
.app
.session
.commit("Capture time", Op::SetCaptured { id: PhotoId(id), captured: Some(format!("2026-{:02}-03T10:00:00", 1 + n % 12)) });
}
3 => {
h.app.session.execute("library.select", &json!({"ids": [id]})).unwrap();
let r = h.app.session.execute("photo.rotateRight", &json!({}));
assert!(r.is_ok(), "{r:?}");
}
4 => {
let mut d = (*h.app.session.catalog.photo(PhotoId(id)).unwrap().develop).clone();
d.crop.geometry.rect = lightcraft_geom::Rect::new(0.1, 0.0, 0.9 - 0.1 * (n % 4) as f64, 1.0);
h.app
.session
.commit("Crop", Op::SetDevelop { id: PhotoId(id), settings: std::sync::Arc::new(d), label: "Crop".into(), edited: None })
.unwrap();
}
5 => h.app.session.commit("Rating", Op::SetRating { id: PhotoId(id), rating: (n % 6) as u8 }).unwrap(),
6 => {
let key = ["captureDate", "fileName", "importDate"][n as usize % 3];
let r = h.app.session.execute("library.sort", &json!({"key": key, "ascending": n.is_multiple_of(2)}));
assert!(r.is_ok(), "{r:?}");
}
7 => {
// a stack: an edit to more than its photos, read whole
let other = vis[n as usize % vis.len()].0;
let _ = h.app.session.execute("stack.group", &json!({"ids": [id, other], "top": id, "collapsed": n.is_multiple_of(2)}));
}
8 => {
let _ = h.app.session.execute("edit.undo", &json!({}));
}
9 => {
// picks, for the filter below to show some
for k in 0..1 + n % 40 {
let p = vis[(n as usize * 7 + k as usize * 13) % vis.len()];
h.app.session.commit("Flag", Op::SetFlag { id: p, flag: lightcraft_catalog::Flag::Pick }).unwrap();
}
}
10 => h.app.ui.view = if h.app.ui.view == ViewMode::PhotoGrid { ViewMode::SquareGrid } else { ViewMode::PhotoGrid },
_ => h.app.session.filter.flag = n.is_multiple_of(2).then_some(lightcraft_catalog::Flag::Pick),
}
10 => h.app.ui.view = if h.app.ui.view == ViewMode::PhotoGrid { ViewMode::SquareGrid } else { ViewMode::PhotoGrid },
_ => h.app.session.filter.flag = n.is_multiple_of(2).then_some(lightcraft_catalog::Flag::Pick),
h.step();
assert_as_made_afresh(&mut h, &format!("seed {seed}, after step {step}"));
}
h.step();
assert_as_made_afresh(&mut h, &format!("after step {step}"));
}
}