authorclover caruso <git@paperclover.net> 2026-10-15 09:37:12-07:00
committerclover caruso <git@paperclover.net> 2026-10-15 09:52:49-07:00
log08dbdad9e4fb203c874a7035cc77f5e4db5bc7b8
treef88b2d70bf82dcc2e1ca2c4dc9c691858c059ba7
parent171ffc3acdb21c0fe1b49e13481ce7f7f83fbec1
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover)

test(mobile): count each page arrangement as one durable queued edit

pages_drag_indent_and_undo_as_one_edit_each read the replica queue straight after an arrange. Since edits are written by the section thread, the page list can answer from memory before the burst that holds the edit is written, so under load the queue still read empty; a sync step publishing the edit could drain it as well. The test now works offline from the start and waits for written() before counting, and counts every arrangement and the no-op rather than only the first. notebook_open takes the offline flag so the section never opens online, which also closes the same window in unpublished_edit, which went offline only after its session had started a sync step. Under load the old test failed 7 times in 1200 runs; the new one passed 2400 of 2400. Assisted-by: claude-opus-5.5

1 files changed, 31 insertions(+), 24 deletions(-)

crates/mobile/src/tests.rs+31-24
......@@ -1173,10 +1173,14 @@ fn a_share_notebook_names_its_files_from_the_share_s_top() {
11731173 );
11741174}
11751175
1176/// A notebook copy with its library, and a section of it open for editing.
1177fn notebook_open(path: &str) -> (tempfile::TempDir, PathBuf, Arc<Library>, Section) {
1176/// A notebook copy with its library, and a section of it open for editing. Working
1177/// `offline` from the start, no sync step can publish the section's edits.
1178fn notebook_open(path: &str, offline: bool) -> (tempfile::TempDir, PathBuf, Arc<Library>, Section) {
11781179 let (directory, root) = copy("m6/native-features-01/notebook");
11791180 let library = Arc::new(Library::open(&root, &directory.path().join("cache"), true).unwrap());
1181 if offline {
1182 sb_library_set_offline(&library, true);
1183 }
11801184 let section = open(&library, path);
11811185 (directory, root, library, section)
11821186}
......@@ -1195,7 +1199,7 @@ fn eventually(mut ready: impl FnMut() -> bool) -> bool {
11951199
11961200#[test]
11971201fn sync_status_lists_every_section_and_work_offline_holds_edits_until_sync_now() {
1198 let (_directory, root, library, section) = notebook_open("Features.one");
1202 let (_directory, root, library, section) = notebook_open("Features.one", false);
11991203 // Every readable section, the open one's from its session.
12001204 assert!(eventually(|| {
12011205 let status = library.sync_status();
......@@ -1499,8 +1503,7 @@ fn the_accent_pen_and_shapes_draw_as_the_desktop_and_a_cancelled_sweep_stores_no
14991503/// Queues an offline edit to Features.one and closes its session, leaving the edit to the
15001504/// background; the replicas in the cache, and the text edited in.
15011505fn unpublished_edit() -> (tempfile::TempDir, PathBuf, Arc<Library>, ExGuid) {
1502 let (directory, root, library, section) = notebook_open("Features.one");
1503 sb_library_set_offline(&library, true);
1506 let (directory, root, library, section) = notebook_open("Features.one", true);
15041507 let mut canvas = canvas(&section, "Paragraph controls");
15051508 focus(&mut canvas, "Collapsed parent");
15061509 canvas.insert("Unpublished ".into()).unwrap();
......@@ -1699,34 +1702,38 @@ fn levels(section: &Section) -> Vec<(ExGuid, u32)> {
16991702
17001703#[test]
17011704fn pages_drag_indent_and_undo_as_one_edit_each() {
1702 let (_directory, _root, _library, section) = notebook_open("Features.one");
1705 let (_directory, _root, _library, section) = notebook_open("Features.one", true);
1706 let queued = || {
1707 section.shared.section.written().unwrap();
1708 section.shared.section.pending().unwrap().len()
1709 };
17031710 let before = levels(&section);
17041711 assert!(before.len() > 2);
17051712 let last = before.last().unwrap().0;
17061713 let mut dragged = before.clone();
17071714 let moved = dragged.pop().unwrap();
17081715 dragged.insert(0, moved);
1709 let revisions = section.shared.section.pending().unwrap().len();
1710 assert!(section.arrange(&dragged, &[last]).unwrap());
1711 assert_eq!(levels(&section), dragged);
1712 assert_eq!(
1713 section.shared.section.pending().unwrap().len(),
1714 revisions + 1
1715 );
1716 let mut edits = queued();
1717 let mut arrange = |order: &[(ExGuid, u32)], moved: ExGuid| {
1718 assert!(section.arrange(order, &[moved]).unwrap());
1719 assert_eq!(levels(&section), order);
1720 edits += 1;
1721 assert_eq!(queued(), edits);
1722 };
1723 arrange(&dragged, last);
17161724 let second = dragged[1].0;
17171725 let mut indented = dragged.clone();
17181726 indented[1].1 = 2;
1719 assert!(section.arrange(&indented, &[second]).unwrap());
1720 assert_eq!(levels(&section), indented);
1721 assert!(section.arrange(&dragged, &[second]).unwrap());
1722 assert!(section.arrange(&before, &[last]).unwrap());
1723 assert_eq!(levels(&section), before);
1727 arrange(&indented, second);
1728 arrange(&dragged, second);
1729 arrange(&before, last);
17241730 assert!(!section.arrange(&before, &[ExGuid::default()]).unwrap());
1731 assert_eq!(queued(), edits);
17251732}
17261733
17271734#[test]
17281735fn a_page_moves_to_another_section_and_back_where_it_was() {
1729 let (_directory, root, library, features) = notebook_open("Features.one");
1736 let (_directory, root, library, features) = notebook_open("Features.one", false);
17301737 let before = levels(&features);
17311738 let (space, level) = before[1];
17321739 let title = features.shared.section.page(space).unwrap().title;
......@@ -1785,7 +1792,7 @@ fn a_page_moves_to_another_section_and_back_where_it_was() {
17851792fn a_page_edited_while_it_moves_stays_with_its_edit() {
17861793 library::sb_set_coordinator(coordinator);
17871794 for edited in [true, false] {
1788 let (_directory, root, library, features) = notebook_open("Features.one");
1795 let (_directory, root, library, features) = notebook_open("Features.one", false);
17891796 assert!(eventually(|| {
17901797 let status = library.sync_status();
17911798 !status.is_empty()
......@@ -1861,7 +1868,7 @@ fn a_page_its_section_cannot_retire_stays_beside_its_copy() {
18611868 let _exclusive = vfs::EXCLUSIVE
18621869 .lock()
18631870 .unwrap_or_else(std::sync::PoisonError::into_inner);
1864 let (directory, root, library, empty) = notebook_open("Empty.one");
1871 let (directory, root, library, empty) = notebook_open("Empty.one", false);
18651872 let features = open(&library, "Features.one");
18661873 let space = space(&features, "Task controls");
18671874 drop(features);
......@@ -1908,7 +1915,7 @@ fn a_page_its_section_cannot_retire_stays_beside_its_copy() {
19081915/// A page the section it moves to refuses stays where it was, and the move fails.
19091916#[test]
19101917fn a_page_the_other_section_refuses_stays() {
1911 let (_directory, _root, library, features) = notebook_open("Features.one");
1918 let (_directory, _root, library, features) = notebook_open("Features.one", false);
19121919 let before = levels(&features);
19131920 let (space, _) = before[1];
19141921 let page = features.shared.section.page(space).unwrap();
......@@ -1938,7 +1945,7 @@ fn a_page_the_other_section_refuses_stays() {
19381945
19391946#[test]
19401947fn a_sections_last_page_moving_leaves_a_page_that_undo_takes_out() {
1941 let (_directory, _root, library, features) = notebook_open("Features.one");
1948 let (_directory, _root, library, features) = notebook_open("Features.one", false);
19421949 let date = ["Thursday, October 1, 2026", "9:02 AM"];
19431950 let page = features.new_page(None, date[0], date[1]).unwrap();
19441951 let arrived: ExGuid = library
......@@ -1989,7 +1996,7 @@ fn a_sections_last_page_moving_leaves_a_page_that_undo_takes_out() {
19891996
19901997#[test]
19911998fn sections_reorder_and_move_between_groups_and_back() {
1992 let (_directory, root, library, features) = notebook_open("Features.one");
1999 let (_directory, root, library, features) = notebook_open("Features.one", false);
19932000 drop(features);
19942001 let top = |library: &Library| -> Vec<String> {
19952002 let tabs = library.tabs().unwrap();