From b305654ab5ef9281fe63f30857a5ae23fa611558 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Sat, 26 Sep 2026 14:32:33 -0700 Subject: [PATCH] feat(data): conflict pages, SMB like OneNote, and no whole-image edit API left Conflicts publish as OneNote 2010 conflict pages (one-sided merge, conflict page under the page, OneNote's delete) instead of a review; cache schema 16 drops the dead columns. SMB publishes in compound requests with the spec's write order, the remote stamp is required, and our locks match OneNote's (three OneNote and three Rust clients converge). Every whole-page and whole-image edit API is gone from onestore and notebook: tests, examples, fuzz targets and evidence generators use ops, and protected sections have an ops path. Edits carry the author's name and initials. New corpus rows: conflict-page, page-date, extended-ascii. Assisted-by: claude-opus-5.5 Assisted-by: claude-opus-5 --- corpus/conflict-page/README.md | 77 + .../candidate-delete/Open Notebook.onetoc2 | Bin 0 -> 4944 bytes .../candidate-delete/synthetic.one | Bin 0 -> 41728 bytes .../candidate/Open Notebook.onetoc2 | Bin 0 -> 4944 bytes corpus/conflict-page/candidate/synthetic.one | Bin 0 -> 43112 bytes .../conflict-page/cold-delete/commands.jsonl | 3 + corpus/conflict-page/cold-delete/machine.json | 1 + ...0b7c57eb3b6110a06aa2114e06d69a7.attachment | 1 + .../cold-delete/read/environment.json | 7 + .../cold-delete/read/hierarchy.xml | 2 + .../cold-delete/read/page-000.navigation.json | 13 + .../cold-delete/read/page-000.png | Bin 0 -> 42268 bytes .../cold-delete/read/page-000.xml | 21 + .../cold-delete/read/payloads.json | 10 + corpus/conflict-page/cold-delete/run.json | 18 + .../cold-delete/scripts/cold.ps1 | 27 + .../cold-delete/scripts/read.ps1 | 142 ++ corpus/conflict-page/cold-delete/source.json | 14 + .../conflict-page/cold-delete/teardown.json | 1 + ...0b7c57eb3b6110a06aa2114e06d69a7.attachment | 1 + .../cold/before-read/environment.json | 7 + .../cold/before-read/hierarchy.xml | 2 + .../cold/before-read/page-000.xml | 26 + .../cold/before-read/payloads.json | 10 + corpus/conflict-page/cold/cold-conflict.json | 13 + corpus/conflict-page/cold/cold-conflict.png | Bin 0 -> 43617 bytes corpus/conflict-page/cold/cold-delete.json | 13 + corpus/conflict-page/cold/cold-delete.png | Bin 0 -> 40227 bytes corpus/conflict-page/cold/cold-main.json | 13 + corpus/conflict-page/cold/cold-main.png | Bin 0 -> 42697 bytes corpus/conflict-page/cold/cold-menu.json | 13 + corpus/conflict-page/cold/cold-menu.png | Bin 0 -> 46585 bytes corpus/conflict-page/cold/cold-shown.json | 13 + corpus/conflict-page/cold/cold-shown.png | Bin 0 -> 43969 bytes corpus/conflict-page/cold/commands.jsonl | 6 + corpus/conflict-page/cold/machine.json | 1 + .../OneNote_DeletedPages.one | Bin 0 -> 4016 bytes .../OneNote_RecycleBin/Open Notebook.onetoc2 | Bin 0 -> 3776 bytes .../cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 5072 bytes .../conflict-page/cold/notebook/synthetic.one | Bin 0 -> 50864 bytes ...0b7c57eb3b6110a06aa2114e06d69a7.attachment | 1 + .../conflict-page/cold/read/environment.json | 7 + corpus/conflict-page/cold/read/hierarchy.xml | 2 + corpus/conflict-page/cold/read/page-000.xml | 26 + corpus/conflict-page/cold/read/payloads.json | 10 + corpus/conflict-page/cold/run.json | 18 + corpus/conflict-page/cold/scripts/cold.ps1 | 27 + corpus/conflict-page/cold/scripts/read.ps1 | 142 ++ corpus/conflict-page/cold/source.json | 14 + corpus/conflict-page/cold/teardown.json | 1 + ...0b7c57eb3b6110a06aa2114e06d69a7.attachment | 1 + .../before-read/environment.json | 7 + .../native-delete/before-read/hierarchy.xml | 2 + .../native-delete/before-read/page-000.xml | 21 + .../native-delete/before-read/payloads.json | 10 + .../native-delete/cold-conflict.json | 13 + .../native-delete/cold-conflict.png | Bin 0 -> 42825 bytes .../native-delete/cold-delete.json | 13 + .../native-delete/cold-delete.png | Bin 0 -> 42359 bytes .../native-delete/cold-main.json | 13 + .../conflict-page/native-delete/cold-main.png | Bin 0 -> 44203 bytes .../native-delete/cold-menu.json | 13 + .../conflict-page/native-delete/cold-menu.png | Bin 0 -> 45888 bytes .../native-delete/cold-shown.json | 13 + .../native-delete/cold-shown.png | Bin 0 -> 43308 bytes .../native-delete/commands.jsonl | 6 + .../conflict-page/native-delete/machine.json | 1 + .../OneNote_DeletedPages.one | Bin 0 -> 4016 bytes .../OneNote_RecycleBin/Open Notebook.onetoc2 | Bin 0 -> 3776 bytes .../notebook/Open Notebook.onetoc2 | Bin 0 -> 5072 bytes .../native-delete/notebook/synthetic.one | Bin 0 -> 42952 bytes ...0b7c57eb3b6110a06aa2114e06d69a7.attachment | 1 + .../native-delete/read/environment.json | 7 + .../native-delete/read/hierarchy.xml | 2 + .../native-delete/read/page-000.xml | 21 + .../native-delete/read/payloads.json | 10 + corpus/conflict-page/native-delete/run.json | 18 + .../native-delete/scripts/cold.ps1 | 27 + .../native-delete/scripts/read.ps1 | 142 ++ .../conflict-page/native-delete/source.json | 14 + .../conflict-page/native-delete/teardown.json | 1 + .../native-pages/a/a-merged.json | 13 + .../conflict-page/native-pages/a/a-merged.png | Bin 0 -> 47168 bytes .../native-pages/a/command-0001.json | 1 + .../native-pages/a/command-0002.json | 1 + .../native-pages/a/command-0003.json | 1 + .../native-pages/a/command-0004.json | 1 + .../native-pages/a/command-0005.json | 1 + .../native-pages/a/command-0006.json | 1 + .../native-pages/a/command-0007.json | 1 + .../native-pages/a/command-0008.json | 1 + .../native-pages/a/command-0009.json | 1 + .../native-pages/a/command-0010.json | 1 + .../native-pages/a/command-0011.json | 1 + .../native-pages/a/command-0012.json | 1 + .../native-pages/a/command-0013.json | 1 + .../native-pages/a/command-0014.json | 1 + .../native-pages/a/commands.jsonl | 16 + .../native-pages/a/hierarchy-0007.xml | 2 + .../native-pages/a/hierarchy-0013.xml | 2 + .../conflict-page/native-pages/a/listed.json | 1 + .../conflict-page/native-pages/a/machine.json | 1 + .../native-pages/a/teardown.json | 1 + .../OneNote_DeletedPages.one | Bin 0 -> 10256 bytes .../OneNote_RecycleBin/Open Notebook.onetoc2 | Bin 0 -> 3776 bytes .../notebook/Open Notebook.onetoc2 | Bin 0 -> 5072 bytes .../b-published/notebook/synthetic.one | Bin 0 -> 58160 bytes .../native-pages/b/b-merged.json | 13 + .../conflict-page/native-pages/b/b-merged.png | Bin 0 -> 47313 bytes .../native-pages/b/command-0001.json | 1 + .../native-pages/b/command-0002.json | 1 + .../native-pages/b/command-0003.json | 1 + .../native-pages/b/command-0004.json | 1 + .../native-pages/b/command-0005.json | 1 + .../native-pages/b/command-0006.json | 1 + .../native-pages/b/command-0007.json | 1 + .../native-pages/b/command-0008.json | 1 + .../native-pages/b/command-0009.json | 1 + .../native-pages/b/command-0010.json | 1 + .../native-pages/b/commands.jsonl | 12 + .../native-pages/b/hierarchy-0001.xml | 2 + .../native-pages/b/hierarchy-0005.xml | 2 + .../native-pages/b/hierarchy-0009.xml | 2 + .../conflict-page/native-pages/b/listed.json | 1 + .../conflict-page/native-pages/b/machine.json | 1 + .../native-pages/b/teardown.json | 1 + .../conflict-page/native-pages/clients.json | 4 + .../initial/notebook/Open Notebook.onetoc2 | Bin 0 -> 4944 bytes .../initial/notebook/synthetic.one | Bin 0 -> 55976 bytes .../native-pages/linux-teardown.json | 1 + corpus/conflict-page/native-pages/linux.json | 11 + corpus/conflict-page/native-pages/merged.json | 49 + .../OneNote_DeletedPages.one | Bin 0 -> 11728 bytes .../OneNote_RecycleBin/Open Notebook.onetoc2 | Bin 0 -> 3776 bytes .../merged/notebook/Open Notebook.onetoc2 | Bin 0 -> 5072 bytes .../merged/notebook/synthetic.one | Bin 0 -> 66320 bytes .../native-pages/native_conflict.py | 362 ++++ .../native-pages/scripts/cold.ps1 | 27 + .../native-pages/scripts/collaborate.ps1 | 145 ++ .../native-pages/scripts/network.ps1 | 20 + .../native-pages/scripts/stress.ps1 | 83 + .../native-pages/scripts/text.ps1 | 13 + .../conflict-page/native-pages/server.jsonl | 2 + .../native-restore/a/a-merged.json | 13 + .../native-restore/a/a-merged.png | Bin 0 -> 47185 bytes .../native-restore/a/command-0001.json | 1 + .../native-restore/a/command-0002.json | 1 + .../native-restore/a/command-0003.json | 1 + .../native-restore/a/command-0004.json | 1 + .../native-restore/a/command-0005.json | 1 + .../native-restore/a/command-0006.json | 1 + .../native-restore/a/command-0007.json | 1 + .../native-restore/a/command-0008.json | 1 + .../native-restore/a/command-0009.json | 1 + .../native-restore/a/command-0010.json | 1 + .../native-restore/a/command-0011.json | 1 + .../native-restore/a/command-0012.json | 1 + .../native-restore/a/command-0013.json | 1 + .../native-restore/a/command-0014.json | 1 + .../native-restore/a/command-0015.json | 1 + .../native-restore/a/command-0016.json | 1 + .../native-restore/a/command-0017.json | 1 + .../native-restore/a/command-0018.json | 1 + .../native-restore/a/command-0019.json | 1 + .../native-restore/a/command-0020.json | 1 + .../native-restore/a/command-0021.json | 1 + .../native-restore/a/command-0022.json | 1 + .../native-restore/a/command-0023.json | 1 + .../native-restore/a/command-0024.json | 1 + .../native-restore/a/command-0025.json | 1 + .../native-restore/a/command-0026.json | 1 + .../native-restore/a/commands.jsonl | 28 + .../native-restore/a/hierarchy-0019.xml | 2 + .../native-restore/a/hierarchy-0025.xml | 2 + .../native-restore/a/listed.json | 1 + .../native-restore/a/machine.json | 1 + .../native-restore/a/teardown.json | 1 + .../OneNote_DeletedPages.one | Bin 0 -> 10256 bytes .../OneNote_RecycleBin/Open Notebook.onetoc2 | Bin 0 -> 3776 bytes .../notebook/Open Notebook.onetoc2 | Bin 0 -> 5072 bytes .../b-published/notebook/synthetic.one | Bin 0 -> 56640 bytes .../native-restore/b/b-merged.json | 13 + .../native-restore/b/b-merged.png | Bin 0 -> 47369 bytes .../native-restore/b/command-0001.json | 1 + .../native-restore/b/command-0002.json | 1 + .../native-restore/b/command-0003.json | 1 + .../native-restore/b/command-0004.json | 1 + .../native-restore/b/command-0005.json | 1 + .../native-restore/b/command-0006.json | 1 + .../native-restore/b/command-0007.json | 1 + .../native-restore/b/command-0008.json | 1 + .../native-restore/b/command-0009.json | 1 + .../native-restore/b/command-0010.json | 1 + .../native-restore/b/command-0011.json | 1 + .../native-restore/b/command-0012.json | 1 + .../native-restore/b/command-0013.json | 1 + .../native-restore/b/command-0014.json | 1 + .../native-restore/b/commands.jsonl | 16 + .../native-restore/b/hierarchy-0001.xml | 2 + .../native-restore/b/hierarchy-0003.xml | 2 + .../native-restore/b/hierarchy-0005.xml | 2 + .../native-restore/b/hierarchy-0007.xml | 2 + .../native-restore/b/hierarchy-0009.xml | 2 + .../native-restore/b/hierarchy-0013.xml | 2 + .../native-restore/b/listed.json | 1 + .../native-restore/b/machine.json | 1 + .../native-restore/b/teardown.json | 1 + .../conflict-page/native-restore/clients.json | 4 + .../initial/notebook/Open Notebook.onetoc2 | Bin 0 -> 4944 bytes .../initial/notebook/synthetic.one | Bin 0 -> 55888 bytes .../native-restore/linux-teardown.json | 1 + .../conflict-page/native-restore/linux.json | 11 + .../conflict-page/native-restore/merged.json | 49 + .../OneNote_DeletedPages.one | Bin 0 -> 11728 bytes .../OneNote_RecycleBin/Open Notebook.onetoc2 | Bin 0 -> 3776 bytes .../merged/notebook/Open Notebook.onetoc2 | Bin 0 -> 5072 bytes .../merged/notebook/synthetic.one | Bin 0 -> 63560 bytes .../native-restore/native_conflict.py | 362 ++++ .../native-restore/scripts/cold.ps1 | 27 + .../native-restore/scripts/collaborate.ps1 | 145 ++ .../native-restore/scripts/network.ps1 | 20 + .../native-restore/scripts/stress.ps1 | 83 + .../native-restore/scripts/text.ps1 | 13 + .../conflict-page/native-restore/server.jsonl | 2 + .../conflict-page/native-three/commands.jsonl | 6 + .../conflict-page/native-three/conflicts.png | Bin 0 -> 42280 bytes corpus/conflict-page/native-three/expand.png | Bin 0 -> 43873 bytes .../conflict-page/native-three/machine.json | 1 + .../notebook/Open Notebook.onetoc2 | Bin 0 -> 3780 bytes .../native-three/notebook/synthetic.one | Bin 0 -> 46416 bytes corpus/conflict-page/native-three/run.json | 17 + corpus/conflict-page/native-three/source.json | 14 + .../conflict-page/native-three/teardown.json | 1 + corpus/conflict-page/native/a/a-conflict.json | 13 + corpus/conflict-page/native/a/a-conflict.png | Bin 0 -> 47134 bytes corpus/conflict-page/native/a/a-main.json | 13 + corpus/conflict-page/native/a/a-main.png | Bin 0 -> 48904 bytes corpus/conflict-page/native/a/a-menu.json | 13 + corpus/conflict-page/native/a/a-menu.png | Bin 0 -> 49896 bytes corpus/conflict-page/native/a/a-shown.json | 13 + corpus/conflict-page/native/a/a-shown.png | Bin 0 -> 47596 bytes corpus/conflict-page/native/a/commands.jsonl | 124 ++ corpus/conflict-page/native/a/machine.json | 1 + corpus/conflict-page/native/a/teardown.json | 1 + .../notebook/Open Notebook.onetoc2 | Bin 0 -> 4944 bytes .../native/b-published/notebook/synthetic.one | Bin 0 -> 31336 bytes corpus/conflict-page/native/b/b-main.json | 13 + corpus/conflict-page/native/b/b-main.png | Bin 0 -> 49003 bytes corpus/conflict-page/native/b/b-shown.json | 13 + corpus/conflict-page/native/b/b-shown.png | Bin 0 -> 48404 bytes corpus/conflict-page/native/b/commands.jsonl | 21 + corpus/conflict-page/native/b/machine.json | 1 + corpus/conflict-page/native/b/teardown.json | 1 + corpus/conflict-page/native/clients.json | 4 + corpus/conflict-page/native/conflict.json | 15 + .../conflict/notebook/Open Notebook.onetoc2 | Bin 0 -> 4944 bytes .../native/conflict/notebook/synthetic.one | Bin 0 -> 40600 bytes .../initial/notebook/Open Notebook.onetoc2 | Bin 0 -> 4944 bytes .../native/initial/notebook/synthetic.one | Bin 0 -> 27808 bytes .../conflict-page/native/linux-teardown.json | 1 + corpus/conflict-page/native/linux.json | 11 + .../conflict-page/native/native_conflict.py | 261 +++ corpus/conflict-page/native/scripts/cold.ps1 | 27 + .../native/scripts/collaborate.ps1 | 105 ++ .../conflict-page/native/scripts/network.ps1 | 20 + .../conflict-page/native/scripts/stress.ps1 | 83 + corpus/conflict-page/native/scripts/text.ps1 | 13 + corpus/conflict-page/native/server.jsonl | 2 + corpus/extended-ascii/README.md | 9 + .../en/candidate/Open Notebook.onetoc2 | Bin 0 -> 4816 bytes .../extended-ascii/en/candidate/synthetic.one | Bin 0 -> 12456 bytes corpus/extended-ascii/en/cold/commands.jsonl | 3 + corpus/extended-ascii/en/cold/machine.json | 1 + .../en/cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 4816 bytes .../en/cold/notebook/synthetic.one | Bin 0 -> 12456 bytes .../en/cold/read/environment.json | 7 + .../extended-ascii/en/cold/read/hierarchy.xml | 2 + .../en/cold/read/page-000.navigation.json | 13 + .../extended-ascii/en/cold/read/page-000.png | Bin 0 -> 39119 bytes .../extended-ascii/en/cold/read/page-000.xml | 2 + .../extended-ascii/en/cold/read/payloads.json | 3 + corpus/extended-ascii/en/cold/run.json | 18 + .../extended-ascii/en/cold/scripts/cold.ps1 | 27 + .../extended-ascii/en/cold/scripts/read.ps1 | 142 ++ corpus/extended-ascii/en/cold/source.json | 14 + corpus/extended-ascii/en/cold/teardown.json | 1 + .../ja/candidate/Open Notebook.onetoc2 | Bin 0 -> 4816 bytes .../extended-ascii/ja/candidate/synthetic.one | Bin 0 -> 12456 bytes corpus/extended-ascii/ja/cold/commands.jsonl | 3 + corpus/extended-ascii/ja/cold/machine.json | 1 + .../ja/cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 4816 bytes .../ja/cold/notebook/synthetic.one | Bin 0 -> 12456 bytes .../ja/cold/read/environment.json | 7 + .../extended-ascii/ja/cold/read/hierarchy.xml | 2 + .../ja/cold/read/page-000.navigation.json | 13 + .../extended-ascii/ja/cold/read/page-000.png | Bin 0 -> 39134 bytes .../extended-ascii/ja/cold/read/page-000.xml | 2 + .../extended-ascii/ja/cold/read/payloads.json | 3 + corpus/extended-ascii/ja/cold/run.json | 18 + .../extended-ascii/ja/cold/scripts/cold.ps1 | 27 + .../extended-ascii/ja/cold/scripts/read.ps1 | 142 ++ corpus/extended-ascii/ja/cold/source.json | 14 + corpus/extended-ascii/ja/cold/teardown.json | 1 + .../ru/candidate/Open Notebook.onetoc2 | Bin 0 -> 4816 bytes .../extended-ascii/ru/candidate/synthetic.one | Bin 0 -> 12456 bytes corpus/extended-ascii/ru/cold/commands.jsonl | 3 + corpus/extended-ascii/ru/cold/machine.json | 1 + .../ru/cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 4816 bytes .../ru/cold/notebook/synthetic.one | Bin 0 -> 12456 bytes .../ru/cold/read/environment.json | 7 + .../extended-ascii/ru/cold/read/hierarchy.xml | 2 + .../ru/cold/read/page-000.navigation.json | 13 + .../extended-ascii/ru/cold/read/page-000.png | Bin 0 -> 39119 bytes .../extended-ascii/ru/cold/read/page-000.xml | 2 + .../extended-ascii/ru/cold/read/payloads.json | 3 + corpus/extended-ascii/ru/cold/run.json | 18 + .../extended-ascii/ru/cold/scripts/cold.ps1 | 27 + .../extended-ascii/ru/cold/scripts/read.ps1 | 142 ++ corpus/extended-ascii/ru/cold/source.json | 14 + corpus/extended-ascii/ru/cold/teardown.json | 1 + .../m6/native-template-controls-01/ORACLE.md | 6 +- .../notebook/synthetic.one | Bin 41092 -> 41092 bytes .../read/environment.json | 4 +- .../read/hierarchy.xml | 2 +- .../read/page-000.pdf | Bin 98921 -> 85011 bytes .../read/page-000.xml | 179 +- .../read/page-001.pdf | Bin 99072 -> 95291 bytes .../read/page-001.xml | 354 ++-- .../read/page-002.pdf | Bin 85011 -> 95138 bytes .../read/page-002.xml | 179 +- corpus/outline-edit/README.md | 2 +- corpus/page-model/README.md | 2 +- corpus/paragraph-edit/README.md | 2 +- corpus/protected-edit/README.md | 2 +- corpus/table-edit/README.md | 2 +- crates/notebook/Cargo.toml | 4 + crates/notebook/README.md | 66 +- crates/notebook/examples/cache_probe.rs | 17 +- crates/notebook/examples/keystroke_probe.rs | 9 +- crates/notebook/examples/migrate_probe.rs | 22 +- crates/notebook/examples/open_probe.rs | 9 +- crates/notebook/examples/queue_scale.rs | 92 +- crates/notebook/examples/recovery_probe.rs | 16 +- crates/notebook/examples/save_probe.rs | 62 +- crates/notebook/examples/session_client.rs | 73 +- .../examples/smb_concurrent_client.rs | 7 +- .../notebook/examples/smb_offline_client.rs | 90 +- crates/notebook/examples/smb_publish_probe.rs | 110 ++ .../notebook/examples/smb_reconnect_client.rs | 34 +- crates/notebook/examples/support/edit.rs | 49 + crates/notebook/examples/support/view.rs | 41 + .../notebook/src/bin/onestore-diagnostic.rs | 115 +- crates/notebook/src/lib.rs | 27 +- crates/notebook/src/merge.rs | 652 ++++--- crates/notebook/src/migrate.rs | 75 +- crates/notebook/src/queue.rs | 6 +- crates/notebook/src/recovery.rs | 10 +- crates/notebook/src/schema.rs | 65 +- crates/notebook/src/session.rs | 543 +++--- crates/notebook/src/smb/directory.rs | 6 +- crates/notebook/src/smb/mod.rs | 487 +++-- crates/notebook/src/smb/remote.rs | 8 +- crates/notebook/src/smb/tests.rs | 105 +- crates/notebook/src/smb/tests/faults.rs | 45 +- crates/notebook/src/sync.rs | 233 +-- crates/notebook/src/working.rs | 447 ++--- crates/notebook/tests/assets.rs | 25 +- crates/notebook/tests/cache.rs | 44 +- crates/notebook/tests/changes.rs | 4 +- crates/notebook/tests/copy.rs | 17 +- crates/notebook/tests/migrate.rs | 149 +- crates/notebook/tests/protected.rs | 21 +- crates/notebook/tests/session.rs | 223 +-- crates/notebook/tests/smb_queue.rs | 16 +- crates/notebook/tests/structure.rs | 4 +- crates/notebook/tests/support/model_ops.rs | 25 +- .../notebook/tests/support/model_schedule.rs | 107 +- .../notebook/tests/support/page_schedule.rs | 74 +- crates/notebook/tests/support/server.rs | 105 +- crates/notebook/tests/sync.rs | 436 ++--- crates/notebook/tests/sync/tree.rs | 90 +- crates/notebook/tests/sync_formatting.rs | 91 +- crates/notebook/tests/sync_model.rs | 136 +- crates/notebook/tests/sync_outline.rs | 155 +- crates/notebook/tests/sync_page.rs | 177 +- crates/notebook/tests/sync_pages.rs | 281 +-- crates/notebook/tests/sync_paragraph.rs | 158 +- crates/notebook/tests/sync_worker.rs | 231 +-- crates/onestore/README.md | 100 +- crates/onestore/examples/concurrent_client.rs | 12 +- crates/onestore/examples/edit_property.rs | 69 +- crates/onestore/examples/header_faults.rs | 34 +- crates/onestore/examples/insert.rs | 109 +- .../onestore/examples/maintenance_fixture.rs | 8 +- crates/onestore/examples/power_loss.rs | 85 +- crates/onestore/examples/random_edit.rs | 15 +- crates/onestore/examples/scratch_dump.rs | 29 + .../onestore/examples/support/concurrent.rs | 142 +- crates/onestore/examples/support/typing.rs | 41 + crates/onestore/src/active.rs | 62 +- crates/onestore/src/commit.rs | 333 +--- crates/onestore/src/create.rs | 56 +- crates/onestore/src/edit.rs | 26 +- crates/onestore/src/formatting.rs | 16 +- crates/onestore/src/insertion.rs | 202 +- crates/onestore/src/lib.rs | 24 +- crates/onestore/src/op/apply.rs | 313 +++- crates/onestore/src/op/levels.rs | 32 - crates/onestore/src/op/lower.rs | 19 +- crates/onestore/src/op/mod.rs | 23 +- crates/onestore/src/op/model.rs | 9 +- crates/onestore/src/op/table.rs | 1 - crates/onestore/src/op/tests.rs | 501 ++--- crates/onestore/src/outline.rs | 29 +- crates/onestore/src/page/mod.rs | 53 +- crates/onestore/src/page/write.rs | 1646 ----------------- crates/onestore/src/pages.rs | 327 +++- crates/onestore/src/paragraph.rs | 99 +- crates/onestore/src/protected/crypto.rs | 1 + crates/onestore/src/protected/mod.rs | 132 +- crates/onestore/src/section.rs | 305 +-- crates/onestore/src/toc.rs | 69 +- crates/onestore/src/tree.rs | 233 +-- crates/onestore/src/write.rs | 187 +- crates/onestore/src/write/tests.rs | 150 +- crates/onestore/src/write/tests/multi.rs | 34 +- crates/onestore/tests/commit.rs | 224 +-- crates/onestore/tests/conflict.rs | 357 ++++ crates/onestore/tests/document.rs | 37 +- crates/onestore/tests/edit.rs | 176 +- crates/onestore/tests/formatting.rs | 81 +- crates/onestore/tests/insertion.rs | 540 +++--- crates/onestore/tests/outline.rs | 80 +- crates/onestore/tests/page.rs | 118 +- crates/onestore/tests/page_attachments.rs | 31 +- crates/onestore/tests/page_deletion.rs | 36 +- crates/onestore/tests/page_images.rs | 77 +- crates/onestore/tests/page_ink.rs | 33 +- crates/onestore/tests/page_links.rs | 32 +- crates/onestore/tests/page_lists.rs | 49 +- crates/onestore/tests/page_math.rs | 14 +- crates/onestore/tests/page_media.rs | 18 +- crates/onestore/tests/page_movement.rs | 49 +- crates/onestore/tests/page_removal.rs | 13 +- crates/onestore/tests/page_tables.rs | 70 +- crates/onestore/tests/page_tags.rs | 46 +- crates/onestore/tests/page_writer.rs | 262 +-- crates/onestore/tests/paragraph.rs | 268 +-- crates/onestore/tests/paragraph_formatting.rs | 16 +- crates/onestore/tests/protected.rs | 36 +- crates/onestore/tests/shared_snapshot.rs | 32 +- crates/onestore/tests/storage.rs | 2 +- crates/onestore/tests/support/checkpoint.rs | 51 +- crates/onestore/tests/support/ops.rs | 187 ++ crates/onestore/tests/support/page_edits.rs | 36 +- .../onestore/tests/support/page_schedule.rs | 38 +- crates/onestore/tests/support/tree_model.rs | 277 +-- crates/onestore/tests/table_growth.rs | 13 +- crates/onestore/tests/toc.rs | 52 +- crates/onestore/tests/transaction.rs | 36 +- crates/onestore/tests/tree.rs | 176 +- crates/onestore/tests/writer.rs | 195 +- tools/canvas/README.md | 6 +- tools/document_model.py | 18 +- tools/native/collaborate.ps1 | 40 + tools/native_collaboration.py | 8 +- tools/native_conflict.py | 362 ++++ tools/smb-proxy.py | 32 +- tools/smb_lock_race.py | 32 +- tools/test_conflict_page.py | 166 ++ tools/test_extended_ascii.py | 31 + tools/test_native_stress.py | 4 +- 472 files changed, 12845 insertions(+), 8201 deletions(-) create mode 100644 corpus/conflict-page/README.md create mode 100644 corpus/conflict-page/candidate-delete/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/candidate-delete/synthetic.one create mode 100644 corpus/conflict-page/candidate/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/candidate/synthetic.one create mode 100644 corpus/conflict-page/cold-delete/commands.jsonl create mode 100644 corpus/conflict-page/cold-delete/machine.json create mode 100644 corpus/conflict-page/cold-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment create mode 100644 corpus/conflict-page/cold-delete/read/environment.json create mode 100644 corpus/conflict-page/cold-delete/read/hierarchy.xml create mode 100644 corpus/conflict-page/cold-delete/read/page-000.navigation.json create mode 100644 corpus/conflict-page/cold-delete/read/page-000.png create mode 100644 corpus/conflict-page/cold-delete/read/page-000.xml create mode 100644 corpus/conflict-page/cold-delete/read/payloads.json create mode 100644 corpus/conflict-page/cold-delete/run.json create mode 100644 corpus/conflict-page/cold-delete/scripts/cold.ps1 create mode 100644 corpus/conflict-page/cold-delete/scripts/read.ps1 create mode 100644 corpus/conflict-page/cold-delete/source.json create mode 100644 corpus/conflict-page/cold-delete/teardown.json create mode 100644 corpus/conflict-page/cold/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment create mode 100644 corpus/conflict-page/cold/before-read/environment.json create mode 100644 corpus/conflict-page/cold/before-read/hierarchy.xml create mode 100644 corpus/conflict-page/cold/before-read/page-000.xml create mode 100644 corpus/conflict-page/cold/before-read/payloads.json create mode 100644 corpus/conflict-page/cold/cold-conflict.json create mode 100644 corpus/conflict-page/cold/cold-conflict.png create mode 100644 corpus/conflict-page/cold/cold-delete.json create mode 100644 corpus/conflict-page/cold/cold-delete.png create mode 100644 corpus/conflict-page/cold/cold-main.json create mode 100644 corpus/conflict-page/cold/cold-main.png create mode 100644 corpus/conflict-page/cold/cold-menu.json create mode 100644 corpus/conflict-page/cold/cold-menu.png create mode 100644 corpus/conflict-page/cold/cold-shown.json create mode 100644 corpus/conflict-page/cold/cold-shown.png create mode 100644 corpus/conflict-page/cold/commands.jsonl create mode 100644 corpus/conflict-page/cold/machine.json create mode 100644 corpus/conflict-page/cold/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one create mode 100644 corpus/conflict-page/cold/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/cold/notebook/synthetic.one create mode 100644 corpus/conflict-page/cold/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment create mode 100644 corpus/conflict-page/cold/read/environment.json create mode 100644 corpus/conflict-page/cold/read/hierarchy.xml create mode 100644 corpus/conflict-page/cold/read/page-000.xml create mode 100644 corpus/conflict-page/cold/read/payloads.json create mode 100644 corpus/conflict-page/cold/run.json create mode 100644 corpus/conflict-page/cold/scripts/cold.ps1 create mode 100644 corpus/conflict-page/cold/scripts/read.ps1 create mode 100644 corpus/conflict-page/cold/source.json create mode 100644 corpus/conflict-page/cold/teardown.json create mode 100644 corpus/conflict-page/native-delete/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment create mode 100644 corpus/conflict-page/native-delete/before-read/environment.json create mode 100644 corpus/conflict-page/native-delete/before-read/hierarchy.xml create mode 100644 corpus/conflict-page/native-delete/before-read/page-000.xml create mode 100644 corpus/conflict-page/native-delete/before-read/payloads.json create mode 100644 corpus/conflict-page/native-delete/cold-conflict.json create mode 100644 corpus/conflict-page/native-delete/cold-conflict.png create mode 100644 corpus/conflict-page/native-delete/cold-delete.json create mode 100644 corpus/conflict-page/native-delete/cold-delete.png create mode 100644 corpus/conflict-page/native-delete/cold-main.json create mode 100644 corpus/conflict-page/native-delete/cold-main.png create mode 100644 corpus/conflict-page/native-delete/cold-menu.json create mode 100644 corpus/conflict-page/native-delete/cold-menu.png create mode 100644 corpus/conflict-page/native-delete/cold-shown.json create mode 100644 corpus/conflict-page/native-delete/cold-shown.png create mode 100644 corpus/conflict-page/native-delete/commands.jsonl create mode 100644 corpus/conflict-page/native-delete/machine.json create mode 100644 corpus/conflict-page/native-delete/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one create mode 100644 corpus/conflict-page/native-delete/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-delete/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-delete/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment create mode 100644 corpus/conflict-page/native-delete/read/environment.json create mode 100644 corpus/conflict-page/native-delete/read/hierarchy.xml create mode 100644 corpus/conflict-page/native-delete/read/page-000.xml create mode 100644 corpus/conflict-page/native-delete/read/payloads.json create mode 100644 corpus/conflict-page/native-delete/run.json create mode 100644 corpus/conflict-page/native-delete/scripts/cold.ps1 create mode 100644 corpus/conflict-page/native-delete/scripts/read.ps1 create mode 100644 corpus/conflict-page/native-delete/source.json create mode 100644 corpus/conflict-page/native-delete/teardown.json create mode 100644 corpus/conflict-page/native-pages/a/a-merged.json create mode 100644 corpus/conflict-page/native-pages/a/a-merged.png create mode 100644 corpus/conflict-page/native-pages/a/command-0001.json create mode 100644 corpus/conflict-page/native-pages/a/command-0002.json create mode 100644 corpus/conflict-page/native-pages/a/command-0003.json create mode 100644 corpus/conflict-page/native-pages/a/command-0004.json create mode 100644 corpus/conflict-page/native-pages/a/command-0005.json create mode 100644 corpus/conflict-page/native-pages/a/command-0006.json create mode 100644 corpus/conflict-page/native-pages/a/command-0007.json create mode 100644 corpus/conflict-page/native-pages/a/command-0008.json create mode 100644 corpus/conflict-page/native-pages/a/command-0009.json create mode 100644 corpus/conflict-page/native-pages/a/command-0010.json create mode 100644 corpus/conflict-page/native-pages/a/command-0011.json create mode 100644 corpus/conflict-page/native-pages/a/command-0012.json create mode 100644 corpus/conflict-page/native-pages/a/command-0013.json create mode 100644 corpus/conflict-page/native-pages/a/command-0014.json create mode 100644 corpus/conflict-page/native-pages/a/commands.jsonl create mode 100644 corpus/conflict-page/native-pages/a/hierarchy-0007.xml create mode 100644 corpus/conflict-page/native-pages/a/hierarchy-0013.xml create mode 100644 corpus/conflict-page/native-pages/a/listed.json create mode 100644 corpus/conflict-page/native-pages/a/machine.json create mode 100644 corpus/conflict-page/native-pages/a/teardown.json create mode 100644 corpus/conflict-page/native-pages/b-published/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one create mode 100644 corpus/conflict-page/native-pages/b-published/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-pages/b-published/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-pages/b-published/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-pages/b/b-merged.json create mode 100644 corpus/conflict-page/native-pages/b/b-merged.png create mode 100644 corpus/conflict-page/native-pages/b/command-0001.json create mode 100644 corpus/conflict-page/native-pages/b/command-0002.json create mode 100644 corpus/conflict-page/native-pages/b/command-0003.json create mode 100644 corpus/conflict-page/native-pages/b/command-0004.json create mode 100644 corpus/conflict-page/native-pages/b/command-0005.json create mode 100644 corpus/conflict-page/native-pages/b/command-0006.json create mode 100644 corpus/conflict-page/native-pages/b/command-0007.json create mode 100644 corpus/conflict-page/native-pages/b/command-0008.json create mode 100644 corpus/conflict-page/native-pages/b/command-0009.json create mode 100644 corpus/conflict-page/native-pages/b/command-0010.json create mode 100644 corpus/conflict-page/native-pages/b/commands.jsonl create mode 100644 corpus/conflict-page/native-pages/b/hierarchy-0001.xml create mode 100644 corpus/conflict-page/native-pages/b/hierarchy-0005.xml create mode 100644 corpus/conflict-page/native-pages/b/hierarchy-0009.xml create mode 100644 corpus/conflict-page/native-pages/b/listed.json create mode 100644 corpus/conflict-page/native-pages/b/machine.json create mode 100644 corpus/conflict-page/native-pages/b/teardown.json create mode 100644 corpus/conflict-page/native-pages/clients.json create mode 100644 corpus/conflict-page/native-pages/initial/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-pages/initial/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-pages/linux-teardown.json create mode 100644 corpus/conflict-page/native-pages/linux.json create mode 100644 corpus/conflict-page/native-pages/merged.json create mode 100644 corpus/conflict-page/native-pages/merged/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one create mode 100644 corpus/conflict-page/native-pages/merged/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-pages/merged/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-pages/merged/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-pages/native_conflict.py create mode 100644 corpus/conflict-page/native-pages/scripts/cold.ps1 create mode 100644 corpus/conflict-page/native-pages/scripts/collaborate.ps1 create mode 100644 corpus/conflict-page/native-pages/scripts/network.ps1 create mode 100644 corpus/conflict-page/native-pages/scripts/stress.ps1 create mode 100644 corpus/conflict-page/native-pages/scripts/text.ps1 create mode 100644 corpus/conflict-page/native-pages/server.jsonl create mode 100644 corpus/conflict-page/native-restore/a/a-merged.json create mode 100644 corpus/conflict-page/native-restore/a/a-merged.png create mode 100644 corpus/conflict-page/native-restore/a/command-0001.json create mode 100644 corpus/conflict-page/native-restore/a/command-0002.json create mode 100644 corpus/conflict-page/native-restore/a/command-0003.json create mode 100644 corpus/conflict-page/native-restore/a/command-0004.json create mode 100644 corpus/conflict-page/native-restore/a/command-0005.json create mode 100644 corpus/conflict-page/native-restore/a/command-0006.json create mode 100644 corpus/conflict-page/native-restore/a/command-0007.json create mode 100644 corpus/conflict-page/native-restore/a/command-0008.json create mode 100644 corpus/conflict-page/native-restore/a/command-0009.json create mode 100644 corpus/conflict-page/native-restore/a/command-0010.json create mode 100644 corpus/conflict-page/native-restore/a/command-0011.json create mode 100644 corpus/conflict-page/native-restore/a/command-0012.json create mode 100644 corpus/conflict-page/native-restore/a/command-0013.json create mode 100644 corpus/conflict-page/native-restore/a/command-0014.json create mode 100644 corpus/conflict-page/native-restore/a/command-0015.json create mode 100644 corpus/conflict-page/native-restore/a/command-0016.json create mode 100644 corpus/conflict-page/native-restore/a/command-0017.json create mode 100644 corpus/conflict-page/native-restore/a/command-0018.json create mode 100644 corpus/conflict-page/native-restore/a/command-0019.json create mode 100644 corpus/conflict-page/native-restore/a/command-0020.json create mode 100644 corpus/conflict-page/native-restore/a/command-0021.json create mode 100644 corpus/conflict-page/native-restore/a/command-0022.json create mode 100644 corpus/conflict-page/native-restore/a/command-0023.json create mode 100644 corpus/conflict-page/native-restore/a/command-0024.json create mode 100644 corpus/conflict-page/native-restore/a/command-0025.json create mode 100644 corpus/conflict-page/native-restore/a/command-0026.json create mode 100644 corpus/conflict-page/native-restore/a/commands.jsonl create mode 100644 corpus/conflict-page/native-restore/a/hierarchy-0019.xml create mode 100644 corpus/conflict-page/native-restore/a/hierarchy-0025.xml create mode 100644 corpus/conflict-page/native-restore/a/listed.json create mode 100644 corpus/conflict-page/native-restore/a/machine.json create mode 100644 corpus/conflict-page/native-restore/a/teardown.json create mode 100644 corpus/conflict-page/native-restore/b-published/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one create mode 100644 corpus/conflict-page/native-restore/b-published/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-restore/b-published/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-restore/b-published/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-restore/b/b-merged.json create mode 100644 corpus/conflict-page/native-restore/b/b-merged.png create mode 100644 corpus/conflict-page/native-restore/b/command-0001.json create mode 100644 corpus/conflict-page/native-restore/b/command-0002.json create mode 100644 corpus/conflict-page/native-restore/b/command-0003.json create mode 100644 corpus/conflict-page/native-restore/b/command-0004.json create mode 100644 corpus/conflict-page/native-restore/b/command-0005.json create mode 100644 corpus/conflict-page/native-restore/b/command-0006.json create mode 100644 corpus/conflict-page/native-restore/b/command-0007.json create mode 100644 corpus/conflict-page/native-restore/b/command-0008.json create mode 100644 corpus/conflict-page/native-restore/b/command-0009.json create mode 100644 corpus/conflict-page/native-restore/b/command-0010.json create mode 100644 corpus/conflict-page/native-restore/b/command-0011.json create mode 100644 corpus/conflict-page/native-restore/b/command-0012.json create mode 100644 corpus/conflict-page/native-restore/b/command-0013.json create mode 100644 corpus/conflict-page/native-restore/b/command-0014.json create mode 100644 corpus/conflict-page/native-restore/b/commands.jsonl create mode 100644 corpus/conflict-page/native-restore/b/hierarchy-0001.xml create mode 100644 corpus/conflict-page/native-restore/b/hierarchy-0003.xml create mode 100644 corpus/conflict-page/native-restore/b/hierarchy-0005.xml create mode 100644 corpus/conflict-page/native-restore/b/hierarchy-0007.xml create mode 100644 corpus/conflict-page/native-restore/b/hierarchy-0009.xml create mode 100644 corpus/conflict-page/native-restore/b/hierarchy-0013.xml create mode 100644 corpus/conflict-page/native-restore/b/listed.json create mode 100644 corpus/conflict-page/native-restore/b/machine.json create mode 100644 corpus/conflict-page/native-restore/b/teardown.json create mode 100644 corpus/conflict-page/native-restore/clients.json create mode 100644 corpus/conflict-page/native-restore/initial/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-restore/initial/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-restore/linux-teardown.json create mode 100644 corpus/conflict-page/native-restore/linux.json create mode 100644 corpus/conflict-page/native-restore/merged.json create mode 100644 corpus/conflict-page/native-restore/merged/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one create mode 100644 corpus/conflict-page/native-restore/merged/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-restore/merged/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-restore/merged/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-restore/native_conflict.py create mode 100644 corpus/conflict-page/native-restore/scripts/cold.ps1 create mode 100644 corpus/conflict-page/native-restore/scripts/collaborate.ps1 create mode 100644 corpus/conflict-page/native-restore/scripts/network.ps1 create mode 100644 corpus/conflict-page/native-restore/scripts/stress.ps1 create mode 100644 corpus/conflict-page/native-restore/scripts/text.ps1 create mode 100644 corpus/conflict-page/native-restore/server.jsonl create mode 100644 corpus/conflict-page/native-three/commands.jsonl create mode 100644 corpus/conflict-page/native-three/conflicts.png create mode 100644 corpus/conflict-page/native-three/expand.png create mode 100644 corpus/conflict-page/native-three/machine.json create mode 100644 corpus/conflict-page/native-three/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native-three/notebook/synthetic.one create mode 100644 corpus/conflict-page/native-three/run.json create mode 100644 corpus/conflict-page/native-three/source.json create mode 100644 corpus/conflict-page/native-three/teardown.json create mode 100644 corpus/conflict-page/native/a/a-conflict.json create mode 100644 corpus/conflict-page/native/a/a-conflict.png create mode 100644 corpus/conflict-page/native/a/a-main.json create mode 100644 corpus/conflict-page/native/a/a-main.png create mode 100644 corpus/conflict-page/native/a/a-menu.json create mode 100644 corpus/conflict-page/native/a/a-menu.png create mode 100644 corpus/conflict-page/native/a/a-shown.json create mode 100644 corpus/conflict-page/native/a/a-shown.png create mode 100644 corpus/conflict-page/native/a/commands.jsonl create mode 100644 corpus/conflict-page/native/a/machine.json create mode 100644 corpus/conflict-page/native/a/teardown.json create mode 100644 corpus/conflict-page/native/b-published/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native/b-published/notebook/synthetic.one create mode 100644 corpus/conflict-page/native/b/b-main.json create mode 100644 corpus/conflict-page/native/b/b-main.png create mode 100644 corpus/conflict-page/native/b/b-shown.json create mode 100644 corpus/conflict-page/native/b/b-shown.png create mode 100644 corpus/conflict-page/native/b/commands.jsonl create mode 100644 corpus/conflict-page/native/b/machine.json create mode 100644 corpus/conflict-page/native/b/teardown.json create mode 100644 corpus/conflict-page/native/clients.json create mode 100644 corpus/conflict-page/native/conflict.json create mode 100644 corpus/conflict-page/native/conflict/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native/conflict/notebook/synthetic.one create mode 100644 corpus/conflict-page/native/initial/notebook/Open Notebook.onetoc2 create mode 100644 corpus/conflict-page/native/initial/notebook/synthetic.one create mode 100644 corpus/conflict-page/native/linux-teardown.json create mode 100644 corpus/conflict-page/native/linux.json create mode 100644 corpus/conflict-page/native/native_conflict.py create mode 100644 corpus/conflict-page/native/scripts/cold.ps1 create mode 100644 corpus/conflict-page/native/scripts/collaborate.ps1 create mode 100644 corpus/conflict-page/native/scripts/network.ps1 create mode 100644 corpus/conflict-page/native/scripts/stress.ps1 create mode 100644 corpus/conflict-page/native/scripts/text.ps1 create mode 100644 corpus/conflict-page/native/server.jsonl create mode 100644 corpus/extended-ascii/README.md create mode 100644 corpus/extended-ascii/en/candidate/Open Notebook.onetoc2 create mode 100644 corpus/extended-ascii/en/candidate/synthetic.one create mode 100644 corpus/extended-ascii/en/cold/commands.jsonl create mode 100644 corpus/extended-ascii/en/cold/machine.json create mode 100644 corpus/extended-ascii/en/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/extended-ascii/en/cold/notebook/synthetic.one create mode 100644 corpus/extended-ascii/en/cold/read/environment.json create mode 100644 corpus/extended-ascii/en/cold/read/hierarchy.xml create mode 100644 corpus/extended-ascii/en/cold/read/page-000.navigation.json create mode 100644 corpus/extended-ascii/en/cold/read/page-000.png create mode 100644 corpus/extended-ascii/en/cold/read/page-000.xml create mode 100644 corpus/extended-ascii/en/cold/read/payloads.json create mode 100644 corpus/extended-ascii/en/cold/run.json create mode 100644 corpus/extended-ascii/en/cold/scripts/cold.ps1 create mode 100644 corpus/extended-ascii/en/cold/scripts/read.ps1 create mode 100644 corpus/extended-ascii/en/cold/source.json create mode 100644 corpus/extended-ascii/en/cold/teardown.json create mode 100644 corpus/extended-ascii/ja/candidate/Open Notebook.onetoc2 create mode 100644 corpus/extended-ascii/ja/candidate/synthetic.one create mode 100644 corpus/extended-ascii/ja/cold/commands.jsonl create mode 100644 corpus/extended-ascii/ja/cold/machine.json create mode 100644 corpus/extended-ascii/ja/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/extended-ascii/ja/cold/notebook/synthetic.one create mode 100644 corpus/extended-ascii/ja/cold/read/environment.json create mode 100644 corpus/extended-ascii/ja/cold/read/hierarchy.xml create mode 100644 corpus/extended-ascii/ja/cold/read/page-000.navigation.json create mode 100644 corpus/extended-ascii/ja/cold/read/page-000.png create mode 100644 corpus/extended-ascii/ja/cold/read/page-000.xml create mode 100644 corpus/extended-ascii/ja/cold/read/payloads.json create mode 100644 corpus/extended-ascii/ja/cold/run.json create mode 100644 corpus/extended-ascii/ja/cold/scripts/cold.ps1 create mode 100644 corpus/extended-ascii/ja/cold/scripts/read.ps1 create mode 100644 corpus/extended-ascii/ja/cold/source.json create mode 100644 corpus/extended-ascii/ja/cold/teardown.json create mode 100644 corpus/extended-ascii/ru/candidate/Open Notebook.onetoc2 create mode 100644 corpus/extended-ascii/ru/candidate/synthetic.one create mode 100644 corpus/extended-ascii/ru/cold/commands.jsonl create mode 100644 corpus/extended-ascii/ru/cold/machine.json create mode 100644 corpus/extended-ascii/ru/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/extended-ascii/ru/cold/notebook/synthetic.one create mode 100644 corpus/extended-ascii/ru/cold/read/environment.json create mode 100644 corpus/extended-ascii/ru/cold/read/hierarchy.xml create mode 100644 corpus/extended-ascii/ru/cold/read/page-000.navigation.json create mode 100644 corpus/extended-ascii/ru/cold/read/page-000.png create mode 100644 corpus/extended-ascii/ru/cold/read/page-000.xml create mode 100644 corpus/extended-ascii/ru/cold/read/payloads.json create mode 100644 corpus/extended-ascii/ru/cold/run.json create mode 100644 corpus/extended-ascii/ru/cold/scripts/cold.ps1 create mode 100644 corpus/extended-ascii/ru/cold/scripts/read.ps1 create mode 100644 corpus/extended-ascii/ru/cold/source.json create mode 100644 corpus/extended-ascii/ru/cold/teardown.json create mode 100644 crates/notebook/examples/smb_publish_probe.rs create mode 100644 crates/notebook/examples/support/edit.rs create mode 100644 crates/onestore/examples/scratch_dump.rs create mode 100644 crates/onestore/examples/support/typing.rs delete mode 100644 crates/onestore/src/page/write.rs create mode 100644 crates/onestore/tests/conflict.rs create mode 100644 crates/onestore/tests/support/ops.rs create mode 100644 tools/native_conflict.py create mode 100644 tools/test_conflict_page.py create mode 100644 tools/test_extended_ascii.py diff --git a/corpus/conflict-page/README.md b/corpus/conflict-page/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ffe296770ee5a58ef7a9d29c5301215a1dbf9bfa --- /dev/null +++ b/corpus/conflict-page/README.md @@ -0,0 +1,77 @@ +# Conflict pages + +What OneNote 2010 does when a sync finds that two clients changed the same object, and +that it reads and deletes the conflict pages Rust writes. `tools/native_conflict.py` +made every capture; `tools/test_conflict_page.py` checks them without a VM. + +`native` is two OneNote clients on one Samba notebook (`native_conflict.py sync`, fixture +`native-ink/cold-ui-ink`): client A, offline, rewrote the first outline's paragraph and the +second outline's; client B rewrote the same first paragraph and the left table cell and +published (`b-published`); A reconnected and synced (`conflict`). A kept B's version of the +paragraph both changed and merged its own disjoint edit into the page; its own version of the +page became a conflict page: + +- its own object space, listed only in the page manifest's `ChildGraphSpaceElementNodes` + (`0x2c001d63`), never in the page series, with a copy of its metadata in the manifest's + `0x24003442` whose identity is the space's XOR the metadata salt (MS-ONE 2.2.81); +- metadata root `jcidConflictPageMetaData` (`0x20038`): `CachedTitleString`, a fresh + `NotebookManagementEntityGuid`, `PageLevel`, both schema revisions (`0x28`), + `TopologyCreationTimeStamp` of the merge, `ConflictingUserName` and + `ConflictingUserInitials` of the client whose version it is; +- a page node with `IsReadOnly`, `Deletable` and `IsConflictPage`, holding A's whole page; + only the rich text of the paragraph both changed has `IsConflictObjectForSelection` and + `IsConflictObjectForRender`; +- the page's metadata and the section's copy of it gain `HasConflictPages`. + +Both clients show the same UI (`a/`, `b/`): the page's tab carries a conflict mark and a +yellow bar reads "This page has changes that could not be merged during synchronization. +Click here to show versions of the page with unmerged changes." Clicking it lists the +conflict pages under the page, each as its date and user ("9/26/2026 snow"), and opens the +first; that page's bar reads "Conflicting changes are highlighted in red. This page cannot be +edited, but you can copy changes to the primary page. Click here for more options." over a +light red highlight (RGB 255, 214, 214), and its menu offers Delete Conflict Page, Copy Page +To..., Select Previous/Next Conflicting Change and Collapse Conflict Pages. + +`native-three` is `evidence/m8/conflict-abrupt-cold-01`: three clients' conflict pages under +one page. OneNote appends each to `ChildGraphSpaceElementNodes` and lists them last first +(`conflicts.png`); the metadata copies are in their own order. + +`native-delete` is OneNote's Delete Conflict Page on `native/conflict` in a fresh clone +(`native_conflict.py cold --delete`): the manifest loses `ChildGraphSpaceElementNodes` (its +last entry) but keeps the metadata copy, the page's metadata and the section's copy lose +`HasConflictPages`, and the conflict page's space is left as it was. + +`native-pages` and `native-restore` are page-list merges (`native_conflict.py pages`, +`restore`): client A, offline, moved pages through COM and edited `Target`'s body while +client B moved pages and deleted `Target`; A reconnected (`merged`, both clients' `listed.json` +and screenshots). OneNote merges page series, not page positions. A page a client moves +gets a series of its own, and a COM move also re-series every page it jumps over (a drag +re-series only the dragged pages, `page-lifecycle/movement`). The merge keeps each page in +the series of the client that re-seriesed it, the server's where both did, leaving the +other's series empty (`native-pages` keeps two, which MS-ONE's one-page minimum does not +allow); series only A made go after the server's. `Target` comes back as a new page space +holding A's edit, in a new series where A had it; B's version stays in the recycle bin. + +- `native-pages`: from One, Two, Three, Four, Target, A moved Four before One and Two last + (re-seriesing One, Two, Three), B moved Four last and Three before One (re-seriesing One, + Two, Four): One, Two, Four, Three, Target. +- `native-restore`: from One, Two, Target, Three, Four, A moved Four before One + (re-seriesing One, Two, Target, Three) and B deleted Target: Four, One, Two, Target, Three. + +`page_moves_and_a_removed_page_merge_as_onenote_merges_them` in +`crates/notebook/tests/sync_pages.rs` replays both with Snowbound's queue, A's moves as +moves of the pages A re-seriesed, and reaches OneNote's order. + +`candidate` is `a_conflict_page_is_stored_as_onenote_stores_one` in +`crates/onestore/tests/conflict.rs` (`ONESTORE_CONFLICT_EXPORT`): the remote renamed the +first word of the fixture's first paragraph, this machine did too, and `SectionOp::Conflict` +kept this machine's page for "Clover Snow", whose author objects carry her initials beside +her name as OneNote's do. `cold` is OneNote's fresh read and the same UI +walk, ending in Delete Conflict Page on the Rust-written page: its bar and list entry show, +the conflict page opens (window title) with the paragraph highlighted, and the deletion +stores what `native-delete` did. + +`candidate-delete` is `deleting_a_conflict_page_stores_what_onenote_stores` +(`ONESTORE_CONFLICT_DELETE_EXPORT`): `SectionOp::Delete` of `native/conflict`'s conflict page, +which stores the objects OneNote's deletion did; `cold-delete` is its fresh read, without a +bar. diff --git a/corpus/conflict-page/candidate-delete/Open Notebook.onetoc2 b/corpus/conflict-page/candidate-delete/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..c7beb44bf509f935090fe55e7549e4b50e0ae0e9 GIT binary patch literal 4944 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/candidate/Open Notebook.onetoc2 b/corpus/conflict-page/candidate/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..c7beb44bf509f935090fe55e7549e4b50e0ae0e9 GIT binary patch literal 4944 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1Ryp$OaMy5#LCc z>i*FX7!e_WTxWXkGHyC7dZ-(g??D;-@>He(5T6N%1%VhO2Eyr`vwT5(khl<33?y!o zd~Y#`&jl4T%@#=p@nLGF>Uu2&v1Oq0+P#HFAig@3Z=rk(#2f{qAut*O0~7+V`X7|P zL4XHGB`+YuFm(x|3PjN`C9#!{u=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jg zw6K&`@AZW{cSu}5J`to<*Mp(=1S2D?1O}GZEh!8=$Q}Wi2mSTaM{uf|q z0Lu_fMDRhCvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf z@C)YhkE)-W&nNc#?f3rgAsKx46v#+yz7a6kVDJ?)m4TH1YCI}7!z|djE2r;@ntiZ5 z%;i8S1`r#h22|n0d||mS?(Mx3!CpNK%InW8|6v4@2l*n7i3j9n5Etr;D4;KZI`R7g ztbCLh79oIK+`!5OV0r+THca3$;hPMD3aDIwq!Os78(0{o$TG<61eO`uKpIqbfLfn6 z4(-7U+CRCiF{?6ukiPFKNDrt5Sy!IjqlWj1eLX1KPgNG2=*Nm;P%a+~PjV*(* z&Ww<03^R5zA^ZP)zRP>B=ld$l{AT%g>%RN_-sRkT&%O8DbIx79H`;a?G3C7`*mggI|V8 z<{aj?O>aA`bKTHl{@KB1SFN7z@a6rtjJo-{Uf6QqsYdzU-=3^{h4&V95_Wu3O52J? zD)B7Az`q;=NAjLM**v`c3M;-ICjFSXDv8s;(`|y!AK%n^cNazfyZ7fCN+?(wsZxpg zBgr-cr#$b9Q8r=w<;|KTzpe0NiY=1Nuw~|2n>f9-VpKf*5~lCR^JB;Z18SCwlrDz!5YtPRu=E@FT?x6Kl|72?s%7xElbvfEd0ma z@o5i#bF$WC@5cQ5qP7z&0Y2(#M^>oMH+wxF!ykFrleHiVc!PXpfrtb6;J4rhdw50b zTZpgVtx!(?RV_9no>0FDW#$LsU8rB^+-YASofFwAWQD#k(KzND{GG`1yz9aHd-ARqxk%TDEZ{dL3;0dQ znv!kGyUlpln_R$a&bvz9_2FG#auMc77I^n3i}(R#0WXkugUH2r2_}nh;16X%``{hz zEcBaf>)bK)`cX!h|Ix-Uay^s070u);Bxt?=uHhsKPNop(qktduR~YMfaV7|1_L(YG z>7nB-pLK8#-dlZiro46oQ}lZ=!}(_r>vUe?6m;)(Y*b7{*w)lz6K+m4nvTGvC}xUw zfmk53cDa@AOeN@GQ`6h#VOq=GsVzoUo#^ri6cN2jEDXID0Bx;QDwHrUC06!hwcRq? zB)1Xn-*;YxFtJi|CDdl!(u7VT)yyB1SY^Pt*fO@!woFCz&JEse_U<^h&wuji+a1C^ zQ(k1KT6N89n$3Zua7JawjJ0E|Q`{Kq{kDv?GkL*IUUP$f1$={c!fd1dD25(Ks*Mwc zA&xFKoGEMyg{^4&gb%HwnY1*4{}gElr?*gtGOX~bG2hF zZ!G&_G*SyKEkHzEp}jGe!l1p|t~ro8CM`fxGrLCFg9+iONUPo6N8e-w+9ZaDO|Jh% zyVJS*^Ux0vM^^%YKT0Gu&pZCGLZ(bQ^s%+);8)Y&kGW1?zu9vnS)|j890fd zC`pWByu2SXM;Q5g7S!#wY)}Vtn;~DC(tG#r%+#~C0X(wmNg$N^Y>U(_Gcs#%UzSxH6y1sPyB}1H1sy`{;}n5 z`G{w-B6HHFO(=1RDZ#+M83Sv+Wox?x@e|E{hiH-AL{V9w- zf=`A4egn|3nS-WC;vvNO~l-ZJ+>2YrLQH}y3w4jl)ub{S!p;MEy=leC<`-6cZ zVu9J7X3?BC#;cL6Czc4P^cO2~y|l)x+4F$2bdc)t;Dj2r922GWm; zfA~xIH0UH?&`+Mcy8CN)rwGQNOp~Fjng1m{tSCB!a)WXN7&?ieNR)dD&yCyw>Y$4=L3$Gl)0X;l(*Zx|JD@6E2(B1a zK{>k~6sL(|?F2v2(Ehb*E%|k&tEIQ?uzQnMDaXQJAAAlI3L~dubAH+LZB}cG#sPB& z?pfdvk3^!FIOOx&c!3snF@4}t_4IR@`Gaoh{ipwQ8;Qhnhd-CoRAZ;hn-8C>(D{bB z^~$hen@X>2$>x-%B~61E!Q8k!uS^+)mZl$x6`q8Q{U*0xc=VTx7S@f2?cHeVR10BZ zg@Hepae~ZgQ>Rf57dEFG*?(ph8UOpqAOt2&(aIP^59VmM`#sqPf6E>;dro?a$S9lBmQyxgp_+{L3;#-kR|zp!~GYURnrW|$FnUwc~6 zi?NX1XWpJG`;R_tP^(+*6GPkdsf~zYMc@FSKkqhms#ERNg659DF;3QzPr^sudxgLx zvFuLOy0j5hG!e4<@p7~CxpqG0mbV(+*in90S)>#zf+o9*W=$~U)d`k6CmA)_+WvV&4Hs6<6)TJ;<9NB#&yL-; zj4gkWG0Pk=T6v>SBLu3a+O#NogP{Q?%(IA~w)F7V$k)(v1~Pq^o=gJ$!p1R!X|_-> z@k~FaJH7X(=L9C2Nu*!IhD2W+SzO>Vp(lp{eHRn`wCVfD%Haav;P&cXytILH@YV@! z?H{yn&<}V7-G6LY3XdAPf7?(?-u~ z{kr$*uSkeaOsEHNf(7LNh7srm)_q{U>%vTx85vI-TytGw(>A{uzYQ~Ia&O&+w}p|V zEO4b)&BfdNulUOPZyWmZ9K0(V8JRpxyB9sB>n$(YXwTkmW!J)cps`uy?5B=<2mLHN z@N~;VceerK;r*4d?DRLMoSXH@`t944`zy+qK6Dq}xlwjaJF`#cgG-1lyGbZV7jEOb5O^6ms z>=?O(-i^o_lQrduG@GHylIcveQ%dr%5m{rha!O-H^xmB4YCT#Hs!!I1tc=1eD9n^x z6S4pk&y||EFs!0>Cw`|7(}NjEzuYP*7tH^@C0Av@K>mgtj`WbC3(D4rE6_1PbppOe zOjOb4dvs+jL9Pv+chRt)kx8kBC!|idLi&#s_?yb>$KSI}92%DF6(CROSuql`!I31E z0%BT{U@_c4mlh%dQBKT3tq?(?OG6QXC@v-_E`l!Y1q;dV=5nbfEv1pz|2jsEFK&rb zf`JkYd^`rk=sd=}+S0>cBiCYwig%$Y#ZofAV2(9LWu^^} z<<+&}>B3U;>>p4Lq@7?%S+^s${7d>BObSWX2y_?wSz$nT#T510bho&CHu!JqlFaH( zSsm8h4K_Ee92K{q)w!ht|Gj$i#AhuU$NTPWmU(8M)kRk8b%eNQQXZg7bKJA88UYbV z>45&uMljG-KOh1rD=-*12)b$u@ZbK%Wii?~{##(Tlde$c;>xS6pQuMQ6v zV~c`}cUfb*f{eF;ap@Aq8>lTYB^dapVnB?~CL%#}n)m=3{GVReKY-?9%7M(M(Di@J zoKDqyPkskw2SsH0onL*g(xZ;ff1vFj$_x4X!S5*7rPoqY436I+7wdsoMbgjj06v=s zGGWjTSV)A{q;S1I(wIu^DXzBOKW-3K0QyHqDdGkv^y||<`XlxK!n}!mU@&irtut%S zo4R@^K|j^CMN~wi9T7&(qbIBn4VdgE%$vINtT1mH_X}#(x^Yl&T8%_!zeiipHCeqC zX`O%`{aU9UGC6Rr(K>xD)0xS_WI8ih1DTziaE}cedUwu>jT_2F#kMiakDeyo;SXe+ z+cJumr1yJ87JSoqJI3TWt@Q|NHdx0wLt)@syOTSR+HzfbdQ;e^R7=$K?3Bg7NXnfn zJ!Lp^u=RKI@4|0gcU8XQF9E*W2Ok{rLu@+cUi26;Rx$KLQ559V?o_4Lp9YN^W`7Md}DzyPeNR6)N* z8AVYMC>%LLeh1WoMq6G1n)Q!gv>hzBL7gBG-}*lC6q}Y(u=M4+7=2k!G$oyS21j4g zl@yt!FBlNm{|&nR>1j5~MOYy7chhW?;g9ma?TfBnI$*_-sjbY1FIn#oPaQKUJS@n< ztP;>+5fbd*mRvNfgjL^fx5fBo%tQQrK1;mZ^VM4i#^A2x$@Gw)?vOVV5)kIU(Km9e z+%|QPKI8c_V=l=Rp2zep6R-MNKA0 zS3j@dYk~PeY)_E({BYi}Rqy5DFXi^7>UK!65gi)zq63C*bV$#gfAXa;`1#VWJ8eL$OEEpjZAjLS_8&H+bS@O%j~U5O zDQF8$Ji+e8;5brPhv*7>Qr&~69GBQXw^{+M4=d*VA+whgI=QJ2V%}w%94Sn*zH))4 zPx9avZ4e_>)&xYcbYZh4g_oET4E#UBfEa&BgHSD3v2!(zv`;@*52Cpm!~O4{tBI(n z%!z257h9ivh4|z5=Yi7Gjg_b`n;+q{c>A}2Cy+h;yq2(kYhC&slI7Bs1e&GS;PF8v z_49#%&B8|ptQ==S@hxFlTr8yhUfS{j~2jCD`MR6hr%00s@GI04%w)%)`|VrDmGQA2ZGO_R`YiWL5kAY# z8C)@(loYSXJ-_f~j{{?6(??BMk{@b&_mI?b{2ilBty;7*uRg1KK;+vgaYOdJP0gy- zcX<5r`ma(?of_d9H|o!83yt#Yzin)2FkO|K7`iBT;B%uzOP78d+vfGr9uiN_1Ik(c zy?0)5tYmY=By4=mHFxZ-&CC`pnv&MBj^Bp%Dfc?8i&K>r`L|eO zc=1kNUhR7i50qYYVeR(djMk~ozw$1%%-MHso^_?E$7F6b73Z%kiv0a{xzy@aI&^5B znVZ|I;oD)YzG&_0x zgr1dbrUpjs>oz;9U!d{0FHSjgbiSv_MNzH~Vx>Ij{%d>741HZNH77%i4 z$ey6gBS9BOW%hJ*Gw*$Ny!n|1%3h{wsa?2Q9VgHEQwM+TjsDF1tAIR=W`cwyf=SW%Y$VNfpnG*%OtMHBFwJ zXn9GVx~NWu&E_|THrJc)^Yj_)Y}hgFdZR}jTyj>-Dl5CU)-&r;)UipD2Iu6D9$pEl zHrh6S>#?P=sgg-iX~q+}&u`ay$L7*gM%5T{t8CrL3zDKGBd6S}le75ylsAzvUfUJ< zN!1rf#;qOFFYl#I4{!TABfeKwpM9j_!80z;+^01Dwu01y5ehgbN`GLqRv!!u|`&Z^QE7fFKi&b-; zTTFsnfmuVeoU5F$$ARZsQzYEC-4T#5wWZ+Iuf%N1>t{+JSbqNq;05AIWq7Xld?MpI3Beguh z5qo5dsY$OG;!46qAT8&4P!Qd(Jl&KG!tt$a_;j?w){SeoWVt7(~Wba0C8mhD9 z#1P01l}#IBB6VkF2Y6VU%FM|_BVCx7%5jQaBY(Uwu>|j=@wjSYFDIlHs;vX!OH2s{ z{yq$d$phHW0O4A#yQ7Y7MqT;S%LEC;JL0Au%8)&cKo3gT_dEBA)HuiRF>5ZAE61l( zJ&VVuqwJuLET8TkB(mlr@aa@O;?vE12#p#(o$}Gorvo+%9~rRavQU?+1L^2HlI>>4 zB*zdRK2nPpSI`*^pQS6{-U@jxZYGw4NvTx3E;6X*;1l>#anMe8dE819U2{z zpy(Ohk3A@*Q(t;j*{Jg`vOc2vZs0|`J?+%+K7D;K7Tfme)mvYM1pTig3PlF|jM%Bb z|0O?j-7ooY%V+zfPU&m@+?)RN4EP+3bbWlzoh#pL8}n>~@067eb>5ttxF1c1iT8Um zGoV(pOm^5zhPKnp#JkN*(Q%#FxtcjhXUJJYUb_4Y@FT#@Ff&6J)WBV9CPN?Bz)dqV z#h~Eeq9}{Zq!@)&)l9(*GdX5>N43W!Gnsh-7$eQ(AetMpi#%o>+d~(X4Fq9eEtM{=%kF;xl=fYS&q&^hS5Ad%m(3_aqXeGyzPC*ikmUw8Mup7_U~yC*}x4F6Bhj61!tF8#t^$53)l_Mc78 zrsJaPBtr;b=@_?IHxKULjV(QumMbj%zFP^@pn$nrkFPqxVE<5E4FCW^`@{Edcod1pbkkkX)01hsSxi_2Do_zg+bNA`F=ie9(c>?ZP zPL1WBXukKT{r7DcNYwL6+AuVVVUyzcT7yk&&B0{@e{g{4$oN* zO?=GGms6sDftN5~hT1U|!KbLATul*+Jjo#Z{q^GSzxU{1*^T)?_P?EwAQBKh>G;E;J^)63;x9#wD;rh4`Cg4{+JwZw;+}@8;8Ywu6$MwGG;( z9C;LPy&{Qp_E@h#{m`($r1a94$V6vPm>{1ZTi+pxO7P7AafYm%!peX2UY@LeIR$yJ z`LI2RvS9bYThW7*Z8zaOIL?A2o~aZOQJ;$BiqlKPqd?ZMVA=S%>+-|tT8+<3=u=SP1b^pYzT`+KipY;5cjNo=eKxkN`mML7Sz`EWs#9 zMkqU;3W2DgarnV}rj=!bgA)dZt!P><{E9<`$>^dPG1p9l!CdFW7d}}IRh4WZ{xLr) zL&l8eB{|Jm7GzPn>9LbL9c-J{a`vW8?g^z=c1KxNbUiTYTIKq6pTSF;Z_kmY`mcU) z*kVuT85jVJ7f}F&ryRZRLu|7zX9fADDx#2qd?R-Gh_-xld3U2KNnQ6y>bF_7-uZ;# z+7I2oA*RQrLW=5fA>W9}Jy@F2C--da@q1p}^qs*ABGTt?Y(LWkJs!e_t6YIDA4KSo zNVF8bX}!r8h6CE4}uO5z28L^`i7r$sh1;7$hf`JkY{L?W|aT!;_ zG#bjf;~(@RK|KoQRob7gn~p&Z0+_xzvJU;MO_rl>1ckP>4Dl9YZ-Ln;U{>)f6nr+?Zqdu&en< z5_>TTb}|IBNB(uMC_x0e6$R`GdnbUQ?wtV0Bhg$AEeofZDLGnCxGFLK5e$gwH>hgF zSn0cOd_Juce9X!SMr(0yM*utOkpb%nB=s62<5(4hBVv-XY{a(`%aP=9)$dH9u|C%eI}zym}7tZ*60H* z7dHK=+xZ4-*{cl2Zr(;7#Awf8W;lPzKC;CVD}7QtldPRP7n@;*?aE(ZtNw_Sb_{_| zQywaN`q7nRgmPEoK>=@@WiPz;B=7N`y5zQX`rMoIQ9fC-u~`X@`6wlyhPRM_<_FktN_Wm_K8RNdfUV`b!VRz-M)e27_{W1h2c>=hxXS zr&#RYzAN+g2;Fkk_`<>8GXGrgo)=$NmQLKU8u<^pC1K0PF60v&u`R`3r0~G@0ax}H zn})I1%9H-Eg}|R&S9)qh&)716EyeEaCIYPZLzD_2e{3x-xTTn{9ObF6J#44~`r4xg zDoqCUZuXmVzw-^dwOsdk(*tK7{h9Pmy?dZ}pC26_bF!%RxD?01exLTB z6e`VqJ&-E=*c*EQ!;g+L$1y`_C)z+pLB7J>xX4B5U(R?TgZjKMJoYHL{)HCFNqKwM z1U*`!Tc4)CZy)4)y?*OSDIR6J$wKQnu#O_9~a=5D|!LZ%isfnTp);J#ptEzeq4c~e*%0G=Gdj` z0{9gAPc1454DjN&({_zr;&kP#pQ~=F6Sb_XHeT?oj}D~`WI?d?x1@;b`WMn)D8>Io zzaZ>0L;X5D@?O8t;$&-;#fT>YOY!Mc1&pKRD+4cPvC$=l7rJ4#cfYZ+_#{<;|zD1;SMw0WhUHphWi(AzZvdB)4ul%#76XPv$quR z7UP{KQS`(a;1i-w!Sx?u?*#Ty2z!}9iv*ej4uai3QLObJ@EjbS5$@&0z6j7X0j7Q; z2K>UHoRGji77mr1U5|nSNwc2=^+KIAbPn?{4ElR;|ErQsV}k;hb_zS)#LKx4$|STu z;)u0B=7aFT2<32&H0*&_D8l?Jrl{T=zmsz<`&`v_e{}N!eLKiUj-+tNe4Kzr4+vBY zp$9U-WYNa_f<716TPqONDiD~#2P!B4?aZyv(}RX=>Ye{_BGQo6Rb#CVYj#5WYw%Q$ zK*0b{S*d+5Id~6fxqZj)=Wb`6Vz-wRh9_Y@(B;k}9llzZ2<(N&yVWcaL0D!sF|*OFh0YG0w;HFWqIq1-bPIoJwvz<<0?x%IDaVr>&^ zod0xvJ+Q~?>s2Y^Udfwm(roHR}J~O_UzhM*~Pc%$G`B2v8k#gZ($yF zp=TJh%b#5%=Iot6&S&$@Qud$yI%0Ei+eK{t!6*tI#=!{mQxzYY?(aVj zqUk3Mc!Z#zY?8mZ+h*O5fhi}NRt!-F+o#i!X4ihx1PWCx!iVYWlx`_$Gbcz7jk`~K&KQVIpAQaF`IM8LFu8W z`Vf+erc)*RFbI<+`!M*apx=+ns)auv6O^*1$JASqwfuCaf~NLOeCD4lrxg0VDhA>Cc@sJka;xSUH{= zk)8*K#_O`jMW6?AA-zx|Ilc>gSHikwN`xylL|!+tjSp>Y)9u zR*NgN)UBTwT@mV6m_93j%7@b?IQHI%tqV@8c#;q5htq^O9E1A#l5btoY5B9*z@hm7 z`g3JZwz#~vJ_|Hk!hZ7zHBlRtiC+Ei5}MbSlJ%g-g5IbH`^^j-_%H75qPdsO`9CrV z@*8t(!ivu4Z({mTf6G$ET(Axb%a(vo)TgJ?XtOl)A4&v+cDi+B!Kzti4MXSlpVQ@D z=1;wJ+etTnf&Q-<%fUe2STw@tkHG7&BF8~KFoWK4d4Ly*ODxw>ct4_Xtui1OMr5&I z54jh*jEZET)3GBfXJrcE`&LZ}zo8_{YJU?8a^W?S5#=O*enl4iQ}HnL;Vnf4FR<4M z6iS0{Pxg(&ceC$ij=ylUzH)=_$Y%2wKF+2p72r2?@h!*;Vsaw*3acaz_*rt$N4Ygb zdMIKZ#)cuyc?^AeLfrW&$ZdLcg{VA^iQwu__VX76T7^%?eLDE;)mpe}OAQ}qXg(?Y zbS19ZVc`q(47?2t#>tt(FUq{mYutLR^Yqnmj}I1*C#Wc*ay;56aXbrx)ytu8tMxbt zyrdO~PG2CvMuR$#ZCNoo4E?Mj#gx)$gDtLp7U`gX{g8Cn`tl*^tUn-~&@T8c zIt<#S%%Vg04Gi7{%z5s9#_x<}K7mz_Cp_zp8-Y*P)h`QlEqo#%bKgD37*VT=(O$_~ zf#~1`0uO2hVmYipV7^vhIqg7!B8s&qHK7W!Bj_sEmXHsz=kE>OA%;6|u@4aAlOmC! zXpo-OM!*-o9}fe3H8Q?XHPbYw#kVzfj}B?N0c5pU_-bW3+}a_zzR%>Om_Md_t#}~d zOEHNB%_zyi+5orhfnExsKcvRu>O~!otsj*cb>#H2Vs*SibbKC9%Rih-12v{nP)Tfx zgSrb^K@FX5!oG2MD*Mx&tu1Em3rK#PnS3t0OQ+)EDY5N@*ON!i^j#Y<=j(&d(>6H~ z&Z(Nrq$6c}Hpt%Yw|t?)fi2J1R)#;y^X7r?wQHP_5)1?m=~=;tp2D|>ZQBvIHkfAJx;x;G-7wnD@eBWkq8>jxRrSP)DkIiRuZ z`T+<|*oaaSyX{JO@!Q;+u`jM=&MkyaQ|x!^n=E`kM|*AQ{pyQb!^ylA>dUV@v-LeN zaM9v0zt8>0MOY82EH_!RyD%GeT`O^Nf;W!JvH`?917c*l%O-e5rK6wm0>*>DE_w-Vo|L0}}(8 zNV4koMdGc!2YE<3tbFd}HZpx}&(L;%S$!qcn=qGZU;D0ITTOj^C>9Lr`)Xb0(n_aH zLss0|)%w|zSL}C6wcA&0K1BPfW^ynh$th6Z1GPGamdf6?&myV88J`Bl8^9f0!R;x}6CqV!@r zJoKfXwacwE($f^k8WsJD?S#;m9(%-)K8H__@{8>>(3gIGg;ps@e>Q>5oEp*CHN&Kl71PY=OD~L{oFt?sE{d#}rb%CVq5q`vI)`19UQFYmFa68=F>}y= zU6PSI!#pA@CYkF?k1voTeIzgSp!{Nzs=o9>`I9FjcZSI%D<;7el|G;6{m)W*F^NQ9 zdZGR~JWq-AVw`SK>8*G!W(K7f27n|t|OW#>!Hs+2aFpOB3IpqNckJ}q8rauwpdq0W=BPaetY`(WrGBe_5 zad95WI9CiFg%q(L+=ZgFCF$x*lH73LVR5zm@3#-!pDQBEPjwjm+<$YoO)Vxlukp>^BoZKjOm)n;u&p(mKg4c)EdA82c%D zp}mmi-TUykeFg7+Ux;tVg)C^2!#O>NT1yQ1w<7lYkntjm#T2zF1A*auAjYcZJ3;iq za?oP^pGKP!^<{Mg^vvq+@J^tm?Aq7}+A`kLj;Z6z-@kww308xTK=OqFKIB8bJ5Z)< z{T8anHpAmDH2)Q`1$-cmZvTbe3~@$>lpfHxvZ?Q!nI}@DXF@x}AJjm1e8b<6{0~+f z7W2q^t;FMe8xz&jvx$P9En_hSl2MWs*0G>(D@=b$t2=+=`FiwKDgl{-Yfc|_x7UaC zdEgluRDI{_g?=A|LXtr{kNjN~o+;fKl94lEj@8RaSnVj#&hMTp=Gs}`czE}INUojV zJy*=>S>L@Ml40*ndiPv0t%Xq8bo-sqUv@1)jTlBj70{g7BacGlP73{3t`i5NqGZs1 zM-1}Qjl0DLE-)x#`b*}wt+d+@&-%vKtu#QO6F_7^Cv=yB#psOCf1qW=*o7>fGdukF zMTb!3hP{8rylwOR==}Ry0QkRxeLNfT=jo9cZDt+oVTzVBbVy#zL`ohm! z{nhu#>cTI%ues#D=92rG|DNyOfS%4#G0D4@bJQe~oYHNl)#>C|Gi3JFimi+no<4!_ ziYvHq6C&7lE|Q5uBL}sbGu^$)kN&p?B3!YW3r{3x!TDA9hj?20&ffX@=B@|L(-2bF ZJYYrPD2E9&1iai5PVc(B9^#0>`9EO)h=~9I literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/cold-delete/commands.jsonl b/corpus/conflict-page/cold-delete/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..059e40e61d665797d4683acc66d6459966fff285 --- /dev/null +++ b/corpus/conflict-page/cold-delete/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-4D3235A0 -ExpectedPages 1 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\read -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/conflict-page/cold-delete/machine.json b/corpus/conflict-page/cold-delete/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..a1f05d8179bfaf87806c989ea93c89a231bd45af --- /dev/null +++ b/corpus/conflict-page/cold-delete/machine.json @@ -0,0 +1 @@ +{"name": "m6-4d3235a0", "hostname": "ONE-M6-4D3235A0"} diff --git a/corpus/conflict-page/cold-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment b/corpus/conflict-page/cold-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment new file mode 100644 index 0000000000000000000000000000000000000000..66b6151e91f90df6a61a19ef2f4447da278e07b2 --- /dev/null +++ b/corpus/conflict-page/cold-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment @@ -0,0 +1 @@ +Fictitious attachment for native corpus. \ No newline at end of file diff --git a/corpus/conflict-page/cold-delete/read/environment.json b/corpus/conflict-page/cold-delete/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..d03335c154a59437cf873f38ab49810353d580b1 --- /dev/null +++ b/corpus/conflict-page/cold-delete/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-4D3235A0", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/conflict-page/cold-delete/read/hierarchy.xml b/corpus/conflict-page/cold-delete/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..8e71ea46462fed2239de01f765d60863222f3739 --- /dev/null +++ b/corpus/conflict-page/cold-delete/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/cold-delete/read/page-000.navigation.json b/corpus/conflict-page/cold-delete/read/page-000.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..668f002c22e396d7530675ec44f659f85b6f6a86 --- /dev/null +++ b/corpus/conflict-page/cold-delete/read/page-000.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Client B conflicting edit. - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/cold-delete/read/page-000.png b/corpus/conflict-page/cold-delete/read/page-000.png new file mode 100644 index 0000000000000000000000000000000000000000..c6d238e1b156fda4d252289eb13f6a8ed3ff0906 GIT binary patch literal 42268 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/8yv0et67E4vlXrNInJuhJVq3 +URK/7HHMzCKxQYkOuxMs7nqhWM9UiuaXxU+PBvi60uGbIsRmbacNslhJmh1OQDQGbBADBkgQRSNG +IwUIC2VPZmdoZGkZFDIIAIAeAiHi4kEzCADwEAKLxeJBZAMVRgBACpUE/QGH81d3mq7mkzgM0mcB +mk1gEBgEBgEBgE1mUAgMzmUAmsygE1mUzmkBmc0gMzmk1mUAmsyTOaTWZQCazKZgALSmiazKATWZ +QCAwCAwBAZpM4DAJlNYBMprAJlNZpM4DNJmmU1mkzgM0mcBmkzmU1gEyAAFpTSAzOaQGZzRAYBAY +BAYBMprAIDNJnAZpM0yms0mcBmkzgM0mcymsAmU1gEyms0maZAACZTSawCZTWATKawCAoBAYBAZn +NIDAJrMoBNZlAJrMpnMoBNZlAJrMpnNIDM5pAZnNJrMkzmk1AACH8C7XgWnmkzgM0msAmU1gEBgE +BgEBTOaQGATWZQCazKATWZTOaTWZTNNJrMpmmUzTSaplM00mqZTOaTVMpmmk1TKATKazSawCAzSZ +wGAQFAIDAJrMoBAZnNIDM5pAZnMpnNJrMpmmk1mUzTSaplM00mqZTNNJrMpmmk1TKZplhuAzKATW +ZQCazKAIDAIDAIDNJnAYBMprAJlM4DNJnMprNJnMk1mkzmSazRM5kms0TOZJnMprNEzmSazQtKaQ +GZzRNZlAIDAIDAIDAJlNYAmU1gEymsAmU1mkzmU1miZzKazRM5kmcyTWaJnMk1mkzmSazRM5kAUU +RgBAAAAJBAUURgAAAAASABEgXhT+65lz/EiPi+8SFvjqCAk+gEcDAa/89Qd0AxVGAEAKABEg2R87 +QQrSFUA=AOECHQJSegFgKgAaH4tCcEWEdYC+0EXZBPqHlTLy7BdBuMAWv/Mr9HreuxOL5V6zSJyboSVat1ES +v+xxzMwisUGJDrsTLO56oVjPVIrml8VPjwb4utLhmyLEZm2nDbJYSZodTkA0BmwQAwZIEEUjRiMF +CAtlT2ZnaGRpGRQyCACAHgIh4uJBMwgA8BACi8XiQWQDFUYAQArKAUuH88s3nkzmkzgM0mcBmkzg +KAQGAQGATWZQCAzOaQGZzKATWZTOaQGZzSAzOaTWZQCazJM5pNZlAJrMpmAAAACH8D9XgeBJlM0z +mRNZpNZpM4DAJlNYBAUAgMAgMzmkBgE1mUAmsyTOaTWZTOaTVMpnNJqmUzTSaplM00mqZTOZTNNJ +qmUzTSagBRRGAIAAAAkEBRRGAAAAABIAESDPTQ4xwIZyTqQ8FVDbHxeKCj9AI5rARvPPUDugAxVG +AEAKABEgLMlfQdkfI0A=iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9J1uoA +AAAASUVORK5CYII= diff --git a/corpus/conflict-page/cold-delete/read/payloads.json b/corpus/conflict-page/cold-delete/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..de140b63b49c11437c4b611e7bf1ba60bd5f31b3 --- /dev/null +++ b/corpus/conflict-page/cold-delete/read/payloads.json @@ -0,0 +1,10 @@ +[ + { + "sha256": "af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7", + "name": "fictitious-attachment.txt", + "object": "{577D18B7-BF93-4AA8-9305-80E5A4F26E9B}{11}{B0}", + "kind": "InsertedFile", + "page": "{DE321CF1-6751-492B-959C-42BC27DEE80E}{1}{B0}", + "bytes": 43 + } +] \ No newline at end of file diff --git a/corpus/conflict-page/cold-delete/run.json b/corpus/conflict-page/cold-delete/run.json new file mode 100644 index 0000000000000000000000000000000000000000..7c3bfe43bd28d9130a244a2123cb5a1665748154 --- /dev/null +++ b/corpus/conflict-page/cold-delete/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/gate/candidate-delete", + "expected_pages": 1, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": false, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/conflict-page/cold-delete/scripts/cold.ps1 b/corpus/conflict-page/cold-delete/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/conflict-page/cold-delete/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/conflict-page/cold-delete/scripts/read.ps1 b/corpus/conflict-page/cold-delete/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/conflict-page/cold-delete/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/conflict-page/cold-delete/source.json b/corpus/conflict-page/cold-delete/source.json new file mode 100644 index 0000000000000000000000000000000000000000..c1ef9fe53decc033de306870451ed968b741f78c --- /dev/null +++ b/corpus/conflict-page/cold-delete/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 4944, + "sha256": "36274ad5cc077c6b1ad51ee4c4b90d3b6798a45ce446cbfd7c9d7ac504776b28", + "mtime_ns": 1790473161246592184 + }, + { + "path": "synthetic.one", + "bytes": 41728, + "sha256": "78e247a18c1fe2c8d4de66dd8bf79bf918636bfb42933a5533a91c8f21939de0", + "mtime_ns": 1790473248313861507 + } +] diff --git a/corpus/conflict-page/cold-delete/teardown.json b/corpus/conflict-page/cold-delete/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/cold-delete/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/cold/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment b/corpus/conflict-page/cold/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment new file mode 100644 index 0000000000000000000000000000000000000000..66b6151e91f90df6a61a19ef2f4447da278e07b2 --- /dev/null +++ b/corpus/conflict-page/cold/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment @@ -0,0 +1 @@ +Fictitious attachment for native corpus. \ No newline at end of file diff --git a/corpus/conflict-page/cold/before-read/environment.json b/corpus/conflict-page/cold/before-read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..6f6316cb1e6b9b42bb515e8c9bb2673249eca696 --- /dev/null +++ b/corpus/conflict-page/cold/before-read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-FA8F1E62", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/conflict-page/cold/before-read/hierarchy.xml b/corpus/conflict-page/cold/before-read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..712236a875d72e75cb8e64fa22404a7e7ad56182 --- /dev/null +++ b/corpus/conflict-page/cold/before-read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/cold/before-read/page-000.xml b/corpus/conflict-page/cold/before-read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..eb35bbfb1c1b8d416a12b3d07500528f3d87ad66 --- /dev/null +++ b/corpus/conflict-page/cold/before-read/page-000.xml @@ -0,0 +1,26 @@ + +Remote: café, 東京, مرحبا]]>AK4FHQSiAaIBAWAqABofi0JwRYR1gL7QRdkE+oeVMvLsF0G4wBa/8yv0et67E4vlXrNInJuhJVq3 +URK/7HHMzCKxQYkOuxMs7nqhWM9UiuaXxU+PBvi60uGbIsRmbacNslhJmh1OQDQGbBADBkgQRSNG +IwUIC2VPZmdoZGkZFDIIAIAeAiHi4kEzCADwEAKLxeJBZAMVRgBACpUE/QGH81d3mq7mkzgM0mcB +mk1gEBgEBgEBgE1mUAgMzmUAmsygE1mUzmkBmc0gMzmk1mUAmsyTOaTWZQCazKZgALSmiazKATWZ +QCAwCAwBAZpM4DAJlNYBMprAJlNZpM4DNJmmU1mkzgM0mcBmkzmU1gEyAAFpTSAzOaQGZzRAYBAY +BAYBMprAIDNJnAZpM0yms0mcBmkzgM0mcymsAmU1gEyms0maZAACZTSawCZTWATKawCAoBAYBAZn +NIDAJrMoBNZlAJrMpnMoBNZlAJrMpnNIDM5pAZnNJrMkzmk1AACH8C7XgWnmkzgM0msAmU1gEBgE +BgEBTOaQGATWZQCazKATWZTOaTWZTNNJrMpmmUzTSaplM00mqZTOaTVMpmmk1TKATKazSawCAzSZ +wGAQFAIDAJrMoBAZnNIDM5pAZnMpnNJrMpmmk1mUzTSaplM00mqZTNNJrMpmmk1TKZplhuAzKATW +ZQCazKAIDAIDAIDNJnAYBMprAJlM4DNJnMprNJnMk1mkzmSazRM5kms0TOZJnMprNEzmSazQtKaQ +GZzRNZlAIDAIDAIDAJlNYAmU1gEymsAmU1mkzmU1miZzKazRM5kmcyTWaJnMk1mkzmSazRM5kAUU +RgBAAAAJBAUURgAAAAASABEgXhT+65lz/EiPi+8SFvjqCAk+gEcDAa/89Qd0AxVGAEAKABEg2R87 +QQrSFUA=AOECHQJSegFgKgAaH4tCcEWEdYC+0EXZBPqHlTLy7BdBuMAWv/Mr9HreuxOL5V6zSJyboSVat1ES +v+xxzMwisUGJDrsTLO56oVjPVIrml8VPjwb4utLhmyLEZm2nDbJYSZodTkA0BmwQAwZIEEUjRiMF +CAtlT2ZnaGRpGRQyCACAHgIh4uJBMwgA8BACi8XiQWQDFUYAQArKAUuH88s3nkzmkzgM0mcBmkzg +KAQGAQGATWZQCAzOaQGZzKATWZTOaQGZzSAzOaTWZQCazJM5pNZlAJrMpmAAAACH8D9XgeBJlM0z +mRNZpNZpM4DAJlNYBAUAgMAgMzmkBgE1mUAmsyTOaTWZTOaTVMpnNJqmUzTSaplM00mqZTOZTNNJ +qmUzTSagBRRGAIAAAAkEBRRGAAAAABIAESDPTQ4xwIZyTqQ8FVDbHxeKCj9AI5rARvPPUDugAxVG +AEAKABEgLMlfQdkfI0A=iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9J1uoA +AAAASUVORK5CYII= diff --git a/corpus/conflict-page/cold/before-read/payloads.json b/corpus/conflict-page/cold/before-read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..88ae247d1cd3dee9c2ac3f05912170135543e7cb --- /dev/null +++ b/corpus/conflict-page/cold/before-read/payloads.json @@ -0,0 +1,10 @@ +[ + { + "sha256": "af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7", + "name": "fictitious-attachment.txt", + "object": "{EEA4864A-EC4F-48DD-94E4-A54023F53C64}{11}{B0}", + "kind": "InsertedFile", + "page": "{67EB820C-348D-4B5E-927D-6719A0D9BAF1}{1}{B0}", + "bytes": 43 + } +] \ No newline at end of file diff --git a/corpus/conflict-page/cold/cold-conflict.json b/corpus/conflict-page/cold/cold-conflict.json new file mode 100644 index 0000000000000000000000000000000000000000..2797bae39ee1a51a132ddf62bd0f0a7b5fb6f9e9 --- /dev/null +++ b/corpus/conflict-page/cold/cold-conflict.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Snowbound: caf\u00e9, \u6771\u4eac, \u0645\u0631\u062d\u0628\u0627 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/cold/cold-conflict.png b/corpus/conflict-page/cold/cold-conflict.png new file mode 100644 index 0000000000000000000000000000000000000000..3e581162febb7372e249109b1cc0fbc31e841ff2 GIT binary patch literal 43617 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/cold/cold-delete.json b/corpus/conflict-page/cold/cold-delete.json new file mode 100644 index 0000000000000000000000000000000000000000..61e9e7317ecda208a0500463bc8537e2a0328ff0 --- /dev/null +++ b/corpus/conflict-page/cold/cold-delete.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Remote: caf\u00e9, \u6771\u4eac, \u0645\u0631\u062d\u0628\u0627 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/cold/cold-delete.png b/corpus/conflict-page/cold/cold-delete.png new file mode 100644 index 0000000000000000000000000000000000000000..8d35fc6da74741e9c963466bd316d601d603ca2c GIT binary patch literal 40227 zcmd42by!qg+c%5~3L?@V-3_83T}n67Lk}V#F?7R#0@9%ZgS3G307Ex8AR0x|NgaS_TKAUXZ+4uYwgI_no0x@sUKotVG*b(%j;la-DAYUx(mR) zi@rlnM$UnSmA<7S|4Pp%eWwM_gLW*tti(Os(`}(?Uuv&$yR~i)4@h&5=~MdGgixaN zSLsAyL=kdJe_r0V75(t(j^N}=*ehVqz04I~;a^V%LL?WbD1P~T;Zv(zs}LH1gi4xt zwVCP9jg5`z&y^Yw*5vJ*ptkf|{3puH#z0$E`*Wq=*J{0FahXDiNIF@C>S{?bzdv!_ zhckx~$>K)31q3Ix@u3nX2*1lzd!5ep7!nv)y8T%qbTX@6f++N$tQU_^Ex=8bNKiK_ zwQVD)-{eAO?Q8?9vurIkOYeJGH$&A6LwU!Df}CgL9JOyiVP;`@^@^HjY?q#FANanP z?hYm}SLRraw#RAPm3_8DnCB1^gDlu`d5sy-3@D@2b#%AIKF<;~JV4c#*pzR(&E(OD zIMwf_pisR-Uc`2G4l)ym4L+l4>P9tfj-%~6Cp(V~t(3Z~>mnwJw7PivrqYb8>ypjl zC~CnTh2S~(S!v2{xZ8}GC+HP7{%1Gx240q*oS-@hzPXH?)R97Rx*S#*&Z0h#z^tmg zbxmtybSgQ*yuD7*US<2qQ9!NT`6tS7v}TyVLMj@3>qoV#_)U$20f%p{#s*5tOu6@3 z&HwE6yL#I;HEA_8Y1Nl!)H5;HlEaa0>rrOaxF38rR}H5nq$kv3QkrUf6vEV$^%-TW zeyZgJuf{=)s!OD-vmJVMCcgTX)lnGcmAUoJ{3$N9t198g=4twEo$`60zz3Nvo)y7a z;Rw&!P6qo^kxt4UM9X2U_WQ^HQC$#*3g^m!)wXgif=5`eOLh;ouN=-ALe0$hJd`p{|kNyv=7NiYTT51&*6kQ1e$>AGI5i5N|pm zkt#V$viBlq&1nydIr;%UjY&^;ZG;&(HXjEr{_dV)(Q*HhHJ}_Qee+?xR^|+%COTf7 zWV?Gtzz49As(c4u(TCVuPso0XmWzm?68Ctgw}rN>vyRCEr^nWCUQd2RdvPkVkoL_l zC|hG)B&g@hCv9w=n{WsIIx}SR7UER)ywbU?X^F;}{}x$) z=b0!s-xnRs!yQM7nJgHI{%lu>o{^9d5i-`B61BJVa%s}Z$CsGRNiB4os@g(AK66ny zmr6FhHQ6kz-m&DG>dp-{v%W@V$jJmb7#{s5@6a(F8CQ^liTg1D=rREhA*-ECV9psu z!qeyCQj0fuESpw|t#M!d5r8)V_P+;ThZ=QRYA@70pHW{eSi5%H7_{i`w|79KO32Pc ztTzD-m8^fJ3${orX_G7`Ftf&0kV28a)(Y#&sgjCV(-pr^r_C5FRPFqVr9t%mF*+Z9 z@QeSp&J}-;PGY?^aavG+U3%Ncd*fn#&u0@^v#r5U_i*Iv@q$gHS#|pMhAGTNTgf)r zJZH57oVVE}5yLF7a!JnoVVtcP^qW&;x6F2BW#_Y9nKr(JqPnvdeacgTAgege0iIGY zaHY6w9-#tmn|89$hgI_B;*MK;D=OA1`zhAuD?#`+%#+H-AH`_H)SG6^Xotgd=BqVJ zW)QnBy=zZ|==+4piBEW_o{I=J&vo)iq-LlvurxDi?@vb%=R1@%xQ+7r{Pu>gnHflH z`dy|>v4ou+7~8GC3n&i>N`G@`$-d>i7a=y;v;z4Agy27NC zdQOtj$n|kl*WB8&ZvvCUD_@cF(v{O2Re!^WoW9|$o3Vghdl5E^su)b3X8rzDx&`8L zk=Tmug7%FVrDUH~(k4fJixZ0iTC;7L(MTba1C=Oc1^v_?J;jKBWY(WrTL1YFgvP?q zIXyiycn`NTGlcecrCO-yb58;d#h&L|ac9Fg`(D*?qAVcSKg9Y?# zQ*b}dPJTyK^`dKR!`3hrNVV(=Rjey?oHiSPO;m-)xDlvnx0NTai6`y*s}Mu$Oh<%A zCwFOdVCpOY2;a#&?4D}>Kb>4$T!b-ZsK`fC$SpLW#q*q;wb`dN?Nv!-yAt+~xmoWz_$-kZfYt2zh+}MokcKH+AZQW6Xr;C^HqVe#J8uB1Hmxso zjJDU7EZ@A1W2;qAxoElxbe~UnK7Z`1aQQy1m>p z3`FH6mzlLf&R=CS15o<72GJ1rT?8+{U#Y-6M?q9q@v}rno;)S5ZndF;kUq?qH$jry zr2CjkPBEX5>a+CQuj_-h4~rERVPq2sGi$G_Fo|GKqY0L1iU1aX!ShnMz^;?Gt1^RX z<6?iJ6DRHS?Zn1iSKONE2r-T^l6|oD428%uEs1g`ZErCl+fZ9?If)M-?%+J(jc7NA z^J>STfoIdzVtv^L*yDIcZ{k*8)%j4;rwsIym3M^}+MJhhaXek=@a{|Ux?8UEhfRN; z%Qm(F}A!Je;F|5H9GLc3%6}R>yJ{u2k){Xp^W1YDqId}tcTz5mh#Yk zpD>%xal%+o9p7AMZ0id}g_)TNLutbBj;gb%Oz0NCvgRA^c$5hRSFk4neE2&ldEd`^ ziG{S>!*uEzF~08Xe|R2pdscEM=w>(BqS0^Dczl>v8}utErD%V4-g1|ysN{I0N<#AC z>X8QV7Mq`ZjUCOy_RV*mW>u{%%Y#!Z6G;|Vk96ABSpXD#Cob4M&fY!IR;dgzEmt$$qSAIqV{cqdHAsb0lpF%H9Y|AarrCSwKb(Cwj>l!W zcCpZ`)la`noXFTRJTNrqi+}H+h1%tnda~K+*Z25NH?3b6qE1Un+lwn)a(a!reX>bpI8}bVgv?L|Ek_?X266}0xJUuNDDwpoC zKdM#@mC>tZ&SjZd9nY13RJ>*d)v4(T@6rpj`CeVXy6K|pR&a_)O=0496=A$4gOtlN zkKa(C-Lq-6lj|wh$gZm6`N_}7j+}XjK+-O0l~Tk^vCh|`*&o7FdR6mh3)Tts>!7o8 zyCIvGcegK8M`zq^24?iExU^nzhx?xj#~+$jDDK{kJ2a;#&5{lh*kdcJa&tWacN)cL z#-Et!^7q&%RB2u$nY8h*)eRucjy-fvOns@3GEQg4bK?v_!ig8tcsG}L?+hIH{;CR{ zKswdDwA*GUX5{q7MtLCa@PWeWg6HD7$Lsv|Loo1vUARlT;)*5>|^s0Tar;Yw# zBDpQiE$>)$QHibOwsmv8uK8fJFNogtYN6b$)$0x1P8)=jp~Bua?YhRnpOKJ8r}fw7 z<*|m=4>S}%9|;oVxLvHL8;&>oIj_PJg--6py1P9E|_`A8!eu)rT*HcP&kA=!k7ww zx0bKcH)d)Y`t1CfOZjh{o{`jZqC3IlJ}UQ~0)I5EMH<$3_}bpXQQQ7H|Lrg%>JLi+ zow3*uu8d=7D54n$J`RjD^bd+iZB*ZbI z+clX@#qKc@lgroq0EesqZEnz*>CUYb+3CYG7yJ%PF?elM3p_kFqZ`L_WcuAn>@}8K z#jY9aW}@12A}N&}m~tCB*`Iz~y?C~7(PuML7hMjI%{`^KJFREHU(zLT-qlf();Tg_ zm-$gO*McrvXnSJuVA^rqd_p)z>(o#qi7(T*Zf7^M?%wo|_)X@oOAYLDN(H{$Gvkl; z%}BvdT82!W{NiJhNB8df;S*T!OujShY9ns*o_b=TJDuHeeT^Ns(#+5wA>$Z{3RjHy zQsJycP%WbU^&oCaff$;H+P@1XFzEVFmLG3YB0K?s@(o;ld*)ylcTh}5PG<)*bzf+bH#jRGpE{VyBtM-^!%IK8Z-K)*ztEx>T*B|`|-Nc4JbaTaZ zH5QH&p8uVM?aDQx_Ua*@NgQYDr%h>jT&6fp9W%M(FDRjft?*2!Hg-w2k(xu=5>e^8 z_z8*bIGx_@U{DIy{5d&uTG{l( z6T^>9tof@%NRs2nY~0!r2C@4XXz-bvmKPM z!??_V>Q%X&AWCgI1|{i^3`!Nd$+E$%8D{nx8Xg=Sp7tuMC{p0ZQx7!SZ4Aj98K;c7 z`UAzZItrGb%#e!h4Wgl+8pHUCfl~$t4-H`Ap+rm7tYhOM#Q&eZE)*z4@}FDmMft6t zF&#~){BtP^=+?BKl)e!B4K(H8nDuWDXhH@zKKQk@5MXyQQWx{^bDG8I5x!hzYo~ai z=Sirf{7d;R`E!!M6igE-+paY{`XKy zZ?s2=G9-}jJFAiS#Fe9#W0m8dlYrNzgPD!Vl6`XuUhYI)>oq3V0|^R(SMK*&AX|Jt z7#+=tskKTNcAu~D8VIkwGlH&5+&P5MaPu_zPajek(`!a=#B6-o__~p~@wUC348x7` zR~0`bMB9Pk7P#rXHh?miZoaa)Z?h*-FmSW>i0xd%u&!WW=$HWPKkZm?6KiLjxa+Jr z!qa<`-@5$BdRf{z#6ElVRZYax$hgS8NKN)@Rg50L%h(Q797Xa}WuGBifME^fAuoW2Y|L>nCx5g->!W+ zuP#7@o#&-k2#zx3hzMxd=|&!n?hP_y6oN~E;h!XF1ZPaPl%B@j=WiiD#X?!E(nAmV zax)n{!|_zcOMJn48tpBFtyPYDEmbzNf=;_RbS$k25R51BrItS}FNb+%yRV z#qlG16zJ=`pj|O%gVJ2R36~xs36aqGbY#rznr2j>^Jvf*V@o1l3>sKj+sl4E?Iy7o zvd@lCW92jJ<{Ycg>qGeX-hNgO>e9j9zPU>7MIw5OyfyK9{k_%w_T#$u=Zd7~pZg** zm=k`EaCxhzx~>20_SQ`0rEg0tmgJ_{s-@71tbDG}{hcebMV{TEv}JvYC|0XdvM~Z} zIQn3;i-dj(eY=Ek94jNMG5)-`RX>}}aZ|764R!5_r#jcccGwi57^rtge*chgSJ3t4 z*3Ubn9FNIVGF~Y6fr>~UpVoCq+ahSyH!d$Pj{e+ zD5W`?l};U5%X?&+wcl?c!@0WqVf%Qtk4i?}*LzTc^nP5{vmXhgcSzYI@)HX|_B1)G zX&mpub(bdyhMo_#DC0OB^T;XnN?M+e8ciK+z5YmC6|az8GnuuSKO`sh?t$A*4LP0Q z!Pc7mIfn;_#T41Cd9Da+>oG!UqI_VfDmx%1V^~TScSyi}e#w$WF)AYVM}Zd}Z7Yda zUaWB*gSYSf3U(gPFBa!CKOf4$B&AnkxdB{tt;fjso??6XV8VE?)t~yHM>_6lw#(?e zzt)&`hH^1Qd`mpMx1dk!@XZi5I;ZQbaVka;On$T1d7-Y=8#q1NYE@oZVU;4NBsx;@ zC_}GTUznd5ENrS@0nY_;^2bG-ijved_tl1{zfgi`%mgbsG=9zf3@dF%pE{iXZr z_15hdX&e{RWQX)>Qt4v0*@l;RL6q}EV>+cbQ{S5MLCA+<64#wjbca8Ql z8=vWd0Hgc^Ta0Be{Xodx}jC}^=_LbhthF$37~U^`XZF9O;8IBGsL`k3oS zNAK)LHe@Z$iFi#GNLOqXY$n)UQt)J)L_|ABaQ-=lo;@SJ5jiuO@zOlUWs;9{(nG7K zE@bC)^6M^(l*0_zD`_c%$FyR`!4TbR${fskfxEt2JcJ{s+wDT~^B?wHCP)5Kzs~VV z|Ag$sOTFQ|XFUH3nrJdlCHN8-65rg`d=n<@a{7Dnc^y}9jRo%bw>rapkM-NtzKQ$& z^M=D78xOdaoxu}=;-1IyHB*tQSBPwmuW@A*`BCV=cO&2i7g)S3Z`hYP%=AwxsMIN( z4DsU-d>48igy__G>b3mhmw(%YSimyX8ea1Ir@mMF6ME2`sZ7gn?Ue(+3{R^L>?{Jx ziz&=>ul?R^6}WlLyZajBmP|FinN(RFoVR;3aysO{x*O@t-;{c|)n+-0SXX(xMb8vE z>a&jRSuPM~gEQ5`0|>M-oZjN;m`s8^l7ai#{OD0@t#v_uvm-@WFMa2?PkC`^qs0z> z?s>#VTBF{m-@S4tft@h@qW}G2IiWERul+uY70Pv{z9MA7@3_{roWMZ!ObWU76<%y7 zCGIu*=d|HIUJrP&6z?8;1Vw&fNLm_-)QOEjYZ^(G%8j7;a>*f!(B2v<9WOk`mql$3+ z@Qx_5o}K=@J*L{fy=q20liB#bhjt9=1gnqLCDhhY2jAAi%ZIPo9|%@TT<@wHg{+Dw zTfuAj^AxY`#G*R&RB_<&3$}y;f?)x>El`54bJ{`tD+D$4 zCO*dB`#_?oE{p=1I-lSmv4thO*$9Zi^7j-jsFpUzUrPuXJ# zzj7AODMD#(pzZ9FBVi!wrJu{|Cd0nc%?g+xLTpVQ3Hl?w*yf z?SFDG-}_;|kM_iT`dblml@nVBKZ_L*IkQx-S9?ZR`BuIu6hzv$f0lk3xL1~SlS(*{jvB-qYh z&O;>%A{A|TN;Pzi=_NI2-$6t_9f|5oG;W0bQ)SGT-&Z`V!v#$Mr__n3eUsc~qHnGh zL@9Dm@qxf(S}s7VO2-e~=wp3Qb%sLCM!`}gawlmraCK~`Vy8G;P#UMYUK(-nshk5Y zoprd|b{X+_2d#;XAzB{Vref{D{OpSg%DS{2Rg?F5@n(2U)Mt$XGWEy09Rn|}{RT@) z_DpII_NLsOvV_m$Hr@BrrT6=(j$2hj`^Y;N5AxqJYp+fvl)gPPu~4PDy2!Q} zcw{-$-&D|lg^2m6%3_CDp0mSGf{Y}}1&C=#&8oY;8@e+@Fhn)PhRIh8kErQ}6m|}o zy=Ok~cv?bKp{FvJMsXW9vh{j)%?C_i-K_UdA|}PNgTW-Q8u;fsu7s-84|0GtqSiF*eEcrkZEu zAW8X)OLC2zAunpWS;4!2uaN_EC{+asus-`z-fxa9cZb|;r{|%>O(dVv2zXz966@K| zXu6*F?LmR|j)KyvfXdS=&!P6&^L_nF_3F!-hMrbo{}dmx@T+3C|ItfD z4I%Vm7xWqLZ$+nE?k2u)dUVYaWo6X&$lmMp2+ES;dmF^9RNs@oA9S%OLfInoc0S5~ zz5Jr?k|6*LN0jqlAJE_{EQT+b)^D{cl(e!{Ua)+2=D*ySK`e(anAcyo`l{`vHSM(e zG6eW9BC_!f1N`B`&V6P93p<_XeMTM&j-S~UQmd|Sebo}%#d6k$uhyc>e#VuIQ5Y<= zt>j)&@-#Rf7GM3;rr-&``X!4t>IcGG&<70AImza(c0pDofnYpzeKahSkg`u*cRhKJ z-$^S_O71f^Xeg=JfKajyJey<1j>avHL+>~P)aZP7Ro25`q3&&89i*%wXmi?`-{kzq z(WzW=c@S`_F?+up)gMK$01jF{H;8aVwtk82<`I@IlyverxMwYKc|(Ax_0g-8_HE!; zuqaD%e7)_cehbvCoHX;-qn7?T^6H_y}hg??4)8|b11>!c)f?vSTJe0ykdwd*d&Y! zs6JgVbiO&)Hwl9IUCEWypeqJkVG4-?zJQUE@wc9&_lsPKLtr zm#Lt5F6~+dqpIDZnx&xxr5f*benP z2>))gMaXMoJD?IwyrnBGujCxOuU+Qi!Y6hE-rjV@J58QW{+eT!nXbN4qVbYxbsdNo z+4X-j(#j#S4}Q~gi@u<`A@@3JcNo~|N6t|mJs-02qa$K(GL@VY#nZ-OJ!+09Dsm^m z3tGbahN;uMsdFsUK>^3~t1oH1rD|W%8hZfOJGSL*<&*!X^jA*&zA!6{Ovu`mK3F*e z%V;j3mP@a)CjI_&gB}D`?hb0zGEmn_Ef+{g``WK_Xn_VUrsA{$YnKlOyaUCrm$iZp z4~hA-_DRIV-%QxH&7Mw0m?<^W*h`P+kV%&|d#q40BU)9%f_PJrE4IuzH@izjOD~sP zmcA_w=G=RbIms=~(#R@));-|O0>CtdpE(hAxbZUXe}40y1tO2k@v*efv`M-6 zeJ8moiYQDelH~U1x*Q7}`W1)2v1CZhio69!?Z7+LOz-zvjUVIGJJ~6ca~=gP3E1N%~{uY75)N`#a5pbK}0HMK|`~5?VWP@0#LP@Vz*6FBPZN z&LI`CGbE!nj5|I)7anhuaM`*@cP4-e^KKXVU2jwG z^A=-+W8@#KRCobNvB}n2u`O11GO`uY!wlJgp8Z$oet!QA1r)j( zbPSW^5kGeydb8Cj!SC{@GY(|c7dyE2t}zz z$epZ>eL8vTz51fXQ@=r=5#m>~YmBc1QzwskAm}p37sD;acQwGU`fy|zV(ICLmR{13 z3?l5&;2i4x)I5g;u-`0kn^Cd6e3?xdtZ(2g_Um$^;yXX;=CziW*o(8c%sn0NhT=mv zh0=ivMDf-{!G38Gh}hn9&(zSw#86GmR%>J$>FWs^`L%oHg6`xac>{yT)TUtU9=WOb z7c{SM;YiUQfXQRHK>9b7vcbkUGZAkM@4=I~y%c}@FC4axeb8eKTw{tbB@b+elgY!} zu2x~cUI*8!L3}uOX$x6Mp^oP>7l~do=9m)rI$*|&LE2Tkt?F1-t!KTjFN0WVC(epk zR{7^)7PFpBYOjd>f;?p1i|$!;;p|*8|Af+8bqOEvZ!hj%9(n>u|63LS!7s{3X<>FE z`$ISW4$^dKdnq#&JTK04c?jyw^G7^pE|ZE%ewEla;=TzQ*)s^X7DJyP&HS#{Uw~*{$qvgXg=;ovm4)0hoHbk{=-WQ$3+cfA)K`*b@x8ZSniSda&Jq; z)z}=DA&SFlYs>QUO}NT^sH1M5hD>IA9XW5z!$Se^m^AhEb7ji z#bc~o;!q0SG=lv*0d&u6iG0Mv_AYndOvRZk-?yry6%l9w3W6yI^?5>0b{IGTLHIrJV%pWclRX=;k>eB~zC5W0!GW814vi8gyxTb2*#m4R~uJ^g_8$+VK{=$NB ze1Un?5SQwg}|H|>4_J)*DQ(uy2s zdcKr6i!Gqe(2)vFlD@=;)O?&W<5|YLER9#t0UpJ?o=o!}-T&%Y3 zO`PfQ%wDcNZhDBH*?5yTa;NFtOaaQz_6yQ}Q1kxx=Dja2&|Uv{y@?kS6Iu8vuFmlj z!sh-~>9x;Woh!nwTW@VL8}PXRnaiij zuwajYE4Kmcp5~SLU2-Orz_O2yr~@q6wC*4u_ns2WqsdSDCZ*0hLP@H`ixLO*Fgwqz zA(su1YJB4%FS%3P%>%$Gi^eQb&Ln(sV(^?csoeVfy}-tv#=E(pPxolGZD+({tdo|O?U4wDX9^WGkJULALLf#`;w1zSHI>|;@f4?pnawLMfXTdOsGYr}as(^*M3 z^Q+^6!r}+>Eztt=P7N@{Z?IJJah&P%w?Cr{ZT7^1P6OoRCg8m}At|0_ATuv3L#D_$ zAYKh}Ga{?=SPp&6r2Od3Z%4XQYs**hy~mD(^q$84dKUU`H$Jvd-qc|v@n4`#KNxn`D(}K5{Q;ioij#S_RXRkVpBfnmfO~k0@C6gH10d#0 zGVYYBF!cnt)_J7H^=1F>0CA+T8_IgqU>87+yv(npIv~fglDT&UvL6^6BhoFoy&%B$ zE2e$`bK>+8dJCBTq@-(}(hNVoM<6n4;XEPC+^GelHj&7SUv#u!{Ig{Y1H3W-15un7T)R)}QPPEb2S-N^QB$#0~OFD)RHk1o&HDAf1DPE`abX&h5%UTNzL$4j4#U%vi5n05ZOE=gR)SZMoU zga14&RExN{bZUQTak1f5lRc@miUG(3S=O;iPN1*Pr>z14nOKu!Qh0UG z!~&%$dAqb}+qtZjMeSs$q2=kRX06{52J+`|pdr0AK+9zn0z75ODK?-k2Kq{?eZd<~ zsl=D+DHD0HaJcp>b1!Lw3>`+SClMBYeJAkYvwSvk(l4rRbcS}-q^v8H< z9c~P0qlsi4Lx8WGKLKEsssrH}z3Y3*s;bB%cZTq`=#Ve_jo(DayTYT@?0b4>_>OZK zZhr2u_0*g8kw5+KP+TSe0pBj37Wi09_XS+s%&is0T7D#3v;@c^BcFgI3CXiHS|v@4 zJ}`ePi@UjQRN*z_`Cgegi(&5VmtY@#Ug;*UzHyu2QXmQDXcq=rRpA&-h11q?wzG*RuzF~S7hz*c!rX!CV( zPR?}UcPR9g==e0B|4~#AAHOJuq91kkb6K0vVGMtEdMfdI)>2VH_^OiT`U|G|6D!3a zq70%Iu2LvhQQ_04?&L`Z6c9Z!vgCEjw3T#Y23DT#|Sz&^(x1 z#KP1PU7$wB9{y7L9{rk<>6xrmXfJM_iokTzr>|daI;TO*`ZWljq!PtK{hKdIG?b+P znV`#5Up2!4qh|ZFoFqGZgWh!1ZH#Y^hL}s`Syl7Em3!Pubp~INSwo-_?|kwPd`)!e zh(~-b&#V^O(QAD1;S*K%3u$<#U97UXisvgM4Q&N86>G4OzLkPTLP=ReV{1*VwvqMN3;eKe=?OzhU0rh~oU}oJLr+4k>Sfv0WA{MzA!z(pXY#P90Ox zANuV3Ej@nsiSLCP;sU$G;D#$y6xTpSL0?6|6U|j?W93!^H=xnjw|UFs7_O_q{6JGE zYG(kmmxlDIHp%q58VOJnCYCe**iibB_-V+Vm(5b>tGA%Y^;E^s0ChIv!I3I+WuWq>9Pn%3c8 zWk1)shJuuEv9K5qBWRY;vnh7HAyO+lQG~JI-V$yq!lw@y1~hQF0d!dwM8QdhdS12P zR(hw}6O5)(fq2hl635&Q^hkqou(19Z;5ba!>Ze+9xqmd`d-SF|$vhUAC;9y3DrhnX zZ@f{4olqmt$?8e4XD#dnN85&n=56GNtsY#*zyeQ-@=F}>4i;9uK787@{>}N&R!i4* z^($Tw8$~dinxBNiMXd`|Cco|Y=U}#$`a>}VPHczEEf3E?*lXPJiUgxnpmn+(x!+S( zIym}xtxHL84PUOudO;FebMHa`)4WV*DgnXzW(#Cf)KId{GWM{l`)Z`n*D*zUqpFP5{tB&pm8AWs1W{pnJE zOVvcYpgZFqbYuPFCpwK_#wI4w(b4Yi?p1f7L|L&pIXOrq(kGDj&R>#Zuj_9TD$TO= z?W#g;&tsj+&39B8Cbgvm&l9Rc@v-qTIb(nyQGD%xf@{_RpjqsarlzKxN@#A$;`OSH zY;eJ`td*V7(0W!7eHI}H)_W6jXy4VtS~~8_iwLz@hEB7}7W;}TH$^3@MjsFIygb|b zVZq7~Z`*R^fqAz==0)jO>aJnH45#@lDnod2A?LQ_g0eEui(b-ZdQiteJI3nH%UL3k zpBnzhAA!5q)jF3?Ep2igc3*au&C)FmB}u<7Z+HXx^+`!-381OQkJIza?CW&_K?^B# zuFHOj1rQq>%LnoV?VjE|nydrF|D`3bI2~N7b zT3J~rKaK-}kh*S9WJdmA!?}6*K~ANc;g>%9+2d!Lt~h=TbB4K8NfE2MpWAb;`}_O% z_Vz>rRPTJtS|){dkf#DyceIGjS5+A{xK+0~1<*&DJS+{3W5k8txeSMl%1HQfkt2ml7NZg)2U$)`^kOrk+Gca7{LM26$d_HQekdc z*y|XI;dU{JW(OFSnMQULkYORkJ_NJvCGmLKnix>T<=H{92sMJNJ`IHCumf70iuqT{ zt$v|AdmV!4PK{&Gr(Vyy<$|uu3}Nh&RQ-gp;;&#w`27YG21IYDnDp}rqD{0O`(hf^b0n!Z5c;%Uhy z+D3}XS}f9HxD;4FAJD)N&n9$t5o7Q80Ix*E7_`t9xUYHO@;H%t_lfO z=^1GdQ`4Mpr0VqTMMRzzGscbOg3(>swMfwAWyHcSSy;{`Uwb{Cfz4X?GM1E5fEy7n zRs`XAaLqU`fG&Biz0CBf1bpiRnNasra+%q?gER5n zZ2ghcoV|{~K|~~kTcT@g$UN0}YD!;*9I9KOS%Y;#d)WO7hQ%licxaH?2K4j^FRzT= z!BMNo`~IMS#9`z146%1`$-=et%r_-G#0w%OjYE(wtbI`#SI2_RP{@gvMOsz z=J6qkG9xb67l)#>I7s@Xgb?7JmjN&eDEa0uaXPxp#gZN7Wb_euE9F)r$q~s60j76f zX#)-xe?&_1z*D7r1q}>R(liY`>cp0si1!mIHa^x;SO#!YX4)x5-sGeA@Sv}ZFIxss;e=1N2wN>c z@y!J3C;Y!}{~oCsOp5<7`a#JpTX zC!Fc>1~TtwQyfy4yu23Qqt!qA682Wh8JlM~%kCwNL}jG6&EB}mFTDKOIga+x#0{I2 zBTqhy4x59G;$Ys_;C}~{=i?fpn5NDJ3DRy8uYazziYi=;QC^3en2VA$mD!zH>1A=| z0-`U;Mnl311c)CgDZKE?W2)v4OJ=_NuPBi(VKFj&z#OeOAb9LqV=mrcZ;Jl&En`{i zk_TU*oEIaEA73E|Ch-l%Zo}U^ZK7ZVU=fKB@lK62DeMskM!zdl4rO_XN zzHJH@%XnMN=hBi28$-l>QPoYY3-1!Po^I7Dd5q3982ah8J>R35j2o)(D1nKEegv)} zJ#`!-ky)JMN#O<*!@uip>G>=fy#hf zYXw!g{v;hU5$e9iMVtc(^kC+kWI+h9WdC(VCGmnQ*<1bTAU;^AlPj7hH`B*?-JBRb zEA9%w=?0O}sZGW^P=|Z<5m?mz_8C(fo@m=<@iXk6a=JS0_-mY=J)%2+GU!nnQ8gJ9 zHbeM5ym+O~5b)sjL6#r^ah0UeZZx{$c&R?=@y&7hLvf)7alT~sb%kv_@HCuDHdk_r zfLcL3I4?E!BXBBKjp{i~TAT7z9Pr~@E8bkfU6CA}h47@iVJv`YyT^m;wo>jjXb@xl z#5TNH@2R7Zcm3>-+)rp(U{t>y3A?TNJy#!xcqtmZTK5)F7I(vRcLI1xr)LyWBlrAK zM+Gkq&-EF>leync5;@40>#+RBBlWqXM{}KjM9?(`)_Wh4el2bBm33}@i`~pNrf_Fs z#ixMG=i~#8d9##vA85Ce@V4>aTyn*k@6|!)^#m{KaAVE$w*DlJ2?vumgR6XS`tKV{ z6BxnQ><+RDaZTgN-!HJmcJqs(LHg<4IHA_j^8j#86X|2)mU)fc)+%z*l7SUb_lKTVjN}amAR%)jP3Fb)KDHW_9e%pSNSRzOeJP>6 z{1ZTVkZpGIUyfoPlX)^keFk3Er6f}x;V#CgDF}G6eMA?NmZx%iVE51dZKS*=!ExFO zmNSQcM_i+qY%G4OHFP;ozXpR^3SN_KmR~*UsEe+Rw>;8z=ornf0UOay9?YKiR+Tg~ z3L!H@Tv%Y5pT7Kv=J)_KiUppI2>}GCuzr%E!yAdl`}@8R!Q**OMPt26{mG<*F~T5RcWj=wD!>BX$Hf*yL`SQha#!}+WYEBh-*hh@!0*17 zU-l$h_1YKW?EoZNZv1v*AF}tZ8I@GF96oTrpkFyofx^h2`jZ)Zc{ixaz{s8SMGx;Xx+&9l76pUmDYFI6{ zUp_ewL{dH0!mGjMX=DK)vJi*3L<7YP8-NqkIb3!T)nU6IbI$JBZ810Tw2G z`P2dSO$(iXvE<`9jb5|zg%<+pi5?NQDntNZg`y#V1rC0OQ}W=}dvUl1ho+wy4Mq98 zn63npIN`;+U_c}V_d8cXWx3gYmN>D-#4gy|kq?!z4~lcn`P#F?vg|(DVsEc>Crp%E z#CLr77XJYg92hTGOZT1mUrg)E+~|t=`uft*(V@Sfi|(lSHsEr)HHI!Yn9!k@L43eM z#efg-FyT9>MdozB4nJitxTT|3P zU!Pn=4L9yJ&!L@||Cyt%ZUT(wzVsbNX+}ULhssw>-QWq1E)muYPD(ILPcm^4;5q+Y z5sEh+OC$2Lud=>gR8a6(^t}{^Rwgeuw>=27t7kb%inVkvn9Uzq1Z>dnYIIqY<#_4i z;pu5<_)q!^!cmi(72v-Y8eH@iD?$)m6m0JQ?G|66lOx8USfY>7nIB_9thjsdFH$Pg z&M)*PM4Ierg7L@uGbAQTO+>5_iRSnQpSb~IN^#cAm{&hwc&-NaWwp6(kM~;lKuS#; zju1&aO7Zj=LJkmiK(t;hXs*%f>hWQ{?+7~@U0C1F|1)MT2o++*4u0l&NTC4}mymFG zbDIy8uz3Hh;}IgU!9pX z1{mq`5LYk-;A5K34Jov2cxtdfj3myd5yc^2t@7J_6JUu-r)BRu*X) zaO~!fs|IXKKNJ|LlneCq%%BWrdx%Mi@6CgO-I*?w4XA4^F4^K2@Z#d)hh)5m!)TRw z!jV(%Tz}_%`yYX(D_4D7T`SlD`rMG<8pkOCGni9i%hgHN0@}N=u$ZyXX0XH?U+e1% z1@@&1sXa+z2aFVG)saF$CUxAwp4Un;H#dO4jG^lz3}fVEWVl7c*d@))vpO)yQzLYY z!NNMBM;kVj4Ui^GH-=MUiZ&wGhu|8Q#k*!O?&RBJty^?OhlPc9Q#Sv0a%h#WuWWHJ zKBV@U!*RZbEG+Q0Ako{lRPldv^eZ+iFJIHgeJ8CE7+LTkSjfAd)(kdYt?u(jDM5Y)i3e9zyLz`b>*;4RV4iC|4jn^7V9;6WmLss64vk0wG}|MBaPH#Q zwA+y!imV){jn=Ey^FcW+`%5@srq2Pq>-2hwGU$&pF&dKckI-!T@d7@sdZKRkWhjXA zlPruA40X(28|dn38|oNY7>oh5M^=g*%F3J-{xWqRZTio+5cD!!DU9c@h0&F4B|4d* zS5;`y0RIaR+6QR5=;ajsnFx$FMDL*O%&G|MPuIuZ{#(|g0f}+%1Y9_RpCULZB?(h% zVF550#l|&odZprJ&V(@o5JTuuu>J%Y16nfoyoJmq~d+L~5c23N=dr>oRe(s%p;4vEz*< z|5P*u<Z*QrSfK_~KmfkT9go{$VK@c7MUT%H5O#<(S>JR>T#UB@s`+sHr@K9qoQ zwD&k@RUP6%y3v~m)bcMzhTrlmJ)th*DS@IH+Bp{glXLSxaS}&MIvM&in4S(YYBU&u<7n&4xqko$^eZ2|FWb{_6 zbV}!crT!m){54|N2Vgh-U&dVjFF5*t^tJTPJhfyrI{uTQaIy{L!X90=Q?|bUS03UR zS}_4JF<*2&;FOQMs-*+6GMUveTUaUvaid|#U4rQ5mv*K8>p6r}0<7q@t^716ZeHF| z)qlydJ~As+U;WpsNIH%OWKbXZ1K>w&|7OHPn@O0Zii%2mSsa~&jnN%6R{k2|y53`R z`1fZP=f=(au&^-3v42Va#IrO!UqC0C{kC#`n>tKF{X0N%@K;&2g(WV=UoaYf|1~$( z?4zBb6SmszUr1EsDOnj(|I5o8Wv#BV7yl(1h5PG&{R{p5pMPlQ6mlvmbM_h&h8T{K zCpGB75$JIJ*81k)yT8s+&b0aP!Re3#AtM~OiCWkZ@YZWp5w=2zuJ#C4r9m&7ta(13 z34cmfL@sB;7*QG>mTeF(YDe_pb6Kz+d^r5-vPN zA;CC|LM^oaeM-=C)wn)7|3Wxq@PCM??1Rh`ukJOrn8*k-?=n5khZ;yPnzI zxOnpXejRnAwnZ?nC0WfO9BeI7`8_hYKq=pgce>7=Bj)jgZP5Edqngbc=7l^#JS{DL z<=4<8eFg!?>;d!)8f-Sp217>cOA-{Ef!-=cmJXREf>Cl ztRf7GeB&PajRlb1&*jGgxYGd=mZ}L9T4Mn~o9;d!o~z)&tjI6|1}7q12q2bOjNAUf z=#N>;G%EprzFSp>ycYs|=&67vQ(aEu<#u?LPEEzHHnMl*Rf*QnTYO?nmI?(Lc@KC| zP%!4pgLhQ_gTxadzau``W6EpZR_-S&Y|C!^ZE{D$!rbu)YVxnA!a&rYQ5Od*Au3y0e3b z1$HxF<1<#EA+1BA`;X%9D)Zt}ybt*KZZ3)VU&oSS9RFW%Z)cPK7n@jEL=>)7|L7X+ zCxbsn%4TsP*MH-h?BM)7{kHxtdM)igfdN}?Qs2$(C`HhA$@lfYwBb(#-YTc>{=*D9 zCPVm4Y9G-E^WJtT{6#Z5@%7xGy>w@?;(~z^Z4?zDf>KvCeKU7!4Xc!rGT3NRhGD=p z873UZ+5GwOAt@>8Q0D1Bu8U<$m_LYKHk#aejf3_ ztw$KWfI~LoteE~K{`lj^kKNtf+W$jFo5k2+p7t4dsayg2_qw|v9=w{`HOnZRm;iyVJq zCK)7*;lDnxTXd`d`LsU&{iV@-`9I5uk!G6vB@No*=5}g3`x$Nxb)E#xS=#F zCahmks0tAM&qB{qX8#f1N0`1Vz~2-65&&jn|Au)%4J3a{!>^W(iRs;6LR;tkTmSzf zcl(V3HvhP6a6#~Y=`sY6Rg|t?eI(Az)z?HWSZc6_;F1h~7yAWIp+x_`n8Ckt(f=Lm z`PXPN8qOp3a$pk&gDn{e1yOqmO}mYM1C0OwfKK2SNPC$Rxlpz>oT+hUV0*Wlf2}7XzWJSU zT64YYONELVzmmylVu9I}m5e{np&;v>8kt14&p*r zHCe$QZ}0c~8NKY|kt~=mSZc5ZBiM`~a0nE7zTE_m>gwV`b5E7c8u-VL-BWUdPkm4e zV$hQ4VbRvs4nZYeZ1ZenD_>k(e3u3Di>vz;YDIkC&8ynV^`Aj$!C2ba`Mf;cFfuY? zd1HyHg82)|KK98dz#t--f~F)F)VaZv@U;|#dZVjC~fbbE_b@E0ui z3mQ+yV5+7JfUW+O^n$nWQmT~_0P3%Z=+@GzLwH9GgKUiQN0axW*#clu;s3s@4uk2z zpK*)_{;HahHCX~5^lw6n0Qz_TGox;VB=?C#qa#`3jyDnd;EdJ&FF<}Ng%5%X`9}l? z2(!-V5?Te7C?0SXt$GJ1?Vo_6W&aE#S=G7mk7Td=Gnw)934G6Gb-ll$=!B;iS|LiS+94dskH~cq5_z!~NzsrNf zgtsPm#$lhx49YoaK^Xt#%woc>y5r+MRq_bc@-X-IdO1Haz`N56rSL-l|FUK2VC9@D z1-j2YRLdVQS0O+0{t>naVQXz5LrKPMq&a~BN&9MLMZx>kAyEUQw^4?NKH}zmAhwRa zfGb^FP%bpYFJK8)S#mr2%93Af0fXZ>vb4Xs*8K! zw_143T6}F%w&QLL2J%w5Z!^xO>r?Y!*#1hp^SczQ9{aO7DKMC7Z|74I4DII&-djL} z0@nW$dNBe805aYdO!;U}UxqcP1r?lfrR_p1a0DRW2K~Ksq{2vG6Z2vfvl$F>*I&jW zEul0A)Yd{hSM4NZGR%`iMApl&%abo)Aw=uGcd7czR-}W=fdqv=z8n6my)6O8By(ca z+(u;?2zj-wqKNCaV9>V>3DVPdKh2F@FpE|Fovr%)FF}ZSb(IzLMzEWgWwZj*9p=cg zN*RL!4zGS=pI?;b|Kz2g(4Uo2c!~em02vQPld2vpa(W=bUm?K(+|HQwi<$x7G!TAT ziFW^7l$G<{oNGsH#Ql{o6`N8eZ3Bw0<9K7?*#|hn=);>i6zIje19B1kza-mqMoOw9 zynGvyDYz7N=@wp(ly-w}a9x+3Xsz_tZp-tBHO1hm68A%@J}^B3C`$GBZPhb)Pq2AM zfDXR+amunYl~5g0XYtFgiaDEkmX0_Ns^@ibKZ z0_KH$5gWQ*;9Zfd?MfX5zrnD)V<}eZ{j@{<s{K{8kQ_zBHR%P>TLthxor@NB_H=7X-wAALB+2 zaQ-DG1sQh@Q5gw4QF}AZlR_z_f9f^=6^s0TD<>HS$C+McY8ZHtpggk z*#QDV^Z&4*@&A(C@1Zg>GWw%Q*IHapcSh2Mowz@=U1{dFpp*Yt3}bv;Fwd&5tE0L{ zf!}-hBanS?7QE2xYz^iNO-xL*w6wrweJCS_&=aws&(@3;AGNg?D)d`|%&40MAiVz| z05TqJ@b?mEFp2(9BoGjj4&cEWaD)#L0Q)z} zbim345=e!npx(^B&z%ZQwSrlTZi|&Oe~j z0jrOY>EeA#v0@N(VoDBzuSqG8|;izfbg+ZeTd^DuZ&*hA-MAniJ%HOMUr*}0Lp1#f&<>O%5T zuD?|^BKa~E3-Tk)U!w>;T~5{1)V#aNg}+2$wY0HWuD908^@RLqV;#4L65Bj)NQ8Y_ zYil_syb+NXU`tC&!AXYPeB!*clOe?Yn?GZDG`FyldVOn8VU~bxIC!h3wYL=-D8v>^`;Hy@_ z2}CBChg;nwqTsR9)B52u)36utb^9JA+U%J3qv~6;4CdcuJ&#`sC5Dh>tWqMQ-{F(j zb`e0=~{a{uYyfB&o3KE0IqzjTy@bK4%wID z+HUsQyiY9ELLRc>8sSgV$PL#rp9>}f^Jn1dUeK>L05Js38)w&>E26n(i3+W0?w6P2 z3|Kv)-L+0_(-qCr#XC(`&$+wVn5>-T-0sbtPet~SRp>0SzP$G5!Ynp^0Q;Tiw&BMeLo%kCq9v`2O@2j_TVM)=_(DwHYZpLFN91C_< z--k%j9m*>l%)_~w49fNyC|CWO0W@5MDGYX5V+J(~2Br*FZA&FW&%`R^P@?GTOlguV zG9`J4N2{y8=n>6W}g@rvuC0M{FVlE-y0~T*`-m; zWQ4Q4&FM9?xc+{BvHgXRq`Y**e0vJZ*BB{bmishOk$LIOep>a;C{b~=Ro!{{)uE&9 zL- z^Bu*$gnz(QLv0XH#$plLDA!&}WIMBpaHe`(Ap`KX$z-wGD!tkMw)&cci4HC7vpy3! zCHS>o6`ZUDT;i-Q1fW9IGqT37u^KTB_U+L7h|q^E3bTQ2fowTMI)>Qqz0y2Iw(3m4 zQi`a*TK`0+23}lH?_@DRZB8%JlBu$Sk)?8!nNDUz58MhHH?<_dZ{OV=0Wk}6*rAMr z00ZStl#_WP32cR|j&QMR;FH-487V9-MnszRjo3!OO)R=~8mjLqIDvd!T%Yb>IxftV z=7S>%gM2G=A`x(J7=%{>^~*RunD{voll+<74#MpC1$oXoT*@ZkmxU#<0{rTZpw>0E z_qf@vkoCJN@bQ?AD9+|IR_}t(NY|%?5<-c8)?@1 zZUf;JeqfSj1Mh3QXQ%ob+n3g0c6-^uKqkKRd8@{ zmY<97zvGrIgcm8pTKn+9)L=5v07~0UVq76VR#0}kK^yzzap+dH3wXLd!}CbxC4o*o^Mr=T7wFuxdYwxzfO9vWRhWGy1QPT46Cp(1#i#!G_L-6ph!rvB< zB#w*IQVriDfoIX;I2x>0ki00mBv?3maHf~Lv`RS*a0;OM~YbqTQTFBJz{;HRE9i^Z^6+|u@C za?8kzI2u)Am?eHd33G1$YmB~iq@aI5(PG;~zb4b?MaGlX@kemE|HZs_T`u+-M( zCl-4D(-`Y#px~&JCKQ>k-|hAug)snfWM*P5o!PZVvc18h&7SGNmNv9+rvh=b-f!bT zf@E2VAq>;D0#1TjQGa@i&2jmiQUof|!&?Vj85SYIK>4>uDl55`mgBO~{W3=O@LXsM z^zL};yvak8T3wL`1Ra@i%>Z1i#hIlp1_Vu0TX9Qj@Ox+sv>ho7fZ|^7_0ssy2QX`e ztIwif!}b?>mSa+?qEKb$aQ&9YFUp9xC<_i?81lQ7wPIv18l(D6ji*0DZ3SFxSDQ@; zY!)Q9O*&_f{W5f2kCr8k<{F}1ZC`G&kOYKRAm^_L);E=eUT))AU3`#k9p=paDuwPI ztvd@1y)O2X*SxN`PsZ98FZk9E+YG&G(SC08JajCBCnV?>xOOD$@c5n8*T7W!iU;MF zTSRua?-wq(%jiPu$jbeQ`|;ef^8;RfdHezvjkt~NmD>TROmV;qvYio2qyC2k2XWh0wlp~%YkCz)=3 zpv-jn<8Fe*0-TNqnSNkA2049Al7I5oGPD}2la|V}2?_0gW&%;_pD2nW*-($4#_gZh zAgbd6T7-B&Ck|28@Bb)Rh~WJ$ACh8doNIA_xo=)@w1;wZngjG*U~rjsp^Il`NO**w zteb{zn1y$Vg{q&NbBz7%8hvgjUk$oHlS?Rzb0ocEIJ0YX=}+mRbNQlc*|Ia)k}JiE zD;dd$e4v-wA=wA6^%tys_H#C)S5nZz&@OP3HlHLjO=9}kEP-y=ES(U+Q9f_8iN`}e91`V>3rzAW$e{thrOMuXoa*3o*?<5(`7mW(Revw(utXma zK;Yd7oD$-_3#5VIgvsWEh=j5r6oH0JLmUVA(Li&)&c1_)M6i$*34}_+8UN--1INjk z-2@qlYT+RQ4Uft8-F&Y*}S^Ua@;za-?&JmM$z+|1&vg(rvZckJ()+ ze^>|R3vllFzT%!3g>|Y*@4bOoi8RUp8~#39~7_HhZ&i5zSO|CSZ(!fvYwgI9{sfOX8x_i&$$Y70YRd ziRkrY`8@aQJw`XXow`7{d2g;?)wk_xp^_LbHu)OuJ~w3?n!8kFEjD`@2hN)wwoc97 znOGMRb4M+e6E|oJ*wi;H8Ea^xMFsft8Ax!Hcpw1|>MEcG5O7Q5C;PCs%=ge_p3WZP8k8$AE(m_ zXsQgW4l^T)nH+;16O)z-Dh;aCt`eJ+8w$gqgvARD4GE*XHOX6n#&YyhvwDmVGZr6F z{%i=1HL3X&0ilgq0%WFBBHz%0LN`Oevu(RJ}(DZAmsWMIkj+p3%m-{q`#5Cz#<* z`d9NatDX#Y>`m@C&LDbN1N$bH0jB=X@-0Jw1QoU>mr7e0gNAx?iLEZgYo7>RdFPom zPJ}ID7$n>v3_JVJ?|KUvEYeh`Wk(R)5^vS?*(EY)&vl?wt!$ubZtn6=&ZG&oTlAW{ z>+68~^YETQUo&^6ufoQ{hv}UouS4f9KF@wc)u=vGtNR(pbK_;TU2a91 ze@yswm!R|%2c8YWt)IZ7KmC@*?~g?N)C-&HM0FOQ0CF{$0bfl?UBacVhoD8P3!8UF zh1#o|s_8ejCtM5SI&2VL#MeW!^D`^umsG>aS@}w zx=xD2Xr`yG&GRAf8U6HuDNKmC!hMuE)dwF4Va`#ZV6A69+h(M772J^(3cq;1XV!KQ*3yqD0h#5`DaOi8wdMItx2b*%a_ap%BzG}Ns zTkfUt88nah0h@yJ_2R0=Z^BTNMjFqfc`n~sHN7h#r@$_Iq^d8we4n4;cIv+TJiNK6 zjW;Xjp#(!UqhR{IHGx=6BZKmVT5N<*b%h%9w z81mGWW1JNssf+tc@IcNbb86U|GHli^zAe8^GZ=dn5mbFR)+hlxogs)>Von-HF2k3& z`ck$xk%?u!Jh#rayf0hNSdg+(_d%AHrs1th8?R0S0=*hObv!9~BvSGaa{PD1!~rCX zu~(Q%XxMURo^k8c*qhJ1i^uGfiNZ)Ayp8WfClr|I7}tb~8V5cL#w?+jb_5XibFbLv zoK(OiwRa^|jwOC-aegKgS>hCx`fnR;Unko#g=l;iFWn2#l-3L{df17*8KK}~j?Ml2 zesdhEF^F(1e=&I>UYvH+&$Z0j?_5_H;!4WbaWAc4pb7hm)7$2KS!sSLefh$Lmf%Y* zoTxdRB#l-A>Hx2e&DoHF~e(L2A6jmXQJ8_mY|aG;>zgatW56de#K$Uw-OBn zu)|#fR4gs|?IrFp7*%eVl?gjmTx{qAEw8Z4G|1~o_Y|$SIhsNjcE)fC#^7qeTjT8!xK0RO`o3?M%P*#Sx*L= zekyd8s}-n<1$eMv;zGu0imJ{|0bpfxRTu&e$PlGrF$3zz?jEa&aJO)flIlpn=MiL` zAP(q+&P71TuhQX9It7uki5)L>r%TxuH&tuVaa;7RGdn%3vk6RS%zV1DkXeoQvoO zy!?n@Mui;+|Bf?)BE7HcFcjk|=rg~V8!>rJrk_nl>+zQYJpIuhNz_|ka!G!w3OY(D zy+EwSU+-5J^TV5cD#;Zi2Ke(gK#}d++k7zMvA4jU*D%qEzo~@tH%IAyW(UE zzPnb?BPyoxKC#adCm2iihtw7<>YxK@Ob!BW4QgOn7I_+lF7{Md9zxPgNRk1lIb{!$G;5gI@sgct18(d`BJ5WGr80Hlk+6z&=l+epJh4qKl`}4ExO` zYmAjjM_N-&S!c64VbNP?A68|ivDqy=FcWCG0Oui;5Ir!tV8!;hL7$iwIwTj%+5ShL;q>H&iM_12e7h%{g{ct=i3*8#kLR zN(i=}Kjs`aXFjg0HDd6HSn$Dtx1DbuhSDUaodFa#?l9lV?dz*Qj;Jjwh|S^s&Rfl( z#5`EYJ#>eQo?%8tsT4CylZH-{iY}?2F`;)bxo;@BXCTKhqM4nhzOe(^CDGGGSfTu0 z2l<@xy`wmFtr%6&6TtF0m)=Jy&ifr^TkvKZ%;sB|n2JZp>S9Lk5=?|r5h|^B$#ovP z+>80S;&LqkVT)g;CX!T|8U|js?CFK<>GcO0kA3kFR#&5fBmG2zTHYHxyWV9L;_tR1 zmM=Mnw(1}oSx)-CR?aI`kk4la@8@XUwW{QU&1tuqf;IQ?3n~(!&uDZIHOe#^6v#^2 z_WDs{?S)5t9q?>UF9=W+-rwOXh{PBq?j_#NNlYLq6R**OrxOIE4Dici!}&!)6=+D! zlr^V#R1KU%BS&YaTvEtun+Q{ifC5A`k?8=X{i*@5FnbHz9d`j-yAZEL6 zEwyc|gzJOILCr&5hXm*#*5V7{+ict!YuO ztx(Zsh)GKXIOGWK)_BvO<}!0z;2B7XMZ-x-;{{f;y>dmj-Qp+NiDk$UvD~LpT8s(w7rBzmXTX=&Nrfbv;R0g>@}nlQB!9a)YF4XBjb=oYU8%T}fX;H7Uq`I%vqc#Ogx9-F`PcT1_7H(B8 zWIlTxIy=2U_-S2n!CrIo-*EGf<`jY|kWH3VzOEXa)wPM)69!)+-Ce(F>XI(b;l{iE zUQ8ORmhQcKX&w7nKPgBtyukPvfhQCkUiyu;KW~+!)Sz}enl*m;w4SS};d#o2ystBb z>QnB15KhIpc+wtwma<)P{3K2+ zKHYeMctuj?tqK%cYbxq&FPd+v8!xCDZ$L23jD9l=uLj1aY_4cwGK@V5+?O0(WZ(qr zeWD-O0kj4IpN20L)E%2^aNFJ?AVIUMC00+BLQ3Q@8LL;Zta)$!AU>B?4;2UaCn|Zc zZ%r#CeS5b@lI9SgKg`Z6Dm{0W6E{Q5ccFW($%A8C$+*aV!Dyx@py}l5Ti-dsf48Zy zNUI~S*r1(ZQ!6xKdd(dExz)!HhT^HfeOV&1vlR3C<{Qds?cN*1asHR`zSB7-RzsXv zU?LB!rKm#{VpR_HOGz>wQ)y?!rm*4sUH5j|muAnVt8Mg0uE^X?oUZuPLVgFI`@pOh z&S;FrHvRNOXY@grHqOtE06Ve z72APa<*Z`WWJu+{e8#+~1S_?y*?c-pT1IthdUdLyft0>X&&6vnCex}uEG>O zsZ=M~tcy|^%$%uLYj$s!jnOy0w@f}L?So{?qnJ-$G1xT7lvOppvEzD5wRv19+GY?R8-`0*(lod;=XyyQLY_b znG;cW(lUq9s*M`Fu<>8s=C`8g1{jk_NNzd%epD8bZYn!%tH&UJU5;_{NJ%+AOlz zz2HN~rMhkur^LHlp(W4s^KM5cVxuNDt|vA$e|$u?|7Mxrkp-Ucvk|0Dgqt)67BRL^o~bt=y^y3EcDx zyzkG=WZh3#jc=q8CIhyN3k-nbat731GN2mN@4RPAum!9yw@05+n%1r`IUha-9|0cE z&JIqs-JaQn0mJrr#hpsGcYGqoGYu+pO=NHFG zF4FX$(@N#xLGdX}Q?WY)pbPnIn262QbP z%}FgnwV=d1sD%)WjqL1IH~GMduep&FDpP#Y@QxQP%}c7`Aq}4a4)yj-=+$6!utjUS zg-3*iM*z^_i-psxN>2;rUBBA;Vg)h!8dNlx!>9{Jtg@_^);H|zp>}z%1cP%dXFCeG zh)!2ffoMdkX$SG?B_`t@Yb6AkDm?sos7QJ0-QeGbEcv3IB>X&`HjJ3?lO$$ine_=+ zx%I^m-zmvun-4n(ky1Q3kD;r?R;b?jELw4>lDYHp4U|Mxew}99SAUUJ(PyFn2@OW@ zfw+rKV;~dtsWddou6MPJCDr9oK=zNJZ0NP?Y$#42zUM~VvR$37oV-GwWk%)jM7wu^ zMfWU``tX`b>LsXFtnYlb-8Db*8GHIURp~IK|9%ZI)rZl>{*yRg>b5-2ORQuh7I8zN z$DFWyb6(wLO8atH23-eNssZL0082ZY!@o6oIun~t&FXOF~(;lk&i zzqsc^{z?g&TI)&Y(dU=LkdjJFJNu&1Yy~`e$2S$t23Oh{zD}g5shLVemYrtaJ`4s# z58bIungts5d0eHSFklcR1~$^&+F{n1^LO71)UL}47gVt*I7f^(e$yeR4OPJk`i$r` z4CTB$INYi^)n%9b(BOUl7RRu!!aPouuxNc8RFp-rA_fiOOQi%7ol8M5W z4Xt^b$$PrvxGGBDt`1-hkA!*pX=zGz;jMM(Dcx1!@AA_$y?4K>S%ZsM;rvl_G!=?h z`8D_SIFV{4*&nM+l=t&%mchT}lZQkRvK_f_{Ov|gc$tyUu&Rh%65o2%lKCT}%TpF@ z7h3A)k`bvJHtFNtvivTX) ziL$sRmtH@hO37G@(|{k4K&Pb&abQA9UFGIn|3m_In3-xDp(UxTe$3fEur|8BeRG0; zq|jHj^K!ss%Jb&VY94j^;y4`ZaYAA*ApGYV{rSt>ZoT=pM`eNYerOvc0|H6ds|JD@ zxHHqCoV%_}?J`ZoZ?gS7?DG5luy619ufDcaFcm-lH5fP}-qJ^Y9xc8R-9S%?GF2O$(}fm|{# zK-5e2s~bqmYj0IFTs$Tnbr^NN2Xd@)E0ub|2nd+N*l+hGjhx^D!+@_wmEE$L{WVf1 znzvD|zE%Fh3=-n3F1SIQm_v#i(*6Fk;7p5`>zUNHn^gX(v8GS{@pmTM$Cm698M|`t z*NcW#haWmBA0Ep4UG1t*JLu22!iR)VGraW{TTU2`J1DK0K9b~o+=ock^TEpfHo=Z( zo1~h9qd~Qu+-;fVpulmITh$*dyhZBcA!kGyg8SQh1F`;&=h#eUEWr;)=B?d!W;+ZK z1#48azO%88_jJifQ)tHW!Vc}ObMFJ)dXfr5mu4niEXQOCi|aaCNK=>beov3msIvx_36H!#Zf;}Go1 z^?5|2xW~Zh$vlnbd86hDHuTsriY4>qbi=IK@lKZTOy&wM=lff$gJy(@EnN}ZQ37xb z2vT&D-!pPmyj*?mCWUDu&VLukcMj=g&9w5V$o+Hn?OoO4vwd)eL{b31XsYN8V2BOH zIl+j~z@Tb$%?$#4B!eXcFRnsurnAMwecfVprs0wr3}bQi+x$1OJSvw}t+yENWEvbV z0#>Bxyi~)U%Ac%XI9ha6t8#mPeDR7sYpQqW`n9#lLJaUw+BmD6AWXGdcd82H0@mrr zDVT6V;PV*r9B1V5uDD@}SvVnf*@9l{2;!@!*uW`Y|@UJrcXJF*`v1 z)lG!QIXb6Ow5;!Pv8w?s)ly^K&2=dj(?_-#a4V5Y!B8RmMZSgR1#l+9Opt?ljPe@{mt@5}&_-;|)5nJj4i?k-EbUBH&G=1q=NVBl81zEhQK3hQk z;f|fw+vZZGxs}OBKkaR-jEE7lyFv`pqh~H9le8npmabAbRYLpXj4jrna@VuHObuK| znVBIFU^^QHYZxc++}&0!MWT)MrMRp0F6xyu)bBl|zEk=F6lY`rT$85`5+Kpc;xU}R zm!w2f+O|QxZmT+Jz?(Y7Jk_7mWlX=z(OfVK3}cp@(N!pIOrL{lNS(7`FJ3Itdd!hs ze2BxaCTV)J!fni7+39q|GSyd^(dD3Zsa&a`NrTaxxq!oN6@V{FQu=kpUZU?V3WE{< z^jp{LAX<*;Co5|ENI3rnyp^Y}1+^j0;N|Omg*ENWIC`3`6nAbK?HWm`B7?S4jJ6ni%_q9K! zSYq?IFAKe04DJUDLn1X zI|P;Q4}Cr8xtq=uAC1%R^3xDm;3@WyK)Xt#qQ-2$Xv#vu6dN=t-=y^4+Srd`G`k3xOr@)>96!T?50bT2VQ-dcLYNM0 zQ?+@ZTDTT0R2JQ-R0rnCnUpVfH)(SJH%O;d)5^&vx<6Tg%q5|z%azy ziuJ1~u6(O_WdaFO9j{qOSILd8OB>|U^zqNqQVx)@xPbLgy?WiLI! za$d%Swb&tVC(b<7qGunt1!B_>+7I z3FS@PnPq}Ng|xy2e+847WV25Grd5@sQ?{&Iwki-IZ|lQII~e!o{2cQ8(#$WuBhx!3 zS*Nt#D?}#AJR8U*a~V$%yB3YNGE?lV$iuliV?fb5au*~`$XarkZeMn=*|8nraSPMAn8wxpSkUps0xIf_j$e!(RxG96zIsZ4iY-NAp9{k}=s-3%Ugg zAOH{Fn>Q(^OMuM;rX@5!SdzuK#_+tW@{`k;<{p7h;9!CDL?wYX&zTZ_6k008^eWul`uT$7l=rfMRR^mVi{HGGbv3&a)ZT>()exG^?IR@R`F4$onOU<8 zRPYVAT({0ImLeV6Syz~AuI8aP=+oPU81%7}1{mrM^F3N`$3*t+ACCrqdx@`aka$aS z{ED*94V}n#z&TBgOR0e2gF>PgPpVJR*lBp}8~($lvbk#AvX(rQU&##Ld=G2a4kM?0 zptFdTPP?vqq>6qZNcyo7%Sc|s$ne8v#QWysF((z@Hm6z5kP3Y!9U79*3)n;7M}AuB zw~7b=(v60BS0(EkoZ^*T*!H*2JQv&7Al z*egY&A={D0El6SBeH%zoqNRG6S>5u9mI@G|P=g8I0lXNnF!fxjo_O8goPRkqZ%AWu49166wdtO{qcaMa>sCT}lgWP6i0MK^4M2?Ov8vv@DoIOuo6!yQRX)?;* z-h%r<*!KGZHEuHJ`Vu{(-%l$vz0=ssD>8B4^oMBkmkfT@I=4KocOmey=-V@A;MshV zbdRc89=#pAv}NLARbaz2xME`H11l7A(AK_G~62mqv~YRPaM79~a3JVrT!VflJMWE69~A zzN%Xgm*R#En{r~#vEpRby!jk!cBhzJk>Sy_yMUu-GGvaN^sVAyHwCk&1U#g3WHeAWnO9uGr2>_Wis`~8b6#( zc)Pu4C0PiGKJM<9$667_oZ`V;;=#OOJ$s^*38ZxI?j~1w(hMbe+HUp!x(uC|E0lz$ zd9O|_-84v?p=-cHQ*RE~UyoUr3Sz5Yu#!0WK+s@HjJfQO^?*t1{Zab3lN+B!~(?E3`2*fa8vnJOoX>WqBFB*KUYNYg}MJ%Au`Vm2hQaSH;W%n3mcu zYb&nZY28ah#w?V%@7wsF#q(lRqk!=h2({*l)vLcK5w$`y-uQqs!zNnnsV)@)f%M z!L^DFQo}=!V7t@lqS-=~N~ujCr&T!+ZmJulNa)6u-M(eu+r;b)W%#aDvV%Wc%JpSGjhzH1p&Q82Wn8PnR_Kn32=hIIDt?OjnnD}7z*@N*VO7p*?jR0MaQPduTQSe zsxyZjCEDOA0+gNm#q|qPw2yMxrLqHm+J; zbXu$+N6R=Sn&;}UWz{Eb>k(mNux?bgF!<`R{}kLJsCtTz+{HgESk3Hv9KQQ{)_=EkE~V z?coQSibDU~$D-uE0W5oqH>0jF$x%lXvseSfLH3h9TYI%(m)$?BHe34+r1!{*YkiOO&wy|Pxo7TO|ZVVcpI(n8p z?Et!cmD1FA(9kk46_1`&>JAY27)DD|P|%ka_Hjsfop-HY@a|Kt=8uq%KSJPxZ?1&z zXx<7*jIj1HcFc4buj3%$?qYq#_%R=$uYjxx+3X4u6~bsSW5M7~Z17~1gy_#Dv5Sw7 zrwqK!yce>ejdC$2q>QT9a?+%kHfzeMSyrjvzp7QzB|M^W=&+M8)d2 z6T;T}gY52D5oe1}#}ox$VqT}IwEfI;u!xdq8*FQPYD!-&L{4^%_armhVdrX~JG#)} zcuEk-)~ime&9vkwml8@8M2>E3x4@Bg2y+umcaN<@MoxcYfiv@e=D86^j{--h~$FcFI^U=pfx^6t34J_2ZfJCtsN_@Y_iNl5|Vb z%x~~c4KvKc%~Cnd(Xrgrb}kdYdEYRu-{524u@+~}|NMDB?GUah_jo5okc3#6Jj8DM zhXmWEtiGD;iW+9Q8rtxHFEtwJ)#-xeQk8b$IdoGO*U^N$6fcY8kSA@PVRw$fYi6k) z%}5k?qlear^Q9l@_iDP;&f@Pb$*QZY=Q%qR1oO1X>qUjW8gLUR)20W-%2mY6^$ipP z)h7ufkQVi<;a3jrT`ZW$tStb1c{v!p3u*(Cx;;BaLp75RXfVTA2%63xEL7xVU0li! zPI*B}a3rUM9(Axcw^(21$-m4Sec3bmGCv{eUkL}>F0CXt`|A7U=m%uF6Oa0UUgipJ zdHi%+t-E?biQZYl`Z5{k3M>6uvFhB&k=&fEhEwQuf(o{3gAi_3O*{G^r<5&X~nfZ+eEPKfo{Kc zOxES3$Je(tJ(Rjv3f> zom9E^zuHbltzB}yFI2r%aegRX-rQV}8r1IZn~aW&i$aeLkqSWl@%F~(xa0CoTb=%p z>(bPo0wyHsxj;L(c#uXR-4eZo|0}hlhGFF7qbZzc?U=0RxItY^qi0+vuJvuF z?{oTMzOp%8E}61ILfp}3e42|8=SKa#>hL32eYv`GHKQStv{1C}z31u)LR=`?S z)yv-WCaM!*5LJvIbfqLkOCeq_!@ZEPUc{1mvGC?C#c3lwZz!SUQ{+$`E9te zHR~L^t69{n5c>ey(ZEc}jfxZe?q0!Z3q#K(6iDaQZge@wz26WebuWSIge2g+26DJz@1D7R*jUCp2?30y8U)-nG&wu z-fhvhues%JZPmgS3ewvbIDJbs2Dz8c%ekpZwRcas&QTif%z1x|1ExU}9YPxNKbC7QY5IjS;GG#e#Z^0`XC`l3z9R^eYHHn3QvtoD+tN2 z@aRbVv&xbw)K38%72w#g3(md!#BlSU8fzZKZ`P|h;M4tg+6Zs;m{QxgTIYmRBl&*O z=uffO&z;&nP$_?pC3yRv3fxLgD}7`8YziV}2!lnWKIg$MgaER_rLCpgaE-z|HvHO7FiY677>ypHkBsP|)g@g>lcSiz#i1OHC_Oz1A*%l)$&E zTli3g{Dp#)7~jlti`Y^hREvLUk56F-h0bb`w$3ZKK~`bdMf}@HPdf2egD(HW|Lhxx1WioUlbGS zL1q72Wp)P`ohpkx&UwkS3v+`_K*i8OP{Dvc7S8^6mJ}?F=>f4WN!zX$_Wpp)2nf~| z3IA=%F`M->x0n}fvnku=Shdj={efrQ2kuoHt<(O|*EKP_mtp^7xzouayVKETziMi+ zTvSlrsTR6fI|Wn()j<-FYQ3<}B^lc#hrgn+48aOcijU8Z$SU<~kNIDv1(S!L9aXTv z5dw}AE!U*KrMy3R5VAoa_dQk2m$prZJljwBbsunV|ID)fL*DVW%6a%@x(=mfPY{+{5gU98*7X z!#*jxZiLk}!`L;-HZa}NC($#Z>P7d2Pg)y?A1iI>r{3pI2Z!y}^t+~JM`ulU1FE^b zvTIIxYgs$TF)<)mA;J-@`R&F`rmg|eDFx^yw@w8J*2UW^J`5}b0+|mKtk(_Yn%#6@ zDrfGXnpT`SSL*^XQ#1EYJ!eR>I=vXJ@|<8Topup!J`~h2brDfAp^vS5$lXWcOAD5c zeXvJZ>WRl@AIb)Aerc9TKY7K@^7b`_fuH6M95d=%2U5!tmdy5pm;`j31| zw;(b;wT<3sf&9ocd6RM4R@0m-J0*S9n+8dj^s|3dOZi7H{jyFfyK$nDONM<^ z*{U4sd7)AxeVN2AtNu1CzQ}T?q9*A$v|CAsKzNl?VH1l#sYDes-G z-)f-;FIvree8ORYg zhpY0HgFxPKpkS>U&+d+jeP&wIJi9Y}0TZIm)2}jX>8yLO7)ZM)%Wl#lF1l6}_#(c$L1QLR@jDH5p5Grl@Ml^gC!8XHO; z>`UYhuXZVHm5#2n&S?`0t92=8x$9fd(_lN?Y=Udh$JT2OVN{1|6$Y!N2dc#TD}?*Y z_`Iia%mkIfmA#&<|v6>OAF4QeoqZJn{pVT_9WVBmC4I;kUF}}(!w$dQ1 zP&>FlGdNE*Fh_aXz0^bU^`py-W6Dfo%j}XXol>ftQmYYw+-7EnWVM*``}PUcE~{nR zmG6vTk$_;8H1yHOVXKwh7Aw2WR<@h0Y&Kcid}Lwup{3Oa7M34aSZuJcc;DQ7Jq5FM zW~OV+%+{Eh0^Tz-dC$}sz_cjtLD6Z2=q0yKg)mC~`^Jh70}Fvb<^u(5m56aneJ22FU@?KJxbfOM-Y^eaLJ(k{*|4iQ6pKS>Q6`nU$fu<%MUBv=4b*GR{}#M1)i zTeXo}EHXD6W^c1D+2&HS$-ik+bl0Y|flYb1jYT6LmEZwj2E3#cf<^vKU^TEZr~)d2 z>Zb)PKq`>%g+7L52$oGiihE?DS9E+(60#=rO061GsoB0X)VHoAuN`TgD@m z+x4YxY@ZEPu*w?ymn6HqDKsw2BPiz80&;@PjO+k_Kq8;NvErkYK_HO%K*4&={5uHN zvtRH%f3Nzp(W1xsVPAIm*7 z^{TAxM-pz^yvsgzsoL&ZyD7M3b7JqN9PGvt{D)Pe8)``aFautKLdv4x7O)yv8C3a5 z(j8O>NnWV$N@KZ#1-|k2!Kp6c$*z&fKC!W>@mghp|5UjgF7W&(E>ztY8BMuF7*qBJ z3Kk9b8Tc1B$nBD2{!qs1wsB@@$i#xln> zY4!h{`S_(#sR#I`)qAF3ToP-1U)?)T6|8dV1D@$pjOofLZ^|gbyjnm`keQJkD5nb6 zb=Fn?P#}SD@a5;Tv`uKz=v+_y5fM97%-;YJPE!`pP zpn&BTov0n5b=wlUwx;)Q&L94$V)T6sd2Qna0L*}wpb#tqD}dFu=psQCP!UwuVfenQ z*)02E&$dD7j$vue;pyObSl}M4H(Rg(q9HYZxs{)DE&Icz^sIBqZ%!ow0TuMCJn||& zVU~5sB$EcaN71VPA*%wflqL|Yil%|L0s?6q3Kj&iDnP-4Fv1;fF?*kAMI%@G_#@L< z`DI>Au&7viAiX2frOf@0JT~7T>H48d_NLV4k1DY5H;=99oCJUw@Ddb)MH{5t4hxur zl|dB-M`T!#WeXPg_Jd;p90_l!U_o969tjN~Se4BKZv{jsSP;l60R;=f2zMWeBLd=; zOg-rn7?b(nvFvgLOJjNrQlMF`!O1p$3Bm^6mu2mb2$=4Zw0ob|6aZ$92%3RHun4RG zRs$TenZ@(uz83&IHZcqH9J zlT|Hz=o1v1y?nuICcSM>`WV&xuh95FBASNp?HXGX5E*@bRC6uiME zaQ(sK>#Y3O?(j%vpR*Qg&6K~sOC6iGoDp9h$V}!qCea-eYn4OR9_2)EVrqs6&WaDBp`)r3h zmP&TY%S+04LU|s&| zcc}t*?IUrHID~3ta&jyRPuCBYz2C}mMM5kK5je?p{JpOnwzEmd9w~u!l|B%raklx5 z%T>2BZTc(N`pH{!7)2?|)`F5^YKuSJ%i+#iTy_$pxRTz{)_&D!0oin%Nzwq^)FRTF z?nbykWMAXTYwTJ#yYpc^3Bkb=V*c(W{U!4m^CR9_R3rqe+qdAWfqu@cdUs_t(BgJS zUS3Du@%ZPRlKG-!G;vs%qHn)MJ&EPFrZ#bb7I70H$X5f?D%QrwE4l_^i^{SVb*;@& z>0CCPD@IJU)_&UA0}ak}=s)*@S%1e!wx&8(WND$FGS6!!<$n{fSjV|8;+phvnQIG{ zwhTUzJsZkj6p0JuU`(cfU$)yttJUIu@>%Il8TK35b&3&lMsr_W{dbDJ2;vvv!)eJEbW)jv!R!(tzT|jCkur-*~bm=6b%6j{4;>{CS<~& z&255Q*Qd%kH+RbyIrdGjQQpnoZ{TeqVDW6cY`3A+pNBmd z6*N34Liv<02;Nb%Ic&sH=IT5=mq<2WF57I_BFm>aTW>Q|wr?cjDp`9s+DGQ=@3lWy z?KUt!S+ zmi@9frfd{3B4uH072x=5^n)z0C`S%FBkx)b1@P^r<;vuwrOiwwi}*Lzk7gi)wVx-g z)TpD*pLJ8G5Pkk)8k1;_=Mm|DldfqnEvu!Z<>Wted_@&>xee+6^?Uo^494fR(%#C4 z*>bA=vq_bl z@{Pts`TUDwrDv2SM1#smc&5e*5w|Gr6;yqem_?5?z8!P_ny;V{iOO!4*UT7&%HTW| z2bJRM!9e8j=J~9|MBap~#ER{&Ai`#Sn^Kk#D{`)Z4RyANC*OaFlqK#|yNkbQTPo~d zGk`D#7YTLR(Z@S`xDCYFSyd|#xNentgU&w;G))+{-A)d!$mC1t z&+}0)?~bM<(ooSb9I47^@6vd62K(`pE=^31)1@q`4Hy?p6?a#2nZ_27G61vamzoq) z6Zw%G75>9BOKBJDGmxMQWFqWzrypQr;t0E*p6R{qU1SO~pDJ@D@3y@~N>{$AJAdQQ zx``4pjC(N%DskFz+#k8{f!piw<>uOXe-QbMn2ZBr`DT6FkimqLq{i!pdRVIq}m-D<2tdo@)@Sk6umds zN#XyriV49=GER@t+qU*a{((KO$uV!QQ~U%!)nlY)U1=D}^q0x;>5G-`VI34MlY4Gz$Nl5mSax;c-oskQ1aYW#B~7*G~oVhb8A&JD;r%I;d??6F*;*E z*X|mm;1MxaE(qtB_tzyS3**k%oolpMOV@R(2Q8udmNdMOvnJZw746JeP9Eru{HETl zdO}WMG94F!I&TMF?MxTo&cRUr*hW^4Q$Bwpl6vd`RETt%{$HE^N&ePD!Il?DY|vwJC#Gt zLc6k|D&X4Fe4=V8;PsLv2&|@-1#I%1nAY~iR!7Lshj{AFFlA6blmvQJ4Ps1t@DYlg}ktRrw&^YGNN4=s$UsPm%>B2Wu1` zt6$8_wmIe9eBxy2_tuNAkepuA-PNpM0Z|q0vYjW?>jc&U1 zc3e^on}S)s0=c-~A7Ch(z8Q~%NoE_5e{Zk2^tX5z&OlK(yMLu>1|s{#z`1#A`J-`# zqvg-vXp+22Cz1)-&!izVC5s!N%h=`j<3+>Ydr~=%dW1)QSA1bw0-_Gi6IPm%gqOpv=9s%%T?Oq;X`+hdRfoxlnahuq+bubKPNs99p zF*guykQ7^|&}}w}TDK?4JgxcBP`K8AJeJnk4bnC6HE#M;=D}{;?!083)wo$El2ThN zzz(;0=roBqIojIQvEtNVz*q1?PsgLPs`U1;;?OyiWm*3O@w>1L*SNc%g1R+h`r}Y0 z+8P6Xay4=3(};H7>4uS8lM^AWT4P^obe0hJuI5$gG|e>D4qng=mTu`3$PU`^2zn7XW9Dky=Js((Nz-`;@C<7=#`~c4!me$TJIQZ{ zrtX#_THQcP--PXRRL58P8c6w(PT)%Y5JK3-72iZ_OHom=NyD5r0~{xm6srDSVl>zs=tZTkZ5``D3w)sY}|KaZ{dQVdDssn^e(Vy$o3(Z~^{g{!03x$e`b(H75 zaU3_xUr6SUzsjc5ZC4G_;iAt$)vlmjc{VCv%rd`Unb-6qeCNqglQIQ1K&-8;qgJ>3 zRF`E7HF=3l z_&T{)wnZg@mtI3KxNhlATq7rBebFNqWMsE^+4DJRN{;^9Mw|Z2)}5`^#8u6V5FwU6 zW}wH$u=tv!oe}CNpN-%cKYceNWU60#UCiyYD=N`jlT$B$ZzcU3R550)F+3knt-N*iUjzG{a)!DefGjId;|oRiq=V<-Ty zT-mJffVDkRA1c^k`8AVk?p}Fd*wqRKo`W!iNgP9%vd_}yJnkwCJZ=$#26w%KX?LDI zF+rJMFU>J3Re5zxVuAuuTC!+u=krS zLbfZ|heI!X*4msoAyEd`!Ex?8ezMg+?dD+|GPn#k+)A-SpuZH4IqqHMJ;jq=eZc@M z`|l$qM|+q5GCrmP{82s4Gcz0AGLarx#HnnEv&?3&A$Csy+XOZWd_Z1vi z^jA?v)$xgjSmTRB932)bg!lnp#JM+r$ z+j>w&9#!g%>t*=Fw=kS*iV{wccZuBpBc=6l9MjZV%Ep~Ve^sx%E9FJ!x3;wux;eLk zh5AZHAbk-}+SyO&bF(zZjKObxX|J^T+xZPlQn@ILAm=CReG^)f0S0}i-@NM}{lRyI z+h)SwG{!j7@+OOAeRpb|NXA}|Nnl0ab53wlA`42ojE<%)sH^LmB<+G}qqJ{OLuDjr z&o!f$SIoV7)@n|c1WE*J@OZs$nw`D#puwgOhGfLHt8f>ytw2lm*}hQd#E59FgvXeOKtugE#4XdXLP8egQ&y< zlC+oE>dpQg1^zu6t#%!5Pbj4?~Em`}>nGZ3Rg>Q-d$8OI+0UAe?87nDh$XSbd!wf;rwirK`ON8Z*k z?N-<4)?Z`rLM9i|_yXVe_#F9cHXeoa+C(mh30ob`9(iBnJf+k~vSIO#8#v^R(HGGX zp|6I&hay)Tz2I;v`&_{ zBDeLvySYCDFv?ScZEnB86ML7FErxx*#*D-coEyvcfadcP+#2Es8o>+V-mO`ISLt~N z5BAeM#uN3ays0YazTN5_@8lhzGm(Yu0$clTeOA4TD=Iy-YdujV-+p*H669@+#7=lY zdM2M>qM#Ls?R29e6QKdvIjs${85)qG2p^FkjhJ5mjC)Ivkuo^v4NK#d@@&$yQ&QIM zk*B4aPJaQ+ai{C^+sXi`Kk*#E?S}T79oka-W`vid#75m8ky|Onp)6RKKGN7=cCr4- z-?e~9nLgt>ZWY)U2-gxBX_RF84aC*E&e%NuN|$v}TX!4i?-ENWE(P|v`^Y83MB$+( z$V&ne&eSb+C6QLVi~LYLkWvw8uVoWJZCMIX6=OA4CWC&xZQ3`KC{rfVyHu8|9B1Xo z4+W?!m%643UZbffiv}|!?2j9R`YV~&YF6F$8b{(z);*rDNwLxO#G8!Q9&0w2A zZ^UqfDZNLpkt-{tU2Ln2Pq|kYksY!8OwvAV!_<|v9W_l`3Z^wN(rX7_A=X@IM9>~< z4?RDhpA{YqQ{S(=v0tgfwz%-|t0yr|;q9l@jgN@Hem!nxL0Z4-S9p=D`v$Jr&96IW zs=?NyylecNm z(_WvwmuG+fd)YXpK9J;aMada^8m00*ntjVp&uErvU!>l^=m0Rp6iUp}tm&X-WBjyg z$|_$Z{%{CHG^^Vj1byw)vRqCM@4xGz7v7~|m1M%&9XNqJNzY{9+RW*p5xB+J#<<6> z*8!dX(f&fbYTVV||CCm`@*(-ncG*mAT&Y4&3{0B@x+q=wxUDY0$$KToG>FA_qx^F{ zcv+3!|4JOTR6G)^UrJjsS+VDrX@lJ9{~L*R7zjiF(>VF5(26u&=Y&Fa_TIH6J{Rn` z%W-#0^r*<`@j;%6D|`oZzWGf$$sBa0rrSg@gnmLxQ4bP^mC(z*z4AMQI*ti-Pm-#L zC*_~e4@^h{%cj>YnUYMdBBHoA6wT2;21j(b!SI7~;$0yVUOox;;x! zVxtS}fl2SeAsGLui9;VB-gJ$3YvUG1{io^tw>do;=g?$4GQj3Q=!mUqE2uRYuYoho zr#CdJZ*3W7wO1OVhaWwE@hi1``JyKk?g|Usq^WqKH@)X~iW&>io$&iONE4z@Ma>L7 zyb$SKXRn;VGgOgHyf1XJOjd6)a{YK?X!SRe>IwK%zeY9H)eqG? zam5zt^WxOcuV?rU@$x4U(k)FHDWS|ae?DzAzwNy%#7mR|bJ>`Hneym!k7`6Voc}0f z8J@9oZ22#;G7Rt=p{OPz0V|4&>UPIOOtp*b+EDWDm*?UFMGM7~0-mE?eSL{V z3k>`g$NQGvWRr^*US)OvsnkfjKHlz+9Bb^rwlz+MbcpP}2tHj99?zVUK%O(WyWM$* z_KAo6L5{A_CsU;wY(qrxB66nvPs>@n;aVb11C7HooLVt}W9jrZH=Y|9?S94WUt8M_ z5R5054j7~5ByDkg&)P&!a%@e)1=90RZ@S{}v>v7*c#^mflzpu=txq)sv%9PnU&_k9 zJ~(}cw0)Y+#lNQh^;McTOx)_QMc^Qvf33W4)oo)j(QZMK#vtXX^NW7s93D&Z}@Pww@9%F60nPJx65P~UeAu2K>B+3NNUTn z2qeuHu?rDpGkOWi>?!(YLibGsWd!B_LIUJwU)2q_bgvljsZ6N4Y2gBTfxbPmOZxO~ z3rD2b3}4TD^!53u-vzY(O!KCFnT{cJYwX2eO9U3>URSk)>SvRL|4~7=CGKEp zH5jF@4vK4#X>T^O9XzA}po2}wNSpWN!iYwOH}cf?OfBaO`zTcm8TfU3)$J0TI00x! zfAsbrCR$4Kx@)Ov7xdf#Cypacn5Or9ugmz(@FyIXug;BJ1Es)&YghANrcfIrWQN3M z^uz{dk~K&Cna!}ibE%+EnDu3{yi46w3sMyFHHYqX2h&&NZn%m$zNxBn778b9Za%eh=vTIE}deoodd z=r*3hs(!WUy%}V)ab-p!G#GftsZ1IDrd`7SD8-J4GTzeLOv{U#`eix;HB&k>b^S4o zL3^`+Fr4u(zR^^t^f%4)fA7ZC^7Y+NxoM``q0iR~jdfgagR?9mz;y#Q5;E9#*}nh{ zO1pyVZ*t%4Toc`S!hjbm?L&84pRnn{YSt_KUn_VG(Vpu^ofRWy$3HXWhz3ose`XTF zq=L3bFAU6b&X#6uc>~NhI&f^ceawq~>rx+N^VF9=GAxMyqv&4@^-f6B*Px z|B_}Q;=11Yrz$0^|KQdWHd|-^6X%X!<37-#6aLS*N8Hffgido^$APNW0%esDN125*~sBz9Lloud4JCE)`sKVXnn9% z-%hTIUXayMNiMSD)^>jC2q)sK8|Ivgjmkx{ge0ne-wb}G1GsblbkOIjUEQK$pyyZHKqB_PXYPe(tiEmQa#uzE4jb*ukp!AyglitQ78IQ|Q_ znLlAVBnNCFE@11SjR-xLi=`57qKf&;Upt`q@{9ATI)ogo-R0_R#yKcpKaL)XKy@{n zXp}EsbcHWsK5jY*{l|s()XC!KT7qgVM&hgGx;k8z^8w0fs2%BgL!Xe@_s+20h+S_^ z-7>1F1L7nAEHI)vzm6VIFTZCfXujK7`oASi%ym!YnBOM^`fmQv2y6w9BxZQuy!1aK z5DL>dUqF`4l?|fq`}EcdtDq^U6x>hkk!q(2Fgyh8|+E%DekH7>D)^* zLPjI^H=#&f^m+Y<3*nKk8-+HF9EhDyWZ?u>-~6oH$BEb%J)NM9$X@aVTA!CQjqLS< z^&}1k!CsqVIs5%LbLIH}1bA<=CH}^Jx`@+h^M+?}!q% z#wrR!NuaTaGy37uL(|r|Nzb;bHABn7!rgSp#gjNphvG(J_3<1bF(=+DAm_Aeg@eQd zW;B_rI$2c@w!i+5B)D*87ni%ED3&QHR2h-I=F;wbw z;w6yZz4U4obr4&(384~++ylK?1~bVIDeJ(SKU@?+P>#}&svC~KqYC3De_Yrp-)lbH zJ81rvbm4c~*iydU5%%l_=O^sadX!@x1U^?)OP>p*-Hm?_{d4vb!%yw1@b#7w+RuRr zR5@2oKlrSSRL-S0Xm^aTUCV{XV3kHg(K8#|2>vJB`q>w_+6uF6SWCkD3!$^WS5eVH z#`>vm0A>;{T+XDvUTK#fRdUDUDOm8hfI{5*IB$1!dksNef9>xw8+Z=1u<=~O*moo+ z{tYU2-$?}7Q-$&MMk>~T&zx2)iRwxy$G)#15j5@-(%u}=5ytwSCugmaW!Q?|yBh}z3nYl`~fWmZw)Yz9X=AZ$A zWc4R-{ukey*f;|~e7MQYJU;XyEfA!=VpeL+k&m|d+fgqY>n?>#`Pk>;j)R=}^6leJ zRBgo2{vJ*q*`$&-_yCCusg(rxKfUv{;JdNJ?#*G7oh{Mjr9hsI$RRgO_(h3rFi6`J z^!epKePVs#GV?A!vVRfWN7|;)Q~hZ<;zdNq_3D`AuIkg^Da!zOrTsIn6XfE8;qHp4 z;D}-TgtC=*UERCQ0r%8J^*|ekk|U_0dhqO0byJr49NOX)k8Wmq_Q_Z->-ts5WxrZ~ zzd4-RF{0}2LO5=i$&K3|&Y*x>jW&-l0(gmi}D9O4Zsb0gX+qL=T zWQ@5$pd~tadp#2w#Qfg~md*OEbRY%5CB?NF3uqT^3LB#h{(RH@{fv=SKg}3VB3sO#UAu06IjMZRw1$JtfgfLmvBBCV&Om?N!;_u`3ij_V zu-*pNSKGzU>ofN7T;6UKU25gOnf^>DF5YcX?`vc5hy6~j z_5@63&p@x31XV<*sb63kYI#u;BZB8IUIMWnI-Op5YhVqWqC|FonE>I(`@9@*{4t5U zfib*f&wg-&S?J(y%#G65K}_R#iIIl!$pL(Pf2V2BZh4-O4i!+_#&xQX3)(LcHw`W< zo{A4Y3pc5|z%K3b5YzE$3)I&dH(}DaoC!Ep9M_(x3u`f3{$q3!2G}1rn@R@hQzuV3 zK9aco>cI!{)`XO3y4~mGnW+XLAD0FNQ!&L%qxzGx zpHRh?`mY?)Ud0z-z<4YYhgwQEsLjMEk|NQ(&MR9b(mvR;8ST#8rx&YiKsoMlrcJb5 zhWq7mT!DxUn-@`DORjjV3#7@L?b}4wlSlKc2lkerYJ~+q&TqK###|1VnPGB9(8SEO zdzWhvI+~<2=OzqvQN)^KH6>h+y2wzxuawsb{>T;4hQ*t##l38pkHYbnue>+ls*&>5 z(8j=1r&n;S(#j33AA^DYfS9vM^1J?5%Noj;w-LAe^!U#&*PiZHYq2VE+M0d;(7PLg zVvw#A-c$>akPx@lrp;6BbKL8{^$GG+Q!z17Fdml^w(-F*D~cJo6dK72#GXpY8~TG}la%1xDZ6}a4HutUqdYYc6P-bcMYeV=M50E^P8?Ln97ZK2_z-auN!}lJ>bpFl4_n+(oAk5 z6$n0Dtf+|fDut;>?Eq<{mdrHgfZ7v|eTQ~Ix!eUeTWs?kR?9(*jj1f2p8FkDdahlV z64}LvOL-cKMG0-~k0#kXr$10vR=(_@ua|=<0hFsY6IOu;bc(*9o_@pp#?|s&?u1JH zkTdaxX9i?i2I*PMssXXe>-n2X=*Dk9-5eC zq-HSGHbA`B)HX9+e^m`{*bDUY;C=+FO7(r2$PZE37F-LJ;z8LJ$W1bO#E_av{-iORYxyT!UY z^DKG9@O*3#TSZ{gMlI*+wpL7qYt93D^nT1Y&7<=B?nk+`@hPJPAG2T$QOpFl#BLhQ zQKT!c1vY|STFbMmy{$~2Z|-WVuP?4Cs*)*a&TT6(Ehwuluj^nM*D^&I>rEINE;4Ir zO#zMPUTW!RnrThF*PGDML>TMN7*33CI5<|g9ymMe+8We(`5{_{J+s?BY%Itj!&EkT zj498K(=UtWn_0_dl=qH)Z+jR%CHIp9}_lG4wVqJ&Otp4Pm(Kj0_d z{>V{8c7Nl?GN)1=vr(H(p+ushxFo{3bwbP8~y%o*bSd z{Z^maf8iG7WFRAk%A+8}e(x$d9Ct8(10{~pQ)#+5y72L-{!Lvr#96jI*_=n7uJxH4 z#+BCpDzvOSGysMg1SiG?3E;|5%*o5#!h9;M-5Uy;fzn-NPA8TAQvA;YU36t$g(XRh z7*Og(3tCD;)UGM!|3WhnVj}F<+gPs}GH*#YB$@Zyq1rAL8q1f;ojYl#CC5fdiP~ z&e1^kV==Jc-EP;oJRS*Vn75TX83@aakfie2sdL2*bBADEpjIxfUf@?A;QaaT^8BH{ zyF=q;_|rXY>qcmOSAa3^LHs;BLvYgDsCSh;cjiCHG{Un&3JIp8v>`gYqBSRqdy zH>RtNIGm;OWsstO=DR`~8C+(yn=cYe?v0oq3h|D8W7IyHn6+1yV|uP+n?rzwmnC{? zE_ch_F&bQl2@I9sy7T9yc(>O{n+wglE$H|%k&@3A5RwB)p4gZa^CmV&m&}z}b=zfA zinjMMwfg2;=g4LJiov5P%!W7;SMNz3D#B^DOnC6Qz-e*!#nY=DA{@F^ozw4GtKp4- z`h7)U3dB>tt{cq-mXT@hJy`l0XoxaCyLxc%g@%`UAR2uQ-Mc2da=ct%i)6WxsZ_5= zN*&~x&t0z}Pl6J0^^D`l;XC@mk6X%mh1CDIGVPgWs`u=NRn z_98}zTJ3`CTEkioG*EH$H!hFC9?5c z#@9;eCP=>@2mXFywA7XINbeJV)25u0EEptsRcEJ@nt?l(0>#sQ(T&P;a@5?8y4i zD?6)B8X*17=W+l1 zPCb{qzLzELTeNtY7Q1{LoJs1uo#7Vho?E-{@dqa?j&1+aE>$S<1WvTSyvYg4HB9V} z%&&KXT(Acth*$hq&R1+Dmwxt%e0y?2Lf!{3%}DOLRC4i94k@?3@(Pb>WGG+MtaWp3 za?QSS(DUjBv`6cj`NUN0cK)BS@IX=`|XP+V>)Ns<<_1t;BP zOinICkI&4@uE361fC=IZ^Q<#2GOu-1kyF%I6_*I}4Jj4)mqF zG2HRCfXvA>Egwvbb?eJ{bsQvZfObLr`x`%t+N$|P&NQh|6|2nC`QLRbmW8DdBEUKO zlYLDo6?AS(BgjNPStVc2~&icB+21wpqGBPwQ@5#WM~*=T)CHgZaOPogO; zNe0nyhiE&tNp*sC=2>0>ubc$o9J5(Gt^JsF`fA`b`8gIRr_pQ@WxhHgb>`{sNm2Y{ z*At?gb`gg|Cwg=p>=a~5D}bIFvsxsB9sv|@;%z=Lbi}r+#Cj(9E5hHHSygi`R?SD- zMZn&Gq?AArxFIe}@H=h6yZmDf+{gk2iytI4RoEgum&-+p)MN&_alJInbx#Q^_t>vL zg~GySX}jN_ypF9JQb$_-XqXBRz+HR>Mp`V34@e)fTJSdE4k)xE!}mu@vT*#XrLe|c9DSNTJR~+8-$njcgOM(0=X*^gUUKd-`zghg z3>=^D)BMG=Gs8EDjACNB%gdEBsYeg%!R?GK=SgwNYK9d_ZxZUZo4!|ZfJR4`YbelW zE+1&}e0Ze;8&R^Aj2Hh?SEI5Tgf-w_huB5U6g_!Xx}>-2|{DLcrf3wiq|+&pd5oDh7R z3T=C^pEMXbg9K6jee zAB7@7svpACW2f2X_{iSLYsAAyN3yg2=jH1ukUxj(D$rE8j>d;o57 z_HW15-<-K@vd1MZ$k64#*O&MqqTd)&}eV6%y@ zLc%Ud<<36kZcCaj3{1o5mrpBYl=qzR<0bCr!w?akoImOi!SjUZ4D)p$O1X|xqqNOD z%vjX>gJXK0nf640A9MCom_AuGW?jv=+UgZo(=ZC^9ZU!nrcTgZ&6i4x>ggcxTQ2)} z?@kj}+TCR;S1-7wqs^c1_Y3e1?LL2ASz7(o)J83xO+<+EM&Rpk?`PS{CuAIDIoa=S zI{j}ZC>~8X$C{y^Uhnc(xVfml4|^KRr%#jWNt4Hcup(ME+fZ@{?2D8MNb3Jm!)2u zJCr;BoF5%rH_icEDqZX-m!M;Vzu7!vyGws-m(afX3L%mpinajj9BsKiHhTO|EJl~M z_j&GXgJ;ey@A`gAqU&_T#PoEG930oJ^=}L-Rl)i)S`tiJ(x}^#^ z?zN@a1j*5WiQi;UlH#RFat&qo;V*w)tN@#sBt{COAQydob7!m&2(!Pt*0{nc6@2`T z%^JYhn!{3MDE?qvL)}Mb2sZ!{^W~o}p_j}JRG2<&qPZ`4QUAh*&JJ@LcCBw5@pBYTuw}MB7p{ek$v|3qmYss z$S(AWQ0>QQA=LDUAh~|#8xubXFPuM>8Y!I8a4A%%H8E0 zPh~HR^1*cXdpU|&Zlqi{3JB1|jNS^p;9d~GH|aw##Y6*cJ_*!isA@b>vZX;JL;!7K->VS|0%^vh8z}nNXJ^z_r2X?dW5S1&^Mb`cor#GC5q$Zrv^-j5t?0fiPE$%qAs_z#HELaST;V z0DkIPQM5Q2FI#sb=UQ&S{Y%}Lhd;sA`n!uVlU|7#yqZhvTSBiY3L7*)9QU)E8wp8@ z0GLw_b~)yKZ)7oTwa_lWsDiPmHtSyhuG$P~u9a{42=5$_v{&h1u3ZVGajSH5lkTJH z&W2GzkvCQLj_uW}S3EY1o6;$L_v(FI!k9GJ}j9O^~3-+`6X8m|Z`C8WH98>H28(uG8y&y@BA~b3_u8$+{ z%T8U;r0S`%EgOtKc>xu@OAvqX;O9?i+==qGt6iFm;T%BF!Gc&tZ5r`O)hZKkTVwX-i@z~z z)1wCuo^TnHaOF3X7cRR}3+tE}?Hj$)+pfT|W~+>P>jZ+|&_-6`aPCnHNHx_gR%lep7x-ywOT6k6lS%LhwOWrmlt^LC z-e6<1Th7SLPT1>HQlcND2~IjpTo4x*9~c-QARsvTXODPB-oU`X!^6YXH2J~n!^j|_ zWJ`j;PX1H5=go1>3Z(nO`ZI>&#m+lVI*gUR^JqhKzj>C(&W2FvbwJBJ526P8l*b^o zb`Vy(B3A!n1s_SK)+PtBcvd^Sx_*tLKSh76Ss?L>Uh^^m*aL?VIcui^gt^1&*Nwg> zwrZfHGwFfu(*4lT;U<-`Bwd?Tt153E^#{*#Z^ZS8PXd zQt+T-MpQOg9GZRFT}&Z8yyk`(x&PmEJ{R{GLffTT84$$LI}=iOa8_I#1k89DtgEf< zd0@%O0H13%cqgQAyhc2v>tDlK z@idom*orx7zX=7e09Xr4o0VtkdkNx^=M7_d_DJ$G9t6^=$^~CdP1~3OdTM zt%6nHc3<{_PQ0W31p<6huB;JJRq8_ZAAF6K7NVK4icW`n1AkJ0?K42&a zZ}!?Z{Lcih@u%AXw?+^}Rv?`6kL3^ve)9MHZJ+7VKIyXKnna-%d`bQvoej7x(JJaQ zBGbf{Gz_}Q*phPHVbYc5rfu^C`mY{5VO~AsmX7@T3tQzwZa&wEm|U6kh@*)6c7Lp| zlllM;5E6~=SG$t2EPzM$#*7ozKjIkIl<%6z&iHoxQr5qex@Hl}OxI z+SSRGt@6u?3X*J^GXRNc?&k|9W`%c0Q}LQKe_ZdUIO22PpRe1UsaS4wTPvc?YyDrh z;$r$nX!Vx;AYs+?)&KCo{L+|G`{$TtM%l`j!o*w;A_*B(As7)9MnmdEHq8s?L&D5i z{1QU(?$M5jw-2?yQYHT9{u6waxOECTiD2`0jGr)8bH*fFilZ0Mww)JTSe#=RL(1X@ zV-^z$#)FtvAyF`Pl}B%4MO33^V^Hw@`^ngusG^j)QOKIk$>sLTMyQdxcITeUIL!kGshn4&BGOfg|)pHTY0 zHyvDRA2txJGhZ+;Yhlq(^QFWM*~l>aifps zL-@`Tf1i)pOa$V|bm7t!y`kohc4mAhqqMM}vwTG$C+JY##|@KNzwBkPhyG%X#ufVv zp8SbRF&|Lkq#i`c+PW*ltp=4lUVw@hk=wF!;(Ch*+dZiM;>Zw6rvK^~rC1q$ca-Hc zVTT7NWt^Itnj4#AFm{|NH_Xn5Rg?x0t&h*E1)D{1*LAT_4xx*52w zdDGJf`XDLaJ6_gdK_wh)<5B=5ZV`UW+OH*u#RqGVsaVI~25%}l zEP@A?kKhTfN${rQ0|hoF0Jor{)$WeGd7V&|ZATk&sM+16D`U{jZpI_EN+>NL(03d; z=ji>Fk!Rjdyh)Q#zV4s8Lz&x(Uk>9Z@4vhDxKgOM;Px2jhW!lW4yN$E3G~C93=pNE z^d)st?O#VG5}-TYD!Khy9sGf_G*PAhzv(q`r-QKxx@;KsN}8H{4;GrFOPZVC#0J+n z&sFd2?35Wb1foi1aMI0-JFH(bOgzYRXykYZ=bxGYO)}M(i|sSwBGPn={&{Bv|J-~{ zQ6F_BorEiXD~^=(o*iW(5EVaAKQs{*1d%&l9W%$VL{GBd;e?M zQ5%5bYkQt23@?0y7^J$bNfbDQ7CBA92eNzDrAoKJcfg zxY!zgr&&B42n4<(qj-ER`Ij-4(dWDz6lf(~tyM#!e_fFn6=-LIcmlpy(_|!98Vcs{ zx#QD;0Q-BpNbm5VgrI=koUk^BRzyIyQ$i3bxm=)fULYlq6+7&7aTpCYTni%$LRfzcW#~Y!5gkOO0|o@L5~RBJuH*Ehf`5sedFV z0_n9(15RPi$5M`VMeC8lF}`w~-kmN!{r>TbNM8g&U+dL)mY&DW1?*b#+#}C_+9^PK zEpWQrmqV8d3m1KU_}L1c4#jJ>T+h>zcplpG3*K(oA_95@clayRJm|ayl%dq->cZ#V zFXTn(a&J&}*i~FnP-SX<*vxuS!$fKjr=p67mm@8}CG@{ssz6GTl2*%sFv6kcKK?Uv zG%!2{{aA_B`Gki;EbGPZQL~&R=9>DXA3}6jjz1lr%tcmyNxLmvU&Y-$2;g znNJvmUO$sT@crZq=)JGLc{=D@g~c{*SJ5&1R%)JjZ0Tl>?S64lQCV*Gxs$YsuJF-P zj+++?2)Gx>#$R9hnK&dO9n@e1Je#79j$QCMKdX8~MO!^l)&9f`jW@ z2Gu-arbsn6uf%mkOJr9Y=SW%n`+*{TJxm$hmnc35wP*p zTZ#LjXEtC;595oatPBQb9}JA*p{K7pfF=R(3 z&;tH72M-Vi>;lHO@fT79R;G6NzYyBL7crW`ikjj{<;A%rbF1PqOBO)e=vV~Zu^t5J z_aMmseG>%kRq)-p-IkDZ4U&#O*3A}vZAlm!?k8wOJuWC|xa!hx2NOR4cq4abW zKXK!e5TG^sKOSuHze>wk9DudVUzM{V-?eBD!ThV9+QL)gG325GcR!7OKY@V_B&Fsv zba5uH;(|(}^t@oaA|S=p)$9fu^WULyY(NlZPq?mrw3k#`;#BLGeR>^P=VG*k!Fon| zl@H~|V@k`v$~Yf9LBrs7Urap5`KA46?1uFRTKlJ%En|XP%?OS3AQKy5vxyi1a!~TrH6vA73;WgT?^`&CxRx611P668o=muWRxQzcz`w*e1 zxe;uK#gXcPYgH4DcB?#pEXQjxOUeZg{rTIiEhsI|F*erv9APwF*!rVWSnysE(Vx+O zLr$_kH9vddOfRMpb|Sm|WPyrg1{)FTu={pI3_3|6%g=c>JB}^vFIvMXHMYBbs-^T} z>}>$;3^tZa$cxMfOiuL71Hf2p;ADHrKre<~3_{sJYWkc>YHtiNnTj`Ge@HN|DK&sU zfGS+c?UbGc*xE6ba`1#-eY3bunjw{((h*|<#d4yzfm=Fz${H}4=>7GfLX>joN%MU` zWK|nG1xE>Fp47vc-Y=T~ z+BfN+Nuwv}Xgxp2s6=F(d)ZNr$XL+WpOtrB-mu``(ZskZ8L-(n0zfRlL%~Kq+t4Sd zMSj^zJNBGuWtS=qS%C+S7b(>rmyfv5J5?$36B6OBYFhm5)PN=1N1im%@T-7;*LwzV zeJyTLIS`mKtcKs>d(emMOT4TzRBui_T(qM&`-Wum(*1#%j}PN*mMQn zjqVhd5Nk`()4G(dr4l2@?F-gDo(&3qa`Hjz*U(C95-_}%&{PZKlizyih1#YA6Dkwn zG9IZfO#p|QX$!bhthnyKauiwwR=-pvvELin{(59Gl zOI~Gd1rUG7wkj#RFoDr+PLhGBYJY{7sQ=lg+yW$mZE5O%24lZHM!y8&zpOy`Yt;q? zrKPvu-wDJgBqV(N_*~Q$@6WJ&rM_G&cm$&n#3d!Uto0^#*NOZIQ5f_!H8t1gd)TRo zadB9e>VK(cufNK;l(Vp~2*9rT>u<#(ClaaI8RHi1f2$9`UEaLBJkcODb8~ZKC4@gA zPAK}d^LBz=3C1nxFE#hDiRiUc)Te*{UKR@RX8Jqoqb$I$aBy%;0eOqxp|t(UEcXHM zEN~0bg5?o=?ZgexeFX!SjyBLD)4_)#J9mxO)iNOa&|$=yA>{)bXMc;TNf zzA{(;N2e^xnwnw%h2Hf)2MA~i%LtLgn+2(>X{c&Cv$g(w!}81MAC?{eSzGw;0XP{Y zwKt1){d4!r_E78$Plq4fv6Ni43%fh&qS=3(xUtxW(a#w!bv4fWv#vus&2~Ez%(Eus zYQPWRJGB~Jyc``*B8j-J8kS;W{?I2FhO@jr=QsS4ovkUAXmFX|!UR{`+Q68ct z6bxTlT6$_E45UPVnHIszGyQaa(#;wk4_&$h`?q$-t392a63+4C+Vs;8mor37K%{_Vhh#GjfI$e1(yjsU(9KRqBK7dixuf-oK!;C}HPMqCyAlu5 z`~mElQA}~F{~!FxLPT}P{;fo0ltCm+JGqRai-|KnCYz1fh^W7^rvQm=B!19P+TVF8 zc|dHqJ1Bp7Rq)ZY~D@* zQa`ePK|i`3BH!}{5lsC@{mHVB$U(yX)_+mIXh6~dhh`TnGbI=)DdF!7vRlveooC0E z>QhOW-u^LRWbtZ8$nL{{&7P`fD(3{i4=T96p^?RoY&7t0WCxl`AgMc+=k<<)-m2(n zzV&FxU2ZQNMT~wf2~_WRp2PRO#(8%O&gj>G7j4Q7jp=v2IX!Dr-|2sm;Jh6>5M&mDh1!R>b-d# zkK+qS-jOW=;f3YZPLQzkOby2B!#eX9h4s6v@wNz`&FQ)lsc4@Xb)gfIw!Rj^>a!W_ z3?leXa79vVw>_ru)9{nh=y<>Z;$P$FRMI=ekEk?ijHT0$p^{Bj&6LYL>6^mMv)xq% z@}fvd5rEB+Ra9(-o69NSGwuA)Mu+cv42C!O1C~Q0n-QEFX8?2V`mX#h4PHK(CeW5i zw=QFz9fXD{LBO3zjeB8Q@Zpzx4u3W1`Td9V^%p_5M|tS4&P>JGKH`V!J8K0a@6~cd z%BE@|Vg0k%_%}#LUER;}qW+{AxQ}-I+R5oe^qp&t;AW%qe6}+Ptu8xIgdVqm*V*;q z=VGi`UfI=ll*T-@_g;E5QCT`X;PIum?!lye^W4cC$JYGG`j^o5KLj`yXe8fO4oBJP{5Ve$D2KtqnmwcN{RvX7jR5LgZR}q5zdiwDrsG;!h&yNjk zgdiXc45o+9ggy{qFyX<$p+1ZnV;uo59R3j${4nZ(FaJMmf~;cY>EI~40|o~cm&r>3 znIHEjFn{?W{7qF^lO#C?3p2j^nX$%e&nHL|mN5TF9E&Ca3Kgb^m7<9c4ho{f={-Dr zktjo)!bk=xfAs%Cwo;H(-Dgv&K@6TcZ`GWY-cr-F(os_q{T5ZZ*Z;Z0`g4#l^+o&7GaSy}hliz5V^I?d|>DU5kTF zGi%H8vQp6~c<>qFVLLc*S7uRw4_8I`O&H>D4KRk18H9yMQ=y?T(SewOx`#YQ$)qnW zUP(j~3KhsHt|}};s_KuufggQB5V#Cwg|ms{BYwCL`eC zERCH!jh!NRz|Ts}w=+@k=8G)|c_kzy`|KjkmWB=w$HYY&j7$dCK!B#xz^6khD=Q5R z4Ie*#tgW|CM@PqW#=>VZzY} zvMb+JFET1}MA#+pc^_~`V}s6H_x5oPS2jB)z-7HsR#nA${yd_?aCT+}0RbV&-4zds zEirE)@H3jb?J#dd*9s*iWvnUl*}f22x$=4w;?5+c>*iN}P&qZ~slJkACtb)=#lQj- zIQhp+%)d8(YlfV1TyRiS^_i4g-C6}P)pl=Jyc}TOhxD6JrZZhWVOL;KU`0?Y% zyaiOG)x;i8t}i|wn-p`acMab_kZ*KjO77@MIeH8_5F{~W6Zxt(u|-_-5H=nG31VVl z-N5zX;$}FCQbfxbv3m=0B)ov>qmvWLSSi_Ne8reaQHX?u1Roz?k~=F|j0Vjivj|?m z5%ssjtR*EXr9FOFmAX1!#N461Fews1U_>NYQ4x_79Ij+t+xtYhrLP3?ZI%`n6cfiC zVWHp`yyYLun;dxg^mNno;2yg@deB0=dNHZ|2!(?dir8Qt4>Jt}WHB~p;{+a_u%;qg z-vHCp8INI4FWJd%zygeRzIZrj$GHPSG~GzFT-W)25($5?Ss1 zL~bsEgNKJ_{fFdE<2EswQb7*RHj;^PZb<`yo7%gBuZP`qso9sQ`2$PC!>I_VMt%c(!|+{1b6)SF%TA+>_TH+vy0r{kvSW~(I@1q=_A*E5+fj2k=XT$w|la; zx`b(^Lv&n}U%NoQZFc_^!{mK+O)>hkn*RIe51e#cOTE;v3Et?lJP8*fV1PiC2o6qsPiELN$}!1M-gU^0<_C^V$sG$W*PfIhDm8rCQfKg6i zFqIj!S^n^*6c}CFZsXqg$KbP~Dqnr%Vi37O>hk008)|Ry1|x$V&Fj#yB;>B5B1>9~ z;56Q=VTab3f=dSN2z~Od=uk!7p`)!#j^QgsRX8BErWWAcuoKt>=N9II0z`g;wh49t z`lXjW0|HstQR5T5ujj^uH*@68UDmL1Ix_XCTUs7#Xes-#(iL|1GRw)Ni9!sqmCZB? z5fSiUHL*+Y#l#=RC%*Z)QMg++=Cj%7{Iqz8`j8c{O$Bg2uRcjYHUm0xhstm#YA!J7 zBKN8p{TGBWBf^;^ATbBnpQujnDfpBy0bJsS>p$Qu_M3(&lKSEbW|E7@cfbRbXrzC} zXA*OeU^yTH${LsR;~%PDb`iM9M`kYouKaI zde8lY+~wt%0f6@(6xw2v$*PFb>7iF=-Razd^#u5E!Vr*OvJ%AI-TgC$>fr|#(5POv zD1OOw-y>gw@3bJ<79$u&-vMXU-yCp)UY!qhOq0%`#inXO-UDZdb!l}apxgWVDFaT> z)FUh~3#cj?7UtCp&WgVLsi^1DE&l<|u;%DH zJ39~R%Ro$9#r|~rEbYU^#l_&Cwmg%OLb~AZHct6)mM!QuSN*%;$Pcy{hpg_RJM0M_ zpPuF*0(h;ffM3EL4)fKqt9z=cyxiy9smB#fA08NC9ME_Z-0{Od&7K2;er%diX4HUv z$jpKnX`;9r3rXGFgaeC7PWK268a7~+jjHY;+4YeOn%W%00y@Ecy6NR3qY*kE3OqU|bBXC^$xn(x)}i z1&LzT)JPMI?ITF)O6F<`-qIEl0Q76CCeR4XMBh`~j7@;8nWQEfVD@uXRbU@45ec%ou(`X709HvtHp!`Ee9SCK z+C2E~6XYl1X3J$(xY8AZE^xC7oK~Gb#9Xe5B&8I%Ka6L^AoaRhlg#kCJbITVaOZhK8^T)a zd9|kA{!Y*4+G_NCkc-mA^NaOGi>>$F3EXvDt2r>PuOFY^EL|Xb=N#SIBAPuH@jN45 zo-A*`pNa$W6?}fzf?=6NpENd3{ZVnEH zkK>|a_4&MK0cFmLV%kd{qfr8)^Z~laSo1fM`OorR@xh;ACUg;yDe=MPIDP+^bR!QO z4S-n}TO8piFSxPq#o!Li`*0KYeJgI4e;pw!_L&K=r$q8q z`lIa$KSEAuYAw0udDhIi0Z$ZWgF>wP!0;8zFhW^H(EgDG33BHr>HLKrQ-Q*D@2#Xe zf}}?&6Moa2Q0`jtFD_Yb=S%QUF~y@|I~0_t2Zr-lD%Q#>aBb`pZI0CTk=00$LD6C# z-(iuw&lA4S$V5{?lxhp04~aij6iHE7{9$ye@KR`t;F*XMc2EPx?dkwB&lN`Qxvnx% zcCdsj7v^xpCIzSSlabBmRv$C7U|+sb{OpyvFK97~gjDVADtOe(e?;vw3gZheOzY`1 zBT4Hs2A3HG!+}_!BqR@OLH6dAA0g_Pab^%K2WbJOuspKGn>STpLj1Az%pe2~u7Y7< zd2|ckH?IN+DaWX?f{-~B3t~m&aV;QkssagF#{#l~&^X=|D0w7tVuG;B7UDzXdm#>| zY;_keYMfvF+Pyr+Q8md~G0I-4G+w1T@ls=q^Xw3n*Tdgd37mIRZ$z%;fG!>T;(Xdi zbT2$y20vjQ4P5!{q$s{Gl?`J3peJMK-}%ZnuP((qZl7D2nW)0y8oyyS5`y@C*6$m* zU0GSVb=(?jV&TYAaoSYo$WxzxHNGv_xJ_O&@>8+2fp)y;C|0xol%YX&xG-x}<{1^b zUK4Zmsx2W51X%5VHLQ2gKtoQds0q)?!&YXrHqw$8C31#kRO3g@RXd64Pa!BWK6h-i|*r&}H z!RDq5p7#g#c8kW5cGn{_d#H?)gQYqGRZe$fBbV5+UD3?Mf|YhZhvvL<0_PD>8_~a8 zEppYl4SsiTPC4Uw)mM{mGQLT-nX;s&BB3UqlOfPicZwbpRBk7Vjt9d8tb)H9>Th5P zgY%3m%1x|_4b0?sDn~dp#@G@kUnHr$ic=p>Rvp3C7^-kQ<980cMuwB^V(I37u~lEN zC9KfDsm)YY2>V4<*;`FcLEKpV?RyDxkc2eo3XXvKA44)A;-zpbdLyj*Tn${5Jqg@S zlB^E7QIUk2^+Fjzy zNs-FDxY<`)LGIl~IYx?J_emXuqf)Ikm`Ky8VCb>bGIP(BCo(8C1)2i!U|#f9#$d;{1qm%7;6^p&~F74>rkS8`Y5zO z#JxYfN#-XQo{=mMQvcQa#RVO=%{yTvSJi+jTkZUJhIy`@-m1X0Yj^mIQ>)a1lrX3w zK&TbBK)K>qb3(C3F=U0WGhtUjpv%>DP;|DHwUM=U(6M&#vR2lyHq@~e(UCW_cK$}i zam2oPvPpeaO%q#7BM(tey9(@F*BFyOC{G=s*FK?|S{LAf8+p;?HT^G)2-JEUdsK*0 z&B8b{E<|tQg-osU5}p_{R~?-LK6iXyw&*yNkw~lY#$)R|8%}nX6KQ%pN%mMF_U&}u z`}=n7x=p6wL#MPD_P$J?J4uR7eqWjj)Ivo~YDjFAeKP`0 zBeZ!rJ)9yQl|{dPhb*yR?@sbEl?Aic#qW4sm#x!AS)vSGejc~vX6$(zyG~B^TsGzm z(uEoE#WSx#5a8J!6sw@fzi#YJjmm`)Gtw(K*>Q%=IpR>C@?#W%u@{vmhY`JDGH5KJ z^)EghzLh^b;6r~e&dyIvA-lmx82-G)bbFYDfK)d=XLQnVaH2lE)QMK>YJFNGGlcjN zEVIjPzYBefNQ?akYC+NF0IOL?jAkWbujo+KE;*@?ndREeVsheX=I$~Jl^tU55xZOQ zzFT}|_A~x>!qh5T$GLVwx()}Te)Gz6yi_(e*rJIVU1O?}ld4@K1z9i6H8-iNT;-d< zIaRV23`FMsADE;B?SDo=jvv6nSFo=>Gl5bh;I5@f(2~Dj#7VyGQVpNdF+G~?@?eMztq+RaEo!bg;3bi`y}sm5#!@vQI|+#4mxcHK3PRc$#s}a95i`#q8P4bKX`Y zr~`@=AB|PX8XoS)1fy-@oE5^dZHyu_QVQ#B>THW5dKS|@=bp72Jr-JYZf)-TOJ7N` z9-+A*`2n6cP?l3c5u81d#OESPzx~vROSH&%b}#-V0iPap#6Ie7XE#0Lj;GYrl+>TT zW6UYJHXcdmXeIAH>a(|YVkx1@^j2yZ_JrfjRufEgT)M1P$EoN`GJ&e^x1{+bdnrop zIvf+QrBP0ehT(JUXUQXB;S8ZF3?VVy==g)E*n`NJZE)eHh@r*^D4!9L@-C6{exlI0 z^M^REggdq1FFCQ!#qxf?vk4*AfV6pK#AM>z6)7M0VmO6hyU2(map$A(2y0_MZZL0m9zA62G)RsVVI>M$U`NJ>C=Q{1cMdCc ziY#|e;-CI$@h*GIOKSEYCTg;r zr@I1T(7`NTIwkdS1`Fm`-Ab{u;&b-Mt0IxMSaB0!&@5mD?N zjymwV2bLgjia=RalS0C0F#0A!kfeZIn_S*Vljv0%i-|b;b@xyjP-Kgv#;2DxH2oPg zXW_Mw91n>#CtR%r{q&aJ6?{TUx*Wf;?Uvka9fLr|x#i?9V(&7u-?^RM^uxo2b3(w* zLztNHKg1%U91F5*TC2a5*B6#&wxoUdVA!pw*R^}eev~#i>V3_2av=X!lItDGCENMj zBUmK?U3o+UBT`HiL8U5Wx!ZO*b9g0tRpcTRwU8q5#6glq1BFjH`l04CwlgOy)FjAN zU*Xa|P@HsU4}s%qWk!!q=T}#)>A&de=80W^O(o?bVG%y#emuTr4kaC&t3ze|$b-|C z;~&?Qm*QT%A>Mt?HBAXk752%|vH#w-kFa=EEU1xwn}68dLNKpsl#{Tpxzowk)g*P) zqPf>X*xy1J*^rXO1gA?pS9n8dB%fU& z7@HRul41d4Xn~VY5H;dOi=@flE5G=JqbZMK`<&M{xr9LJ+kV2pyX1n^6FGiIkEhI6QjzSeX?d`Te7&y>_?69||NbB{&9hoE+_rk9Ep@ zkpz%&bziP|WQQdmg!PAlleUH(pGN@h@Y^Im)f(edj>RcC=SCQ0qiDni7S9E}`gSGG-%D3~!JXl8>VBUBWRB;pfH09#5 z&bf~mp(R4Gr!WTYg6frZLfMo4aM9QH`!_Fbnub~UmZIhal%&54s2#y2&d?@E;BYp) z8&9P*fDNCyIZoSEhR_(i#al{A!hAb{`IfT+zAR;2_d~;*=P!|XF+k!-B~)-~PsomV z-euq@&3g=R(ib&cq?*iC?e+j`Jc$E2BUH(xxN94-(aHi!oo? zN)3!Ikzh?}vff?BPl=0WEZft4IP3>e1tJ46Dl#5creyNHDe6Q-xUZx7@dtfsx6G}C zBynoz(PSnWhyuj%%T<6~IGr7GkK>{AAT5Z^A_x^JDO9%pL)L{g2QBO*hOjjv3og-9 z@tqwR&dJ2xfv|1&41t8B$@QD?**=?tPL$ma+5O_!Sv|_uas`+22BZu~OM>vKxG-#N zcwEuvj?;m#tqjaz0p^1DGC%IPxBBXEO@5A-ys0U0s1EILGUUgHDzjbO>yNgOmLom) zGCQd%xP_IinUlGUb!a(?@Jjz6Dsp~KE5FQ=_H*IIS(E9e%P`ZAk6v@q$)`CA1ou-k zp7wi7iW|CO7wC`O)HKp<-<9gTEA2<)v0$3Db7x+Y>CZz?lQp4AC;Iaz;UK}SCXPcIa^ZM=nGkck^oQwdAJ&3)fh@kt`VPj=3-nc;f~`u>UjA-S zN$(G-lhm81~V#Q%@?vaKIlHiJpiuF0XE1kVl56GN%-n{?X&-natbPGJ6 z&C{CG;l%+&;EUR@?91nkH`>KFj~lc3PMY|_JtLH!cD_KAHwUh6>7Hg~<3`s|9c_LF z+&KuN9|{B2#A+m?i+Xoy!YLC$7QZwqZeDU1EKw&~<+53#eu$==p< zc5d(GmP1^6SXJht=hly{&7h0h)4;1-xSSUiB{@BPJp1G$nLldAozErKJf_UMF)~Ng zWb?U6PrLp>JYQwl;4?S*b2WeY-u(L3D*mR~YrW2e0I@{Hm8i>B>Fn)gABTWRFuq-Z z22C$l^0Nt3hOyky0)_al&lIb6^bS+T(?z+#iJ@$`$Rle><*tR{%ehMrce7@psaxS0)?Z5f2|DW>33{`QNJcvl3RfqPnt;J?(i)w};XZ|EW>m>vlB5$Y`a;8t&?K z#u;(6oaM}lr5R@B37p(|?3=gj zj8(GJG~#pPfh?b)5m<>(Ru`Bsl)%k@R-Rp%baAz$u)}0zEH^r*=YEZu* zZ6x}A$ywK1=krAynRJY?2UvLjJ~Ns#fV1moox2NkIq{Wdh5anA&%%9-j%NR!p*S;F zp_CbuxsT$$Yu)~0^>N>!bjhK7=M{~CP7dl*nHf);q^1rw=Xmkn4H{(9Z3&-Dtm;(J zv(bZrnS0l)7&rZR9G(i2wst&}!f=t@kD2}=R+4C#vq2%HqW;XHu>GR2k*j_P$3pY^ zt_JoWMO@Rz?aEQ}xnD8iZkEbsRPA1Y8( zPolkvwf6^2MjdMK5lPV&XWUAL*r=Cr{GRcNZO^)1y!?H%l~YV^Rk7Y)INUqyoNjPt zZ_AK{14fk7fVVP)q9{WR(sIAoy*c&FYmB=HRDR#hLksiAQF09Y$hB` z7Q*<<4<8HT<*`fZBA$CuU^x~aAEk#s0(Sc(g#nehO^ZOG4brRcRhD?i46332#TSn3 zwfNK^v@#+EZGwo@v62o{yb+pL9_Ypz8Uz#+pZhnp0%%y4nr6x#U)i|g$SES3-9d}* zh+5CdANh+?m@b}}rRWN1IvLUGTG^(r-^ATQ!exO@6V8C z-?jR9Y7TYpsFI&|3Kh27eQ${kJD57S$yvd8Wt6G(B>2PGvce9U%Hn9Gmmb$Te`JU= z$|Fls5X<7l0j!#bDd~2-F?_H7=#<^S)SGtg07VQ=iKoQW@X=VXlEE)lz2(Gg6c9T z5AnV_SsWgIX@zaQNk5lhdizdu)E-DpokM6U%6PKMD6onzYqAHpWEvLfvH}fIgo_Yy zS6k!aXD)_zlbAKX)#%);b5;~4E2=c`UmarFmyP-ryJP7&uu}E8c z!w_&21>13T?7&K~i3BXJ!AMP=SGcuK?6H;4k=B7UnxHcrX>AT?iJ6S#W$*ZO^y^_K z(@Y5Z%KiLaiy4%s%fM{y+f*3_7@dP4(%)o-*ds(yvG@B4_``lilZD{C+U?z^i8@aN zy%QpzHuB`JckO!UcBx#aQq3L9wyzNyI-$mE5c9u|9-n( zTZ*x1N{xjV$4$TKeRSD$$E}axhZ7}k!H^H-W71wK^bUER-3;9KH6x#jFH5Fcz8ufK z)io8&$H*voA-A2XSNg51+aM=5M#*r9?xnu*kgf68cL5*Rc~m*sR9}j)5R>C7hq{eH zcbELC4Mf=TSffUWIL3&1#&D-X>z+WCjn4~Fkdrj<1Zp9*pq+08XTsUTpG6BzPiY4- z={Y(+6x2lK-f&A>a!GrjOIzN@%mts(;Ey#g^Esx86iIJ<%Frji+W6kR2@*H5HKDYo zQoJI|a2hSLN%ypPLfoJ~+=ba8zJl8lr@}*4?kV($dDnU?@m5B`p**6SWb3*y_tXaD zV1wP*B5qu(Km1CLux!xkyEB8`O-6CE#{1{_42ZK8&tmlFF@gFnj6U*v9e7zKCHsNV zF~b00ufa=ch#4iTT%f6xQdN>1^2Ba(K68q>MmPqO=F!{ar}*R`9@D2l4u*h|EXV$a zVxtMXh7(hN9aI#M(nU|cCwigU+MMHlST>=-htPOHpxxkEcRX{x-Kdr0=41DRclqnR zYxdZ^^A~Fpy6NfXcWvjEBQ}K%U&iqYac5q3IQVhjlx=Cq8AbP9lB~J>+yLKw;n$#k zI;SLwn{A=AW`^zYS{45}#ts4ePFeL`T5+vR(G3xfYf*!oJ>AvD(Z_y!zu-k~rn+lI z;1v*!9t=+}#Tvo!OyANy=5_)G`!wlpRkRZ z4p@D8_9jCQLXq#P9f@`ojrLO+d11^udP$&eN%x&hG_1+-a^_{b&O_a{J(STO_KX46 zIV@LwgN!I&k7D%+!%1J6{ka2?`m935y}#AZo;To$Q^~8ybB?FnAE|r1rIcsHlo#by zXL(B?!^7H&ykwNmWO*#paQr?Oj#r`L@ld{zl<3>`=%tl7F0tCpi%wA(Em`-TtCoD! z;3bUDG;=0wMWtX^9x*2H7S1b}pY#f4KWTlDx~Ez6qhsS#WNEg>27XCl!d1eoTVR)| zhCvYz9xPVLtcZ;4!Uzlu>Tv_g#Ma5Ki_Kk?0h63cGU4alFlq2vuOFQ3{LngKG>#D* zsRc>v>-!vsIZm>$P155Yl5uthvTa_{R){ zZMdjw2x2u)L;HFBBUAmKSim~>257p8oV7tscYN{5Kv0vGw?E&b4r7+GB9!{jnzty1 zj(#F1?XC+_`QF38E6tXHMrT75yAWght$06qU1M00DE|@gyEF~IbGTJ31(Dfyk1>)r zpU8PyS}0Ju?mfv`Vg|<5)53a#mybyck(Dv4!-*^ExhLkhINVM6F+=WcIZN57`36D( zmt)P}W>i^J$;b)--)&hSWv85us69i#uQk6$$(OMr=HTyZVEyN5!AO zS@K8@E{YwHV7=)?>alp~j+Z5Aww>l9YsJIZa36LWOdGH$K+&{ZKUO&!)~C=@+L&9b zG-OgmzFuvc{>CZXMxoUNS(vRrU$7iHv!mISkk^;0&6uY1;(gVsLx0F)MR;maoPK}cn*kW(q}4M?9n>Fd6|BfaPv!DYWeOkEQ;c<=X~{m1 zQsujhD~#-{t5@u3iRbTzRp>jjJ0Bgya@L_n^wJHMnDrG|>BY1N_x2+mLf*lE9_und zMRc7{S{QgDGq!UJ?dMk zl;UIj;eDfcS!Kx*k|Xm*?ZI+Ok4bH_pS)Cqlsq26QqDW;k4i8NM-SX59MoPpa5L_3 z78mI$X4}>daGxmDU|oP$2K&8sd2B{MK^g5 z!alnYZdKd@dD3gRou!*dCP`kyU&-h&MaLb$qs`$A)Q*2}%dzNqcBu*K7NIjPXi$q3 zjGx!kckHU*`n*Ybhfu4swPSxKFP834wKblAly`I~dX7nebEhw4kRhR8>tWF4h*N@{ z{3R$z62-D%6IQ-I>n--=6IqK!9U9=;%d!_;41$w<_Y>+b2dvriUE-w39ElUsZ8Rd_ z`{v1hwBvKZ4;BYg;pHZ>o2XT@B4IH(8MC;_!oo0h_fdkJ8VU2GM%*a6vh`+Wn~ch{ zvzO6>pHeF5HeCv768kl6N5$Mc5iq~vC;?7RkKbQc9A7)PE$)7vYvyp;VmF!RwNGn3 z+`!Rv*mBDPJ1!^;uti`djT%sa_Q~ms6?&OPEzbJ()_e|`1u1HmtG|Ug_7d5e z`Q#=wEs$B3TMwHIqVzyCUt^ufPwU5rnmsyq7D@o)cD* z{ZmohtSjMD4hmtS9e$$YDWd&n`9X}I^RKPt>t<8;wKex$Yv%UN>g|eNFDOHrFNeZJ zM^=L%=eQ)?gFjL7NwyP@Dsc>VjP!7+;FW`kBoVv{VX=udR^z_#l%Z|x)Ni+iovWjq zsq>Mk7XSRP_#043BGk%m&DwXchHPJlW*?vhGQP5leI!}-$nOHt_<{`T&}Zur1$JI1 zWW_20Zhd45ff7Cs$pB_R297NTz{eyeZZQqgVf+V=AzCUGaL<-4>s@;KbDh z7X|cc^+euP9Op{_t_^CpyxTaAA`_^;zq_pS<%gr2n=u_C3^Rn=5WGrNK|Cxbb2X!= z>&ggBl`%oLz_elc{fvrYmg^O0rQQM4{R#BgD6YFq{iE6x!;haWhBxl>5&i1$wDY|r za7!Fmgv2L2-ypEbaw3Y|A<-^Bqw22PyHY}k3e#hhUel0!Y239`JoO0No2;C<(9q@r z*~^ov1u}>M>*rybp`Z`0@r{=6RSgmiY(n`sBmGxTpzmjhD&1YDSUM|iOMfB+cgfU* z*-lY!+~AS(ENB}tY7fUs&?X^ROIuM_wTgo&MspC64hBn9M81poOTuI@DP#=6F;3IX zOA5^C&=|Xrj!#X2Y%NJAp~0`*A$D6Fmr&|w8IRcv%q-THBT$SrkY4fnv_6xQQCgvZ z-$AMS`H%1dwTr5YY&gv>m#OtnqMA3EoDQ!SO+Cg(QPsV@XZ1V^x3OijbGrEXhFXDw z`jDokM^E!cAOLhf2UJ4)0m;R_;pFQOf><>f`7FQEb`dk2CNEnHhEL9j(Y4oI>#uT1 z%ky7|kM!1i<(y-_KNv;*d_gU4sm7hY-^DH9KK?}&q?K!oP|U)Pd@@z#h+!oWy|Zs1 z65pFQsj{kXXH;}~+jfCX%f9V>2Rz50*m?A-6*E4_{Kbj1%z!;Z;aJC{8G7dp8)3UT z$!W(`T9(yD^K_I&GksFaK!Y`9V^NDNu-EU3wC+md8_TuZcNa%O+vePcExlp+U^ju; zi;L+#R~@6i5AFrY9Rj&%oDp$g$)L&*(f&^z<~K^X(E75*r9^&FO6{dY*KfVH!AbT( z2@Zit3n=toFhNe^tv@RD?x-`GP#3s2_+P%`uT=PA^2tfMbsN)?$Z}ZyDzoua*R^MU zLB?Kj)bc_62*WlGiY{uTm|i|_-0pwe)=5KAs^-Jub`;;&+xlKK3mlauT$8Tu%&Vby zzi;9CqSrtvm;J^Z%&`W#*J1hI<~*|Rd3A97ait5d+A+jh_>-D9Z<%AQe&icTsoT{; zg%X1D^O{pdd?tKxQezF*7K~aKRAfcw9y&{%@6;lX?LVZ;8uw@s!srX@VnLT<*{{`JMXIH;kHmd_&P@1~*&9vitn3 z5k9Q*cp7Z=)MSIt2%O_bHQb&(VekFC*RosPTe(=Dd{0?@vkFAmgoW1ZD?inr?uFe? z7(x%W-dWc&yk1sz>8g`q$X4g8w-3)0w(j(DXnyiV(-x^%tPmqBY5p1g-J;a_-BLGk z=!Bs6`@x^39_O|T>{|BHu)oS-Ir5oLUYchrMR#`tcWZx>S&5SI6S$~ZIkIv!em}+X z4w7DB)cE73Or{4Rxb{iv+hAKMUWp_*d?xZI8;Zsv6SGbwBVSvnb*du-Blk2hW;3>} zh+107Gr#YWl#Dzh=oQ+dd+EzF_bi~Fm4s~asnT^}&h1zQ#bfHCv}eQOXa`h5u9!Iu zA+gwEPUsRYPb8e5NZd=xUFdrr=%0TT64I$N|0wHSOyskHQ(fnhvnx1Sv!IHnC?pzTfNGsg`(yY=p*t0HK-j zqCHfU9qRtQqNvfKBK?)k3)dUqg;h$aN0&D6jxIHk%94l;2(w4&&hU|DJR3ZxXxPF2 z2a-u$s#_HVQ|>|HChAPRfn-Sb9T-gg+1&eT<_0Y8^WT~}>!_&Ou8oTV zf`D{Oi%5s$P=bVlgf!^Dzz|CJz)*^)bax0y&oF@0&)dDEd*A!oziS%|96s6Xy-y+_%h`09xE(&?K6BQWOoCrVM>6-A9``|Q^KPe% ztmD0yx&S<-Ri2{zZAGaXSd>*m`|ukJA7NEytW8P6Ck)e|)5{cJjr(c>*2zr*~2Y$mAo zUboG0t~nLA7Hcsp;}Huh-^p8sQnqj7RO-cf%Zb0;hJP`90FBFwk87}^c%m1GxVQsG zGj|sgwKZNc%T%XrPdq;?<^oMn>>%$tA@24cToGWDy7M=ql?Mitqj%%|oYng1OJl{* z)rkD?@q2R)c_$Mm2W5}Wy0y=`bb}3Vu40I1_ozeAlq%koAuzIie-o6qvapD$u=q^t zrSCBWyq>`NQ1OvT=mSS}Z<`C!ZN+@zM!i3=(Yv$w*9#|9TNe@lOua`~%CL@$da7DaJ^&LFl`-HQ`E+L)La z0gy{v3Aa8pT;WAcm2}$xccJwU-r`>js$gW5)G3|MBrZ8)x_1B}bfNBg##>LzlYiWO z+S0byswd|e<%xlxrPgeA?=h}A)(V|tOEz;d6GA!WO;+a?D_{RCFfQRPQVYKyweo;L zdsgc{c5LUg+m~R>upPTcecDZ1F9ddoj0ZLJ`y~&l=}pkBJ8xGt6NJp z8(T<}ZBp1He^d7OeByAhyfO=CCA)W#{B#C_`#M}wLzB~4; z)vM%OGPtfk?<$~bQ^KCxMH6DQUs#n@?YBHKPhj9^{v+3%hFA3wqy&FZNm;{(xjtRR zi6cr^t?RRzn(QwwH?k`wiDrdKAq#&rTgwdFJd4@H`iHIih&}SR3`b7vOh%NE2%=8I zE20F%bs`q8qq?DCb|&l0SeC@4p2^(y_!>u;QV-a+^~_ZE+Jmfv9xMc8vsVRq`uo=j zWwW2iFRr}~xoa^D=({{hZ;gd-#&_>p5OzJ}gL5~mio;Wm<<#36&YpV5%E`Sm2BeRO zolK8dor|c16r!#0^f?>lSme3wcW#RLG7`TRBcVJyX?JFmXVZl-%1nM8Wcn^4@uo+1+-3gz8?Qlc ziPoR8sNr#9r7m^N8z$cCD6`X%=h&N9Pfqlfyc2?9_ZwUUD<_-v zXW=O8UyaIGm-(7N9AY5+$zk#Is&VAdKM5d*$=6rEr&P;ixV`&r$TuCaj`wEYF4A7j z$ne$%pr^D!Wr=-3gGA`5g4M4577tU*MIGf-t=rVG+hXUzR>x>772&ZD%(0@_G|&9F z)*4&MOzGGt6Fr~kuJfbi{j+9v<@i|(h1vD-`PIUV5gPr8tkKDsJuawUx3AM3LK!WM zlE1?*B=o|@sECQ0ROGb9@PyOeCum^0c_ud2BxQ{Lf{dvJzF&nNwcyo{rknNkN6Q@# zTsjC5ZvQK!ls&KD1Kq(fQ#}zw&;)Yr2TpppK<9UALj2gM@L|A3iSJE`e{yu_kL|_1 z({K+CcS-ltPS8=B!O;q6b9CSh?|U}(lx(I_H)i;?rr+Ubtz2+c(>?Q02wU; z{fW;Z{p4~!MrRHtQ3RE5@{zq>jvrNB=zN3Q!9!uwUF_jjzno51Nwr z32ZC#_4((=hA2Z=-$a;CGY?z6k-S@t?V){5>KOSQ$zOzfuPT`ar={Y*e@SnFN!yTf z2TIBKPvp$s58fINL=Mi90>joe>#r>__LkYOIw!1hu$La8VPld$IUu)&G6vJ8QlyzK zB^hqBy&CGYgalhv{#sySRrYE6k14OpC<{6* z%cu_H9=4m$50KH-?C~WDHL~;`*ZlaMap)|JDWf>Eq{?rx(umQw_>uteEHJh~`UZNq zKg%}vhT7`~nRpf11U5Tyd>bgt8345=tFe9~v`l-AN)Ha8F!xrh)Q~_<6(WR@E>9)v z8zi^je!Hq6RSGx){*Xr|2T9--UouHzo02md)t)ZDkXnj5#ohJ!VZR_h;rX@e#_V_( zR0-`hh=W^;i~Xk{a=g551ew#1%+ENcQTE?6ol!ZzjLnNpB7+jcSdoV{qd)QkH#u;h2d@9 zMNgg=eudbbOJCf&)wc%x9ez~li!Xe;gl6H%U2=o20@_Nd;u5;(@s>`fO5Z29`{jCX zk=evpRAn@n*?ssEO_hFT#cttnY5Ad4W}r?0>*qS#wo8{Ev3XBnuY{89AW zXm{O(ScFPoHB4K;W-EtQst;>DVd86wn`<0*2x%VgRV$l#ZVHX98mM+{R_zAi+F||f zwHxsBjGHGEl^)WLUAhb2@q@KWCxUmPGtN=1j!!N!b@bh{93S)5sZCyhoimb*t93sC z%{XUP4dq|8D}ocgC3xI%*8rO>T?l!y<^dwlGq%zC~ZMZ0Q^QT9e(eDY`VF?9UCTfYt5g^^C$F;h7Lc z$EEtKs%pK8Stah1U=93szNPd=>-c?%G~*eM0glWgAyK~@g7)vMX=7nYSqk-0+JB_i z0f6(>GfzGDukGEox$^mq%rj@OWDS;Xt;nO7{G!o(hd;!9=)HVaYK9(f$^ljR&s*}) ztf%w>GZ0di_2PJ*xrD}Vi02jec$}M+gF9|zoB;Fx6x{+uMqs-av%J5?f;ES;L`#lM zRTo$1qMEBFa>;|ZPreuh+alUDxB#&&B$KZ*<7=?AihN+)pXKd4w?r}~UR0P9#~NrI zGi%M>9A!^gC-!gKr4I|I6MI3#uKC59??fuaow2SrL25ia&Y5DGq9w|!C83c!O~L?! z+e3ZFiiNt$Ct?0-$DskR5x(bFo)YmUFR6C?F|6750G@GZ7(IywAGpY-l#6Lzt7|@+ z-`7Kok#pBttiEQA6uDGYO}Z+so+a$~Az>d@%+43hr)1dNz329F)~ao>ywK-f+hI&h zSU1rN;Y0iAON%tojEEF&aK#X1a5S}H*J-QjoMIDGMLf?I8NsX_otG>) z4or}^el(J2ZYUzb>v_#Re)ITe^0K;~75q`Nw$pRgaH(pHWUICS)0V+xJM|mq4CAid zsCTHut)HAVRJ*90DE4V^&j{EZjXc#h{*3}o4%vO2S?&&}ml~(JTWX4xlLECspV6=aaMCv2$~0Y0{fyQrtJW-Y?-*^g z9s}8o%Gi!g*^PrB>4PR6xu5JxscCMD-2Teokxdg*)GQr=j|@_-*qCRte*gEJhoV%K zkT^NPMMaO*yA3s>6#RE^YD@Ew=k6_m`$XgM2OGP^#p;C;ZF#aI>mnTIa?6|}=2_n4 z);}o}UqqXIb=H)7AO+|I0v88c@($AgRt|z6b#dcjM{c z0xy0q$HO)r$!RGe#A8j`qHa2<7luQ`O{1cTGl9wq+$*oM;^R@Tp8pLRrbg~h0|G;t70EY8eLKb98Hy)(M&JH5MjkEF zoFnr6_%ZOFPYs%9jS=;&Qc<3rqx-(}I6r!vFYS!oQjla8O{rv`j5@fGBw3vbD~Pv6 z^ZFf|z9qH3GoxW4OJdPlHgYK&wUEIpwP>z^Cu2t00gvlAf7OvpY*QPyTKj_bIJES< zYMeMcjynZq^#^RE<7E6bAy`!juz4QoVA%31Om@%`oJch*m6Wh>vbrw&{zvvvp$l_H zlapi9@Z_l=SDw^6hB6Ux6X&;eeM7enKIdLD6T*pVecg^Do)7#Ks`CQnb8OBRr74gC zc8yhdZDyR`Si$_6Zsl-;-6qeB^5PxpI%1R-gr!b#D#zV7Sza*sU~ z^(}9-f+y~jL)bho`%a283qYfJ?WOuxD)9mM-M z&A!=c$QtJk`FQY=9yXAUzaAF~)A>=)0|751Gne_YmiU~Yd@tMY{r!j&zZ5;hGLknB z+sKWu!R%}|g;7*7YWPy{YgUw~b|BIGHjS;D<0*u$2fZr!z zt@RSNW5>l5E9yi?`pn?yFEYDr@OlCg4hr3~6#Z_t=#Exd8kS1_%dy(nuA)S@xEW`H z3$hW|W|5ZLPwt@Zpg8Uing#^KM(@Cb{CHtvfN#$&&D%tD6%0bI_so1%er$%};^yZh8nrMz8(tm+16phz(2Q&m& zCOR3;pP6zsklfZlPtqK&MOs>c1=BjN(nx${Y0G-j`+saGHSzy_9+s~1BrHzC)h5!# zdV0){N{0*hHqcnke)l6Ck(2P%32#Lq%bJWX2r_i_^&3JW@v4joivQ4Is~wS#dTvn& zEb!LxBc`X++tEiUQb#Q-qt#UBce`)Q8=C)%JoHq@$cQMlO@0&&e8s3EY@%Omdd?{O z<+ZOGfEaSh{T!w^@~s}4Wd8PW>a61!(u}`{=b%Tom;V|#b zMjzvwkUXmN^LfZ*=##T|-EaCdIn z_xrzl|G6`nIVYKO)?T*PUe8*4CqzL`3LAqI0{{SEfB7t-1OOnv1ptu1=tziP2woA; z0RYu)UnIm-TvPX((HzNo=SoAK4x6_Rmo5%($uk-~?ltkm6#HWyURPL#5L#m1GRtJe z=&9Gd52@KLFjQB0OQZe^Jp{!+v5idZ+biS`Y*?I$AJ|N6i2+mwKkz%*zKfY%!nKyP zmnJxOLR?qQ?B%i>_lnCpzluVOcE9Wjc#sF_5hFg%WAxf4Bb{tme?KgwzNr6h_ba&D z7<4sT&Xx|+gE118>mhb4ZedE>EKO!|j}Z=p_jmiKE9u-%PbvIIKEtVJB_x$k+vh(> z3r?L%X4Nw)p`Nq%_=Xz4M9qwHT$oyaO%5c@wX_>>n=~=bl2O*Sl?kMz9#L>Ch`yWQ zPE(0Ui(aD-+7lk;Z09j4M7*@+^RRZB7O{&=#{4U1b@^v<~5x?U3RVQcb#u* zJyz1af^o>K&EQoG^1hoT=XS!^xt9Dm(KmfnL9f5X-g1`^S~#HG`6K_ek*(|FTaS*3 z)`yh#s_`ILyaav;?eEj>9>(Nr=^5h=W-E8sflk0#6h3a50ZF0f&7LpyNryHqOrv(mM!cvX}X1an_@&(9q}18r*FYem%l5GQ!FLC|8pd5*c4boAiDCr;$FTXW@lFRV;} zH=S}k@lrHTfuMi;p2&DOeZAH8i#GMmL zf^)+DVujh@c(+VH8V-5pMN1DM5>p4Xx$#PUiU2q`SPcNrAm!%jROC-GuD;~;8&D-L z&zWO_kQ8aYA>(8BjI++_RHGkM7PkIGXk#En{nHI+9rJ1_ll&q1+XRkav0SYs?^QQv zcpara8bcpD8Q*VP;!ji|`}OPTsA2dFzvCMoXD7eyQj#;8QOr45`D{wuHUzG|wYEMW z9(hfu;`M1Gefp9Gl$;VLC?85c2m7j-hCuJdkbS7%i$gxI8{p1YCMS}LIGOJkbJ<0hxNmeX=S#K>UjZUqsT7;k7-2?j>vy*wzDIMzN0;<1SNGYOV#&pN6dr;DgW6$vWHZefu z{L7ClAX7YJqFQdI^S9nh5tqfvHsMSoWmF!0`PlQPZ@4MPUnvT-H!eYPIc1u}FV?0n zr_N8l>qBxE++ z{80hTBw59al3A3w2?~4tndEp#-p>0P-)mGQE48ys3qP3ZNLCf!M6DlqnLGQN`!spV zo*?CIL&LoN53*-+X)Hgrm;q~)>vHk3B&J_g6H+xBSSk{xV5-x-p*)(*iG#CHcJ~Xm zhm)wS!hD0zWqUdkcCpiJCadUIKQL03$!}9VyZG{_Trs{H%~rB$YZJ=6ntP+K(KSTRvLSbUq9k=2r zxazA#Du}LaPiImJg~{g6Z3}DVAsNp>VMxZED8<7>w9Vq>7VNG_%elC?h^VsfoX74RV1f%Av^V6fy`=M-_V2FLdx6mHRQosl-qYsegQ@Wh@>L$r zNopqi7W7&|w_L41eO2Ffk@Lx3)-rTIiwSrQzRD~d_aip^QzdTcW{sRwLu`h4*vhe*t@fj(dSme-iJft+$hGuxkKgXoW?8c8*mP0!8r z8(&3`5H>MnO{w-*El!SaP0M|8%zXw*!6g|~L;=E1ZBnTL)6s2+#bc#uy<{vZyydX9 zWv_&rb?N>#w55g@f6z7RdJAek7PZpqkTiT z1I>zY`^Jp_sv~@Qr3pv&4J~r0tlFLkN52F5hXr$iw@t_v0@U}FHbReTGtW|#(DRoc zRqR>Wc;q|9Cd6W_Wq-$!=^|XsZV~|Jq)oof-&>tqS6A5y!FDtPJNJR^{%C%Eo)iHdY-PU9GZ2JVq_ zu&@nIsdj{^n_^dhI@l`E^+R6c7P^pIcy}hn{im9bF{`+mtH;P|;Bu%4ObX%y@|63YS z+BA;S=04&~x3`%zmOiwMiZ0))UtVC&$g>ve+>geOE?y5z@HPn@C-`{V9fbK;MkS+E{!-z8b)iH84H#2KL}9 zHqPNm4<|{s2*d%~b8Rh>M8@p?NPh9_X5Lh}4EH9Bu#&#{hs&?(I+W9H(z5ya z3)Ie4LOSU5Q|x3Roi2sHTSEn`~E0sPX4>MPWQx7G0Th_ z)a+a!7EAT<%gN&E_raNr;c#q|Z^AihXun3SUWQ zm`ME$*y8zF#e!{6h2D&&2i}IhTpvD{WHd-mj7M!tetH^F+J@ZJ>h3MN=Gm#rIEgJ4 z2IdcLQP7nN@WJ0VOFO`5b9Mk0JwxhpOC;!vzj!T3Z}w9@SFID`$$ubHz_88S+)R0C*3zWLaOevE-y?kHgaC_a z51A+1yWb`@WO!Y?O)NJq;Tg@*HrgapVkvA%c-iOr-iiN^R#2Lt(JhQ-beYR=$|iw8 z_|a_DH&Ba6=r&}{bFAZ(V^j5*8q;yw#)x6Ssf3gxQ7NnIy|6ULmK4T+>|`qI>|9+A z4Ec5AY%;q-)Rsq0MxJi45EH?2z9~bN=>E*iAu!M^X1nOIuz$ck52m0UnGN0Gp-YO% zjC;>69TgLP6OGU`f|~PA9D-e?C0S2f_*Q@W>cRV1 z<=&Gtn?7wls4jLOLA;X7A=Awhd5gl)@WrZ=dNX|cBXU-j@ch7jvrr#4Z>8iN3iR~U z%kuMl*e}cEd*nRNWKu&vTxHmCzUqXbboe;(e2xocU9}++M%jUioR; zowKqTSx1-Gz5OwJQ$(Bq2qS#tvbkMSrd{G*-Vbq!OmW@r^*g!+gIg>X0|a|zf9M1T zmaliuJa-xIm_2ccAd$B?#3MR@X-CkSGro`L$uUm`9_mA4d+r{A+^M#Kav2 z3muZosULHO6#O&PuHXB>RL1cw{0^gf!UoTMFTOmvDYx*t-CL~>Jzh_+MFt}k@W zX!b@nMhFExlDWVW?P?>)Jr)wn&)q#Dj~ST055{bAcXaTz8}#&7_Zj4xQVe3g^2Dlj z;-Hb-1;YM0PGpD_>uIPX%`a8&!cm{$vU!KLe2(d3)VnziB*^g7*y5L3S+ZDxp8YW| zd&w&jVS)Hm&b+dn6bH$xVG`Lg-YC)~8+(WoS?Z4%bn}YGrF}|!x34e<>rtr;A}*GnZX2*Y zs&Bvu9vVF^1H9LzRuZ$XOzlUov+b?>`s)?l3553Y6#020ZVN_(=XeRyFiiIb(xTHE znuHWunrtq=Pj2QTY3H^MJr*1VXDdlX?okWoj}|Dtl7a zB5teWQ=4CBf^128W!w=HIfiaDF@@ijEs1c*M`Lc$>ysnRg@)UU>kT<*`NyPi7pFAv z3xLcPWj7VXXytsh(F-FAO`ujJ^+IRw*yn=4tFr0%a2S;iDQ&@$>Y1DLKR!j99)Q*WW{-izd#JmsCSXgzcW>|Zc8l1;Tf@^eZ}^=Tp)InmQE zWCWGQ&=lxDRTW;W1&DVk;X$w!w`ycAozR8BbErrVqeueD=em6?ii~+!O}i2GW|cix zZ$$T29I!@iUNlFNU8E^i*z}P&AvZ^ox~=!&SPzi9D}UqtUB8)mRe2kJ{g(UAzlFLY zAhLMm00)O++9jz=haxAEc+8}Jaje?=CYaZYZ?8v@+J-o{`yHcI2*OuF6y4(t!BP{? zV3Q)q&ckiB%Xxyfx0T8-1|Po6TZz+Cv@cX9r`NH4I;6@n%t5-#$9e@SaM2Z)mVQJ= z6Dd9qEO64U0f3*O|JhB`DNfw zmeb3mQWN%0@W$vC6LYWpUmN+UX2tvp)vhVzhRim!pDsE_lvc!-ciig-mZG$sEU7`=d$h>~2_g;PuCeY^;(nMh8U1 zJgPqi4!*sa(7OBn^GMZRPcQca&RSY{JNl;RrekcFmMFHz5hzg{KFao@E>TJ8bS5w~ zj%MCKfT51RQgf>LH;lDGO0^=CzTqpD8-_>z{$M2vRB|sA67z9lfUSJ>xOR;0!ZHP%KJ!{mz4D*Kzmp?= zILhwzlsZi;=j|guOG_>1jZ6^QqHQ<03nxhtxa}07UOCYG@Tz(<8tStoBvibDcC;dN zpXS|>ToTnH3eEVe5oL^hRC|wttVgGF15wFPe5IGVQ_d?*&di&`Eg(4%@IS#Fnr$}Y z(eA9Z3$iEbNTkAdu30?C>eSqdtE;9mXGg=9cPm+toeDiwj2}WGtX%$)-!c6Ok(f8x z?^W8ff+Xk_qKD~4O#8ZSZ^NA*NkQ!E{wHzdSe-CaWCPUPVq?AUFbVHt)lzF)*d%;r zBAyLRhF9YHV+QZp_}0dQ2$UE4>kBYSD(xPM-_Lcct?Jkn6_U0pE-C&l;K(ZywXxge|&`C-=vA zx4+h@f9EVenms98r#+`cP{QZtm9&(z*7#5EW+J5(47frAI(TfAAmtn->_Kwtgvg=4 zVEFsmX=nu${sybUB3iU4t6JxaFe>R;+~HO6a?H_#c$i^bh-1tU-`nl$@9HHgoaOZ* z*{Q9WpTr+ee*Bf%GQK?Zw`wI@zn5rxED)BXevou~SR787+qumayNH(C67MtGv|B5i zKDoW#Uq~T;cxPw5aO(9=z)DfrIGXbB}?3J%~`gmxCFVf<)f2^5+ zFBFeO%zjwI9(zd4dwDt5X`|^KQ)k|WcC@7So^?^cOVjC>!`}H=ZA6wnzV&Oto0_Qn zMV@bdUkS2gajC9SK48mc1_jKVD8b{N(TLM4>c=hrnmqOn`)&Usp&K`o0Q9`Jxi3nW zFBA7d^mZ$54G&f&CVp1>>88LL9GPW*`UCUnR+q3uS|hBGc#dVAtVo zxbG7T=2CZCqd7L!6 zRUV}Z93(uSuu0>4s0rb;PT^X={K9FIA#@hmkGOEbBl{~4Q-$D-=gER_mQzFbG|%np z{RL1emSeog$yiRKNc!VL2>ub-PcNyLC3BD0-=Tuvs~V)qDNfoC=G3E(c4DN^8F?4^ zy?d@`hY9^QFv?uEMhL04F=Rb?L6+7W+_KLVNaL__B@j{3XGf3=>Q8+7&Sfx?(M~_1 z;Img}nA=x>RkRdu^!~&cYm;2l=+q5s;_-@6xA(1_)TdHY?r&oRQ9TMG8^)8|KD{LC z;vy%sB2BotW0C$Y^b-@bkvETt(JO-_ZbWXctVkc4$MAjFB6S>EesW&k z8T5Mj?B_gPyIw9Si`EBIeSO-nZg+cihBk5L&Bh(2Q&@k5DXWb1Kc0blwf%Imb^Kpe zq*o^;qH*Zqutol@+?CoNPrI4lXmnNiHe-O&S^cIhKjYDLgwBlf^e8>)sWS=lD5-iU zeJ(W=3yJ7#pQ6#qcFby zql0L&EP`)LAHFa|5^PG7d)1o~+$7N-3w(jodVk#IZi=B@0$pP>lDBv*EyfTd7$<|PQpHK$hWGz&rFq62CD;DMJ&x2 zz%cKG*8BVn1@TXl0}q%5iss39zqsC$NQWR&o@ZD8C*hT`nbrK1c=6HsxfnqbA$rNI z8wiTpHhCI@jg?G8*lDN#wgZ*wp z@Xr#DI>-a{U>}^E8T_N}&Kd9P7A1+~E_NK2%gAqhJn3F=^hJ0-FcCRf;rW zkE=$0KJ}Id;)BzVej4f@Wq&hrOaFC*o`DSJthgO{X`KI>Yqm)00H~FG$=vbhv4&ub z>8A?Yht9y)a?EiVhd*IfNoPR1br&KXEv1cv(*@e@nT1kD0|Q zO{gZqXgPl-SfKzqw&7JONNc53zIU2L;g%Ry%%0?;fj#td|BDv~o_WOotCnlR)ok6eOQWxj9<5H&SFD%s8izeHmMWrzY~hdFHRP`8I<;i3-h=H#hfkp; z4aH;UrMSIEV6@k?Gza3Cl%;g8tD`@VKD%|PE#XEwsW_PHXb!-7ol)DXDWT+g#; zT_f@$qLv+@C%1*9F5B(;(bo$jMBmC01lO0@Db9WIm-edT*nF$=TxVJ;%%3({HAlUd z$GwEh7rieV$_~hg5#Wq(M3%P?fD%|7fC4ot4uxxuIHf-|)x+JX8V<1jJ+O@3w%2^i zfP1ZU4qEN~{JAYFXH@sGqWaF=z$7a<#>P|_R7Dt$XcUv>KZ9IIdJ)v!yFp|-yZbkX zH)5v4zOi<6K_yV3GN6`oCoLJ%T1RbU1B9x2OMu{JIbBrq5gm7LO#_W@po_r;(gjv+ z4F7UGVi>7FDNRF435kq5diSX<#YaWoZ81be;c7^-jY0D`8H#FjxHw9$KhksgC-5A* zsCsCs`_)Y#b$q(!)B^7oTN0*EKL#GPDdW6ZY-EIN;9!QXx7%h&_?(WrwnJ9@{XO2v z=mlMXHxxI%%~y5BoB8?TJC134VS_h9YKpK1*Og3sZYO3U7Oi?$c0=B3#}aTY_CFbG zoP4{NpB8wn9fM(HZ_%q$Dj{MoG>6Dx_V!?f=i?IcHP2MvPX#Wi420TtS0uPrm1OY) znZ$uL?2@{u-wQbGn^{@;#d2t5I~yE#$Ime3eh(yH%6uzJI|!q40l+(>B)t>4Ig*Kl zfofLpYIKi&-?*bk2dVd}&W1~*=JC@O5pQPpG`V_;tSmJT=f)SuWlfz97jr93Se;yS z(WgvCv@FecwaTp-|JhCm4k!+WwS$g-;bHECyhVptF&W6vJ%g|d7990skTYYo;mss1 zTywCwIXLvpj&lcVRs+QjrA_S7U_r(aWXTsJzx9qJ4zlRlip=Usfnf@&`Z~^Fny+{B z+&vnaP4GWsr)5Q z54*jqj${Tqvk`bi(3>J*Mj(T!&|iQFF{P0;s4P*=4%v2hi)`1ys3zY-7HkY>A?%(e z((zJ1?7cW4&BjO{br^I}Bd4lNnlx|k6PFH5Sf9t1wxCZ|>1J^o>P47+{~c6{bU4yZ z)o}ZutNZXk@kksPmJTQ`x+y9d)M;e?G@e}0?Fr+UY(sCtctSa((Ah zSC`7f%*s+Yd0TZ}kyY#)=1Y^Q`f8_083-N7V|1nsb@yby8eQxSaW&N|Z<8FCo=-k? zw2rMhZG4)gox;OhynJ*oMygD2*^J1}V;Daj^I1%wvaXgL-)NXF?gGPW_RY)>%`F6p zCOf6V;|Q1CZ0f4mHX`(bI0DqWz@PpJs9<@K_$&!H^!`_g;?asQkZEaVC7jwt3@BsG z`mU|wi~JlJDTE|{5$zT3c$vXnZ0b;Z1;6!35c;q%m_fUF!_B+P-Cec0u5SF1F+i&U zqYf2}r}5wp;rb^A#tMO;jn`8`T%2YQnd&r?_q)yA{_&2Iqq6uJ&#S4DJvBsCK1Q9e z)7;&&+=;!g{)hGg729<3ns$V2T1knCzx%A<(FFWQZO|CJxU9LId1BdnWM&fBgD5LH zTR*(JieNG?pf+vLtaWGHj%v3HMlS&l)_tVG3HHr(-m{fD!GaS0k>xO$-udf(&s>g~ zMJC=ozFnc(XNQn*Tq}{0k)>wvusxOZr?C2X*Ec^X^{)=Zbe8-a(W%&L#9(j%g#F%| zLH5BPM)(_Cr8EAQ4(CRrgjm$jkDqtg{0z;ZnyAF?MuP+o94<7KO`8wi^z!nP)oU;? zdZ~4b1|3I2S$SpdCX~_I;$QR*hMc`~S^rAz3=Vz6JxLCQ)HS}QZC?Nq{**bVc4-F; zvc1nYDt>)EaoJZVxTj_RQ01sYwk-X|1|ux53;=UWKI7>!P{QV@D*9PUt-vzi4Dn+A zt*T*cJ)b>i6Tuuq@%y;?`rO%!Qfn7bygn2d07C)Oy)NY|mwLybm-K6UI-jCN74X(> zVl7yCXmBw;2#Mal?OBSxly3c$0uvXAK78sA+|*j6^8#*z<#mu@({j28-%A$7dK|=T zr4Ty4S#cPwtr-ZP`aMo^7K@HpytaE{7qjUG>kp!B_6OTGnuZG>f}1YNgpx|N@U%q{ z3n|3cDeI9_P~Jy_q{qYEufo2=fSx|et0?X!4zja3++&paHB=>B_fOZ>)m@=@fIb(T zp@KZ!I;AKukY1hHL^#EHYO0o($GLGWA)yjG;-Az0c?S8UoKU&2lyGvFtc$rqsMX{d z(dbCP222p8WCBi~G>m?{xb9UVPbai5A#SKRk+!OA19MlGU}L9g;oLrC>|D5vwtOgS z^}elD>sI0AcH-r#Au3rK|2_WlO!+xNtXjmHOjRGM79-IXQoVRq4*H;uqTPpkgY!1C2nd1EJ!E-1YD|1cAba#bVE*$I4{dZ{>0zo|(z2 z`n8}SxsY;mQq8(xQUr||_jV;5tg9}Dp`66sP)y-c%}`eD-@^w>ehUrB^8z%yHO|Bc zMQ0AkG!N75vEUn&%)=_1vI>ierU+)HoF?z)F@#eX;VDCZsr`+kP<<>)SqvG_v(nH- zpO@(QIWx3=MQXr&Pv$q+!v`0#R!$Km)h&dTG;IQ|w@R$%Ck=yBR0e4bHc+UgU3Yc$ zK^GXcm^Y)SXr0{lUv4PQt%uc)pc$G}iq$M#q{@QXe~a#CRB8@4w>&tnu2RN}la&>B zaPm@CEh($WTXl5u`lPDurdqP69M6phIVh`}6g()IB8H#u5`vgGr;FC8=>~QYE{)Y$ z&h4@8k3SN?g2EQSj=~whMbD)DvP)v}2GycJg88FQd|A@(BCF*r0*BL|qgpV_p8MR1 zEtYfv6U9U5eLtsY5|_cZ_63~kePO3^UXRg}O1ffALS|ixkTZReKM4A^LWzeVwpoe9 z#sm9-f9aD#s-4qwJ*>pxNw*2s_Sz00EByM(yz=aMDeE$G(_*NNp!SS(X@M1enHg7NqamIDW-D2F8;MR%SyrFUMH^nnTW>(M7lcnFEG8 zB>maKcZx~`gA&76ReJ@jy`rMNpujQ$ou@`h2vy?%y*XguAOEuKo#WqhfEWz{pXzfp z(;e?(U1g}4pH~9FAx7A+II81!`-IAoLj0%9bZ^w_4WCgZDmJrjN&P6XluKx6YQ zB7cI&g>}4-ci3OT0_H#g+bFw$f0*dSci(C~kk?5|9y6Lot{jsYEg^tWadCpO<7F4v z=fd;a(ET>n!0*LNJP0#*?`1EVJU-+=RD~9(wp-5mf0L=HGVVMKT6-8)qD$bCMsJgT zg)rRYG|4g<)bd-Yq+pUi4RAUn)Eo$F%2#QN=|QU{xBRa}VEX|D%1xsRcbOtMvdFs2 z{`dCVU?4a<<8$q}ve-7H(AQ~Ww%-da3J>xHA67%LJ8S=oTMmfWMR58k%$p^GG3+LV z6)Gn__H{~Yyv17n#m9{okk8ThJ3^8-eG*i0vL!lBR8%ej(T0<r+U*~iKYFyyH?`7K_~zQ?$GO&8{1S;4T|)O1E|@SZTj!$)!1q36j8%Y8fdcOu6(G6t65mqL@g zDx*L~vk^JZL$d-C%B?)ChIOEjarBiLGu7cQ-9x>rAqtRcG}rdJKuijvv*_-S78 zus!r9jlhA*WX2$yHW5Q0?T=_;p*uctugw+Y&C(;n)=S7GuTlAajI} z`mAv`!%QY+lq$=Dr$x}}#;!v~$p&&FE9Gg85@hki+CKsMDV$L}9An0|d!fGXzrf`% z$#pfUk6H+&GJKspH~V(hVHrUUT(hH66L#xyjymbPG^AYF-(zE@R*gtG$l2F*!28gp z5Ke7H_qH{1vPX5?nwGeWR2K1dH#_Hqo0%yMG?3yCxb&?}$fJa93~#=J9 z-IA-)dA%K9Vy*I;^rn)Yd&OM#OO!%!p~q<_w*%5rLYNy_L$N#IIb7r z)yM6KwKtfEYp}N`n9~8BE(CaxC2j=ji{Uxg{@@oTSxqN(u-0N& zll*}rxP5IwL7L!8aC=e<9ejXsXfyX%uJ7VYKItm26Bx1d$hU3zfA-;;-P5qvC&~1! zz;#@)iY&Ub0cUf!&8xB^(j}|8E>#ivqB4#d-F|>tOYn{~N0_qLU9J${*r&FbVxQZA zH}nt7cU;rgc$%##+^0)=)YNE*$=-LpfM@mHc379QBW=SP6HEkma?IsW1Kl_l!(+~# zTZk^d^J1Cx*{{gBTYQZb-08}Tb(rrzm2n&O`k+-zZmm*vcrxjTc&pR{<=lRaN`WxjLP|tL8=arvv;R&s37Pm`Y3mGTK6<7HQgw zVpmI^*b;}>78NA09i1fIee7e2a2maY$Y9!*84xw)44}9-Gp7BCnCFzDbNQzChYJ-7 zI9=2vNAx5b37nm7fBI`o0T?C&dW@tr9G#skjeS8fxTr_&)TaMb`-%bLmh{BHA7$e< z$nJ$M5HczZQ_JYM>`#o!i9J=LXi-Zd=HW+U+Kx^m!yV-85X9U{30Q3kuuX7lBsphl z#00;^9?M+GwN))bu!oTI=#y8jG(4!*T~BN^5~7d~b8f&fOQtw@QK@X(S{ z2E3E>I1S=X!U2=NfZUX!-g}Fp!Gwq;MN-GF;{7EVPMc;x|)x**oEW0OwL2NP5uB7UF5_li3`^1*$0qvwlRbVRx=c=z>K=^fUm4?{qBXV)-us%_A@Zo^ zbRY`FayfB)I9xEs%atb%JL{vMs!Cnl+V}4}Uw-}q?402}Ej8863+=8p0fKWSoA7oA zX4=u176LInXpJ$hx!IL_sBbG*-r)W4LijvJ5=mw#Lat{&?PT8@Pw7MJ2ZG8!7RfkX z3{mKK8ULX`D99SoumXUYv~PNhZ@t918j8r)h^7s1Ul?wf_)M&NG8y0M2{>dxZp{!i z&zjPEYxTTJaySLR%uVg7Cg0ff1V1C}W>TgtuR$!DswQnzB_cKTmAdw6Lh_=G@7xio>T6$r5 za<_MO8=ZRvYwe;}oLEQ*sgiWwV~mV=UL8N98D{PwErJp==4jw)sE?^?AtO4=MLx+k zw#jX(5};+u$NjIm&mNL?&x~DVChUVfC!RyPqa&0R@R_|OVnd2Ivdu<%1zxD^I6uG= z^FQ|bmy{;#7{0cd(J44eW4oLo6Fs#7vp_cYfP-a@x^9dmK$%~;acFcEbq2)wv9{)` z{u~-ck&|{Y{J->>F{oG8ZVUHj3>6jgx1A;wrY?5;`t|W7p zpMYoZhIrqDKKAiEFVFq(^RS%EMSPwSjvQ8-(vyRMXgRy}x)fJZowNv#^QBt^ve6M@ zY1a&w2{K!s+~Oj@INBPkpEX4X_u~f#REUszTLN8a`R(_%Kyf8^&JxQU+ z5v0`y_VmGcH@omAKy8_8BtoH`;8zlXnj$$=I9*CsH3*=ZB>FxyV()D_ulK3EE$@T|e z5Y8wJ0YLd|slu@&C+SPlw$X!<7e={iB;oAXoBtAGEob|qZ-~mvyPngHyb!J2Y{nH} zL}_vqE5_Mr_3oeQ`3-leDvl?1^4_<9-f=7?$t7fLiHM;2;eB$%zGCR9K*Yg9kZ~^o zqbATeEJ9%%I@GsKB?UVCh)b^z7D zD^yshFeE$!qkP0B^*2}Ww;Fb07~74bT^?5?iD|t7~86@d8kLzT{jORcIhfW z-)Z%id-Hx8)@sWV+$vq)cF8#IO(p;HOAgRR<&u`3@np@A89x)tFsAyQ+MiIKee424 z;)A8?77%ix_(ROwM}^Vgjy9ec8 zb;D-#>JLR+WcY~6!UnE(|BRrs_j3K&;Gwo|M#zI8W7abWO@nd{$sq!E`GmT%0k(5+r-&V*MtniTM`CBZ7C*j zz1k{n%1s#C+^r>^U%6CRdTVqQg{!J5A~Vq`4{JSR;znX)WWj3lixJ0zL_c5)mO24o zf9jm0g3y0H(ujL}?Hp(|5#r~u7Y`7w+I;7dLizE8vPrF#)PkIKJ+Az&c>y9GFal)! z>?GA%Z*hfLvFDl*yA_vhem^`Tku;W(^9&-^Pe}k9nL4POe$nQ)KCcHLyfb~~zgB}L z13T$W$9`ZRQXoAuC_wtU<2}Z+a};$d_1bNs`3Bi7PnUuF=Qhi%O6_R7LNsrp>HUpr z%9bOd{KdSx1BB`Cy+=|DxjevLZC26Ov{%saDoEKUb<~mqsX}vWK4UKC)YF#>zBXT1 z;8Oy^Ee|4#tpH4XrAw7x4)wgl*m!(V(P|$OX;Wd5ERezi#SGrQpz=ik(>L4ww=7_z z>iA#G8o-2#3aO+Ia#O_s#{XB*TT(IW{aH3>rI5W-#O&@UExA0Hg{2= z#C65IQC~CwZ0mU&%U%5`P)ZAh=|(dEftVv5dV!GLE#oAC2q=$TEk`-OEgnh=Sy|O? zr~xQhiPO1Fy?V_ui!_G_fEgM2(}rJN3 z@StxXJb*x)4?IBMX zWrqTA5O;wcVul=@$F~@-@IH%?U(|=jZMA~{lc@JGjX)6@)<;iIUm9n3m?bOFphQw^ zJM4ZJDpTt;!gStqRf|rEZm(JH{?*@3LcTUR6Mvod5{Qvn0<=~+%W?JQ1%F=BXwZ{c z_;MJ;pq|>iUfPAO_a4t3mOT1n)LFaEebXH}@my@lWb`LU#hP`g(r)mDhs{EHV?{&4 zfF?jq=joN~gXni*WYjA(`RDa{FJ6x#j#y1j#59v7*hQD#zY*_CFE0!%_GcqZ%c3yKD{39UgL~X=@+6Npr#$(Pz?ZYj{W!BG5{AUy>!( z_iH4X0Qk8*Fz+< ze4LQC`Qpad5EwS&zd29F^mlX%0AU;%62y+JPePk&;CuHwa=iQx{hrwo3>?%hNavrY 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/cold/cold-shown.json b/corpus/conflict-page/cold/cold-shown.json new file mode 100644 index 0000000000000000000000000000000000000000..2797bae39ee1a51a132ddf62bd0f0a7b5fb6f9e9 --- /dev/null +++ b/corpus/conflict-page/cold/cold-shown.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Snowbound: caf\u00e9, \u6771\u4eac, \u0645\u0631\u062d\u0628\u0627 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/cold/cold-shown.png b/corpus/conflict-page/cold/cold-shown.png new file mode 100644 index 0000000000000000000000000000000000000000..28b4db4798984a0b42158552a903406f50099aab GIT binary patch literal 43969 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/cold/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 b/corpus/conflict-page/cold/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..e795d08b1ebc6d904ffba085f27ed57f2dfe90f6 GIT binary patch literal 3776 zcmZ3e|KIuM`!b(-j|5(YE0x8YKd!ov%u(pJJ&aYUgJp6R0~)ZuJ4--%;#be57sMyN zJ+bbFG*BKH!<7FA0uY-8CIF>j;xOTc*rnP(dS%^KZ8ZNFrKq2fm6AOJqy_}QYB-=O zK?cDL1JV!`5H$z*V5ULnazm2b~-T6U53MnqGV2`5xK+Qc(`CySw9Wk4380H~>TxWXkGHyC7dZ-(g??D;-@>He(5T6N%1%VhO z2Eyr`vwT5(khl<33?y!od~Y#`&kYqb%@#QWWR8N-5Eu=CArb0U3@RtFfdh^u zlidC8GD74PMH(6yRDeMbQkr<+@7qw{Xy>*G%vx#kZvQj{8X9_la=#*{BzFt)cyE07 z=2M9|hXqKht_MT!RYqnmAd?YNUV}tVF!I96WT1FU3PTUF*FbhK)MzsNiZp0wfaqjk zkO5i(mcTnR$qG4(_wG5?p4=aZN(tx z8Ue9CLmopagC9daLkWD}S;Y)`KouaBkiH&JcLT&v-((n6 KAfX17VgLZ>IsO9x literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/cold/notebook/Open Notebook.onetoc2 b/corpus/conflict-page/cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..6e5dfc82f4398ec9c92ff1009ceb7b20c199f700 GIT binary patch literal 5072 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 + diff --git a/corpus/conflict-page/cold/read/page-000.xml b/corpus/conflict-page/cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..631b9d5bd28c3cfa00181deedba99f442f148656 --- /dev/null +++ b/corpus/conflict-page/cold/read/page-000.xml @@ -0,0 +1,26 @@ + +Remote: café, 東京, مرحبا]]>AK4FHQSiAaIBAWAqABofi0JwRYR1gL7QRdkE+oeVMvLsF0G4wBa/8yv0et67E4vlXrNInJuhJVq3 +URK/7HHMzCKxQYkOuxMs7nqhWM9UiuaXxU+PBvi60uGbIsRmbacNslhJmh1OQDQGbBADBkgQRSNG +IwUIC2VPZmdoZGkZFDIIAIAeAiHi4kEzCADwEAKLxeJBZAMVRgBACpUE/QGH81d3mq7mkzgM0mcB +mk1gEBgEBgEBgE1mUAgMzmUAmsygE1mUzmkBmc0gMzmk1mUAmsyTOaTWZQCazKZgALSmiazKATWZ +QCAwCAwBAZpM4DAJlNYBMprAJlNZpM4DNJmmU1mkzgM0mcBmkzmU1gEyAAFpTSAzOaQGZzRAYBAY +BAYBMprAIDNJnAZpM0yms0mcBmkzgM0mcymsAmU1gEyms0maZAACZTSawCZTWATKawCAoBAYBAZn +NIDAJrMoBNZlAJrMpnMoBNZlAJrMpnNIDM5pAZnNJrMkzmk1AACH8C7XgWnmkzgM0msAmU1gEBgE +BgEBTOaQGATWZQCazKATWZTOaTWZTNNJrMpmmUzTSaplM00mqZTOaTVMpmmk1TKATKazSawCAzSZ +wGAQFAIDAJrMoBAZnNIDM5pAZnMpnNJrMpmmk1mUzTSaplM00mqZTNNJrMpmmk1TKZplhuAzKATW +ZQCazKAIDAIDAIDNJnAYBMprAJlM4DNJnMprNJnMk1mkzmSazRM5kms0TOZJnMprNEzmSazQtKaQ +GZzRNZlAIDAIDAIDAJlNYAmU1gEymsAmU1mkzmU1miZzKazRM5kmcyTWaJnMk1mkzmSazRM5kAUU +RgBAAAAJBAUURgAAAAASABEgXhT+65lz/EiPi+8SFvjqCAk+gEcDAa/89Qd0AxVGAEAKABEg2R87 +QQrSFUA=AOECHQJSegFgKgAaH4tCcEWEdYC+0EXZBPqHlTLy7BdBuMAWv/Mr9HreuxOL5V6zSJyboSVat1ES +v+xxzMwisUGJDrsTLO56oVjPVIrml8VPjwb4utLhmyLEZm2nDbJYSZodTkA0BmwQAwZIEEUjRiMF +CAtlT2ZnaGRpGRQyCACAHgIh4uJBMwgA8BACi8XiQWQDFUYAQArKAUuH88s3nkzmkzgM0mcBmkzg +KAQGAQGATWZQCAzOaQGZzKATWZTOaQGZzSAzOaTWZQCazJM5pNZlAJrMpmAAAACH8D9XgeBJlM0z +mRNZpNZpM4DAJlNYBAUAgMAgMzmkBgE1mUAmsyTOaTWZTOaTVMpnNJqmUzTSaplM00mqZTOZTNNJ +qmUzTSagBRRGAIAAAAkEBRRGAAAAABIAESDPTQ4xwIZyTqQ8FVDbHxeKCj9AI5rARvPPUDugAxVG +AEAKABEgLMlfQdkfI0A=iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9J1uoA +AAAASUVORK5CYII= diff --git a/corpus/conflict-page/cold/read/payloads.json b/corpus/conflict-page/cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..88ae247d1cd3dee9c2ac3f05912170135543e7cb --- /dev/null +++ b/corpus/conflict-page/cold/read/payloads.json @@ -0,0 +1,10 @@ +[ + { + "sha256": "af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7", + "name": "fictitious-attachment.txt", + "object": "{EEA4864A-EC4F-48DD-94E4-A54023F53C64}{11}{B0}", + "kind": "InsertedFile", + "page": "{67EB820C-348D-4B5E-927D-6719A0D9BAF1}{1}{B0}", + "bytes": 43 + } +] \ No newline at end of file diff --git a/corpus/conflict-page/cold/run.json b/corpus/conflict-page/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..376f7384b0892afab7194adc1def657f070b7909 --- /dev/null +++ b/corpus/conflict-page/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/gate3/candidate", + "expected_pages": -1, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/conflict-page/cold/scripts/cold.ps1 b/corpus/conflict-page/cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/conflict-page/cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/conflict-page/cold/scripts/read.ps1 b/corpus/conflict-page/cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/conflict-page/cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/conflict-page/cold/source.json b/corpus/conflict-page/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..94cafa9cd7aff191a1fc72f639a1d752940f8906 --- /dev/null +++ b/corpus/conflict-page/cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 4944, + "sha256": "36274ad5cc077c6b1ad51ee4c4b90d3b6798a45ce446cbfd7c9d7ac504776b28", + "mtime_ns": 1788585164198018949 + }, + { + "path": "synthetic.one", + "bytes": 43112, + "sha256": "ab763188e885724957e2440a78eb29c457f47d538f78c3cc40e9c1a396be5c3b", + "mtime_ns": 1790489509247358867 + } +] diff --git a/corpus/conflict-page/cold/teardown.json b/corpus/conflict-page/cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native-delete/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment b/corpus/conflict-page/native-delete/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment new file mode 100644 index 0000000000000000000000000000000000000000..66b6151e91f90df6a61a19ef2f4447da278e07b2 --- /dev/null +++ b/corpus/conflict-page/native-delete/before-read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment @@ -0,0 +1 @@ +Fictitious attachment for native corpus. \ No newline at end of file diff --git a/corpus/conflict-page/native-delete/before-read/environment.json b/corpus/conflict-page/native-delete/before-read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..9048ca4bd4e5ef3cdfc99810e0455a385a8556d2 --- /dev/null +++ b/corpus/conflict-page/native-delete/before-read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-7691E04E", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/conflict-page/native-delete/before-read/hierarchy.xml b/corpus/conflict-page/native-delete/before-read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..22ff82ae69fa8483dea86f371739b36373c76aa2 --- /dev/null +++ b/corpus/conflict-page/native-delete/before-read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-delete/before-read/page-000.xml b/corpus/conflict-page/native-delete/before-read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..0fe04aa3c60ab810ec3f3dda7268469e20ca8065 --- /dev/null +++ b/corpus/conflict-page/native-delete/before-read/page-000.xml @@ -0,0 +1,21 @@ + +AK4FHQSiAaIBAWAqABofi0JwRYR1gL7QRdkE+oeVMvLsF0G4wBa/8yv0et67E4vlXrNInJuhJVq3 +URK/7HHMzCKxQYkOuxMs7nqhWM9UiuaXxU+PBvi60uGbIsRmbacNslhJmh1OQDQGbBADBkgQRSNG +IwUIC2VPZmdoZGkZFDIIAIAeAiHi4kEzCADwEAKLxeJBZAMVRgBACpUE/QGH81d3mq7mkzgM0mcB +mk1gEBgEBgEBgE1mUAgMzmUAmsygE1mUzmkBmc0gMzmk1mUAmsyTOaTWZQCazKZgALSmiazKATWZ +QCAwCAwBAZpM4DAJlNYBMprAJlNZpM4DNJmmU1mkzgM0mcBmkzmU1gEyAAFpTSAzOaQGZzRAYBAY +BAYBMprAIDNJnAZpM0yms0mcBmkzgM0mcymsAmU1gEyms0maZAACZTSawCZTWATKawCAoBAYBAZn +NIDAJrMoBNZlAJrMpnMoBNZlAJrMpnNIDM5pAZnNJrMkzmk1AACH8C7XgWnmkzgM0msAmU1gEBgE +BgEBTOaQGATWZQCazKATWZTOaTWZTNNJrMpmmUzTSaplM00mqZTOaTVMpmmk1TKATKazSawCAzSZ +wGAQFAIDAJrMoBAZnNIDM5pAZnMpnNJrMpmmk1mUzTSaplM00mqZTNNJrMpmmk1TKZplhuAzKATW +ZQCazKAIDAIDAIDNJnAYBMprAJlM4DNJnMprNJnMk1mkzmSazRM5kms0TOZJnMprNEzmSazQtKaQ +GZzRNZlAIDAIDAIDAJlNYAmU1gEymsAmU1mkzmU1miZzKazRM5kmcyTWaJnMk1mkzmSazRM5kAUU +RgBAAAAJBAUURgAAAAASABEgXhT+65lz/EiPi+8SFvjqCAk+gEcDAa/89Qd0AxVGAEAKABEg2R87 +QQrSFUA=AOECHQJSegFgKgAaH4tCcEWEdYC+0EXZBPqHlTLy7BdBuMAWv/Mr9HreuxOL5V6zSJyboSVat1ES +v+xxzMwisUGJDrsTLO56oVjPVIrml8VPjwb4utLhmyLEZm2nDbJYSZodTkA0BmwQAwZIEEUjRiMF +CAtlT2ZnaGRpGRQyCACAHgIh4uJBMwgA8BACi8XiQWQDFUYAQArKAUuH88s3nkzmkzgM0mcBmkzg +KAQGAQGATWZQCAzOaQGZzKATWZTOaQGZzSAzOaTWZQCazJM5pNZlAJrMpmAAAACH8D9XgeBJlM0z +mRNZpNZpM4DAJlNYBAUAgMAgMzmkBgE1mUAmsyTOaTWZTOaTVMpnNJqmUzTSaplM00mqZTOZTNNJ +qmUzTSagBRRGAIAAAAkEBRRGAAAAABIAESDPTQ4xwIZyTqQ8FVDbHxeKCj9AI5rARvPPUDugAxVG +AEAKABEgLMlfQdkfI0A=iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9J1uoA +AAAASUVORK5CYII= diff --git a/corpus/conflict-page/native-delete/before-read/payloads.json b/corpus/conflict-page/native-delete/before-read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..f4ee299a7df5c6b52e3098b2bc5840a1996ec7e2 --- /dev/null +++ b/corpus/conflict-page/native-delete/before-read/payloads.json @@ -0,0 +1,10 @@ +[ + { + "sha256": "af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7", + "name": "fictitious-attachment.txt", + "object": "{19686511-7C8F-4B47-BC86-55344C46A4DA}{11}{B0}", + "kind": "InsertedFile", + "page": "{90276157-A44D-48C4-BA1F-976DCF6A224F}{1}{B0}", + "bytes": 43 + } +] \ No newline at end of file diff --git a/corpus/conflict-page/native-delete/cold-conflict.json b/corpus/conflict-page/native-delete/cold-conflict.json new file mode 100644 index 0000000000000000000000000000000000000000..2fd05a4a7938577ce0347213e7f3ed53dab2c0a4 --- /dev/null +++ b/corpus/conflict-page/native-delete/cold-conflict.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Client A conflicting edit. - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native-delete/cold-conflict.png b/corpus/conflict-page/native-delete/cold-conflict.png new file mode 100644 index 0000000000000000000000000000000000000000..8d522c209a274b0ad1076843186dd469c683d028 GIT binary patch literal 42825 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 wyA-Mnpj>SvC#H@io~pOu#NlplUo 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-delete/cold-menu.json b/corpus/conflict-page/native-delete/cold-menu.json new file mode 100644 index 0000000000000000000000000000000000000000..f43854a7a42d5bb99c0b443513bc0c0993c2716c --- /dev/null +++ b/corpus/conflict-page/native-delete/cold-menu.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "", + "class": "Net UI Tool Window", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native-delete/cold-menu.png b/corpus/conflict-page/native-delete/cold-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..c740dbf75d89b9f347e3cead98f6b4f188fe0b1f GIT binary patch literal 45888 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 Y-MY0F1f)x1bVy5W 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 Rw}18)sVHeE!sIPN{|71K@I(Lr literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-delete/commands.jsonl b/corpus/conflict-page/native-delete/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..bf51e5ed90a7bdb52ff561231782da39c4669557 --- /dev/null +++ b/corpus/conflict-page/native-delete/commands.jsonl @@ -0,0 +1,6 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-7691E04E -ExpectedPages -1 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Rename-Item C:\\one-tests\\runs\\capture\\read before-read\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-7691E04E -UseCurrentCache -ExpectedPages -1", "exit": 0, "stdout": "Read 2 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/conflict-page/native-delete/machine.json b/corpus/conflict-page/native-delete/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..a5cd39156d6dc01d0e333458bf636f41fdff0721 --- /dev/null +++ b/corpus/conflict-page/native-delete/machine.json @@ -0,0 +1 @@ +{"name": "m6-7691e04e", "hostname": "ONE-M6-7691E04E"} diff --git a/corpus/conflict-page/native-delete/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one b/corpus/conflict-page/native-delete/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one new file mode 100644 index 0000000000000000000000000000000000000000..1cd05d71d4fe210c8010abf2a4a08bfd8c4abba9 GIT binary patch literal 4016 zcmaD76jR-EW4Z6TjlmTcG^br|Uct6(y8aQ@u9=5Eas{0K%76y!@6HmCp7_;s=>_qL zZ%?edp#_vj#xUjofdIs2feAoqm^e&W-6GEaSK|(+8Rpjva@Q(7(_ydg1gQZ5uo@1i zN{~S?!+%9IP&HD*Ns(SO9Y_a;mBc_+Q6tH^^?(D$sxgK`uj_1 z0`iMn7=h9(2@MU{#D_;fjQV0U1crGCAlI4RyNsI-iyrER<$F*DcX~Hd62xZ$VnHAV ziGeWNR9j&XA0#dW6$6RieVAwp;Fc5>PYLIyIE5|S(H3~*UU^D~?naQ@iH3$vY0DPJXY0H5yQ{Ehss-zV z%0p}bBEA9G+PcK3Vi0H>YR2Q>v}WIu1nC!ZtU!`2DGWWx4gwo8X@`km6wn1Hf+FfI zqyBm<+tz$JP(g4jH&7N-I%AUqb#p)l$_O+x04WLpvU6bd2hg|%CI$l;2ANbD1_@B@ zp*Yhue{H&uzohNI!pCq=_m8~aT|_`MFf%mCGKh4WFo^7vVGz*(>H*=04<7AfC4)x4kw^dQ~}t+8vx9lCaC}b literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-delete/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 b/corpus/conflict-page/native-delete/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..94d5ec410cd4d8d0de874baaa40fe256f0a686bb GIT binary patch literal 3776 zcmZ3e|KIuM`!b(-j|5(YE0x8oTg3VQYTV&8!~B{-?pmd13~0dq?koZ6iC;aJUJ#%7 z_Qbjy(m;7+3{(Ce2taHWm;jW9iNk~&VwY{hgwFJt=R|V7m(0u#+Ftgc z4pkQ_Z;R$aeq@X)JIWgkf#DGXN4|Xjy0I#3i2&3)961bA8yHnq^I8R-tvlx0%)9N7 zc^~f@MxZoHLPG;K@!=5=qrMmofngp3$aSXoF5{-dqKCR+`5u(PFHdC(0P&fCSP+Oo zVj!H}Im;Ks2Z;+o#X#aV$@dn6_}ox2(`=D5K;|eI4S~@R7$PA6tN%gy8w7Z8RPq8c z3{#gdsz4MCQxaSG2n!!jIR^qF1-KhQ(k~hstWS1YNI3p;*twN|huz+SZJ=@z8~DJX z#-!kPmk}bbDALftpaKkfkkXXnN@Y)?)qS?D>q#h6ac_ z1_l|RC143W1hOHp`UB{Q28iw7WEfOXeA~dnFh!O@W~U5;NH&n(2c$u@OY;i0Wz+SK zxOUAv^pPvz{8x}3BOvx?$YV%l@MFklC;`&(3@$)22Z%wu6ovqXM22)AtC&F#r~;%C H(gy?pgmwOS literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-delete/notebook/Open Notebook.onetoc2 b/corpus/conflict-page/native-delete/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..5d8fb2d3a1d1c39967d3a3260efd997006aaf554 GIT binary patch literal 5072 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypCOaMy5#LkJ;N<@5_R(EPkzWbK>C%-ItHftVCJvO>z+d}>=nh17N_~ofg0U$mT5QFMWkQfN3ch2$!@j>E3P%)6W zP4d0PAU+pV%rsjh8N`RFnX2ox6vUQ+%4_!)8iDxgP`-uoEp-sz5Xx`6AUPMv90j8x zFd71M3ISOC4{Iay;HcyUWEiF{VN{`$s|LmzY~>>?d_d(C2#6HmZUjlcXlQ7QoxZrx z!^pR1r^Cz-O+0p>auOT(VC5P6#pW&}L|##(p@BgK81x{e$pry(!^Aq>nkOw$TI~|H zKtrIRp$8~eD>@@BETz?Zec{d>5|@up1ZmavVCX%;$OtQef#r2e3PTUFM?fZm!1_po zh6b2A8K9B>1sEE@GDH&*d{Ct<3=FXP11Qn}6>VT-*eAmvGIi=Ipa62XfgB17n?uhE zcgD*6c3Af8+J$tt%6%YlY~l4`wKsa$r~(}iR1F5O@bJEKW=F8!M7LcfJyG?a{eFXt zb2MP+J;}%f@+S+xa=`ZL@7muuEU81H$o4I~GOH-9E>SbPBE4dilf zCKfKBEGt%**Jv{Qg1P*o>gVS3iT!^2z5jbi2H!meG7_6_1PnG9e8o&ARw4A1n`ZIZ%oL#0IGWRroMpSni8^d+$WBR}X{o`ZLRa7=h$LzKCPu0l68( zh58~2=nJ4u!oKj?#_QQT`HRPnox2a0+CGW|nSsq`4@`8K6#TH}6&L1;0|$T3bKV=8 zQT)ick4?=1l1`XGjsaqr>#x_nEi0F~<-XTvZ?&QQPH=w)6yl{!tXx2~tRSCcp~MH? zH~^YF3g97tT-?CQ1z_NVOB*I|nea`9K?PJUKvD@*V*?Ar6j=tDoxn098%Tr74p8gU z#-TlULHj4SHD*=D57PHt1?d5`Ad49)8S)rP7%~`Afn+8_GJ_sNK2RhTCpgE zKsuix8>{_54GqjF{sQ?)-6GEaSK|(+8Rpjva@Q(71DUM`HXG!An7Q!`K|o$I(4WZ+ gIY5>Z(3g2YRUiONqzxcHfpQk8)IjwcD7ixb0KmI7CIA2c literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-delete/notebook/synthetic.one b/corpus/conflict-page/native-delete/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..2d9ecaa8a40f17d391c48d15af3eb5ca56563ca4 GIT binary patch literal 42952 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 M3cT4Akdl}F4>rO_m;e9( literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment b/corpus/conflict-page/native-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment new file mode 100644 index 0000000000000000000000000000000000000000..66b6151e91f90df6a61a19ef2f4447da278e07b2 --- /dev/null +++ b/corpus/conflict-page/native-delete/read/af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7.attachment @@ -0,0 +1 @@ +Fictitious attachment for native corpus. \ No newline at end of file diff --git a/corpus/conflict-page/native-delete/read/environment.json b/corpus/conflict-page/native-delete/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..5f02dc66163bb072e2494050996e4b456ee3b69a --- /dev/null +++ b/corpus/conflict-page/native-delete/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-7691E04E", + "cold": false, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/conflict-page/native-delete/read/hierarchy.xml b/corpus/conflict-page/native-delete/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..d9c3291f114855dddea2bd070799775265dd86e0 --- /dev/null +++ b/corpus/conflict-page/native-delete/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-delete/read/page-000.xml b/corpus/conflict-page/native-delete/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c687224b62d5cb559633435c7772dc6de9e40a4 --- /dev/null +++ b/corpus/conflict-page/native-delete/read/page-000.xml @@ -0,0 +1,21 @@ + +AK4FHQSiAaIBAWAqABofi0JwRYR1gL7QRdkE+oeVMvLsF0G4wBa/8yv0et67E4vlXrNInJuhJVq3 +URK/7HHMzCKxQYkOuxMs7nqhWM9UiuaXxU+PBvi60uGbIsRmbacNslhJmh1OQDQGbBADBkgQRSNG +IwUIC2VPZmdoZGkZFDIIAIAeAiHi4kEzCADwEAKLxeJBZAMVRgBACpUE/QGH81d3mq7mkzgM0mcB +mk1gEBgEBgEBgE1mUAgMzmUAmsygE1mUzmkBmc0gMzmk1mUAmsyTOaTWZQCazKZgALSmiazKATWZ +QCAwCAwBAZpM4DAJlNYBMprAJlNZpM4DNJmmU1mkzgM0mcBmkzmU1gEyAAFpTSAzOaQGZzRAYBAY +BAYBMprAIDNJnAZpM0yms0mcBmkzgM0mcymsAmU1gEyms0maZAACZTSawCZTWATKawCAoBAYBAZn +NIDAJrMoBNZlAJrMpnMoBNZlAJrMpnNIDM5pAZnNJrMkzmk1AACH8C7XgWnmkzgM0msAmU1gEBgE +BgEBTOaQGATWZQCazKATWZTOaTWZTNNJrMpmmUzTSaplM00mqZTOaTVMpmmk1TKATKazSawCAzSZ +wGAQFAIDAJrMoBAZnNIDM5pAZnMpnNJrMpmmk1mUzTSaplM00mqZTNNJrMpmmk1TKZplhuAzKATW +ZQCazKAIDAIDAIDNJnAYBMprAJlM4DNJnMprNJnMk1mkzmSazRM5kms0TOZJnMprNEzmSazQtKaQ +GZzRNZlAIDAIDAIDAJlNYAmU1gEymsAmU1mkzmU1miZzKazRM5kmcyTWaJnMk1mkzmSazRM5kAUU +RgBAAAAJBAUURgAAAAASABEgXhT+65lz/EiPi+8SFvjqCAk+gEcDAa/89Qd0AxVGAEAKABEg2R87 +QQrSFUA=AOECHQJSegFgKgAaH4tCcEWEdYC+0EXZBPqHlTLy7BdBuMAWv/Mr9HreuxOL5V6zSJyboSVat1ES +v+xxzMwisUGJDrsTLO56oVjPVIrml8VPjwb4utLhmyLEZm2nDbJYSZodTkA0BmwQAwZIEEUjRiMF +CAtlT2ZnaGRpGRQyCACAHgIh4uJBMwgA8BACi8XiQWQDFUYAQArKAUuH88s3nkzmkzgM0mcBmkzg +KAQGAQGATWZQCAzOaQGZzKATWZTOaQGZzSAzOaTWZQCazJM5pNZlAJrMpmAAAACH8D9XgeBJlM0z +mRNZpNZpM4DAJlNYBAUAgMAgMzmkBgE1mUAmsyTOaTWZTOaTVMpnNJqmUzTSaplM00mqZTOZTNNJ +qmUzTSagBRRGAIAAAAkEBRRGAAAAABIAESDPTQ4xwIZyTqQ8FVDbHxeKCj9AI5rARvPPUDugAxVG +AEAKABEgLMlfQdkfI0A=iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9J1uoA +AAAASUVORK5CYII= diff --git a/corpus/conflict-page/native-delete/read/payloads.json b/corpus/conflict-page/native-delete/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..f4ee299a7df5c6b52e3098b2bc5840a1996ec7e2 --- /dev/null +++ b/corpus/conflict-page/native-delete/read/payloads.json @@ -0,0 +1,10 @@ +[ + { + "sha256": "af1af4b8987109ce1e335e538910fb3860b7c57eb3b6110a06aa2114e06d69a7", + "name": "fictitious-attachment.txt", + "object": "{19686511-7C8F-4B47-BC86-55344C46A4DA}{11}{B0}", + "kind": "InsertedFile", + "page": "{90276157-A44D-48C4-BA1F-976DCF6A224F}{1}{B0}", + "bytes": 43 + } +] \ No newline at end of file diff --git a/corpus/conflict-page/native-delete/run.json b/corpus/conflict-page/native-delete/run.json new file mode 100644 index 0000000000000000000000000000000000000000..8bbfce1033d107da6ca6bc60347fa5c2614f6a2e --- /dev/null +++ b/corpus/conflict-page/native-delete/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/gate/native-conflict", + "expected_pages": -1, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/conflict-page/native-delete/scripts/cold.ps1 b/corpus/conflict-page/native-delete/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/conflict-page/native-delete/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/conflict-page/native-delete/scripts/read.ps1 b/corpus/conflict-page/native-delete/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/conflict-page/native-delete/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/conflict-page/native-delete/source.json b/corpus/conflict-page/native-delete/source.json new file mode 100644 index 0000000000000000000000000000000000000000..c7bc810c8304aa1ca14df7b60ab0c9dcfaffb63c --- /dev/null +++ b/corpus/conflict-page/native-delete/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 4944, + "sha256": "36274ad5cc077c6b1ad51ee4c4b90d3b6798a45ce446cbfd7c9d7ac504776b28", + "mtime_ns": 1790472520423354873 + }, + { + "path": "synthetic.one", + "bytes": 40600, + "sha256": "e8d43dd57e02fe76e88c8ef45a7e18487bbf28928d0baf077288a4bb1415692c", + "mtime_ns": 1790472520423873581 + } +] diff --git a/corpus/conflict-page/native-delete/teardown.json b/corpus/conflict-page/native-delete/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native-delete/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native-pages/a/a-merged.json b/corpus/conflict-page/native-pages/a/a-merged.json new file mode 100644 index 0000000000000000000000000000000000000000..2f394e7eaf1defcfe3e06e321b5af76be4f84054 --- /dev/null +++ b/corpus/conflict-page/native-pages/a/a-merged.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Fictitious: caf\u00e9, \u6771\u4eac, \u0645\u0631\u062d\u0628\u0627 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/a/a-merged.png b/corpus/conflict-page/native-pages/a/a-merged.png new file mode 100644 index 0000000000000000000000000000000000000000..2bf9f7a8336aac9401d21ad0614e03e51551d83e GIT binary patch literal 47168 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 z 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 + diff --git a/corpus/conflict-page/native-pages/a/hierarchy-0013.xml b/corpus/conflict-page/native-pages/a/hierarchy-0013.xml new file mode 100644 index 0000000000000000000000000000000000000000..1b7acda08daec45d584097dfb24cc17574ae24c1 --- /dev/null +++ b/corpus/conflict-page/native-pages/a/hierarchy-0013.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-pages/a/listed.json b/corpus/conflict-page/native-pages/a/listed.json new file mode 100644 index 0000000000000000000000000000000000000000..67a675a4f64d48e4266aa24db7ca7731d86ad8c6 --- /dev/null +++ b/corpus/conflict-page/native-pages/a/listed.json @@ -0,0 +1 @@ +["Fictitious: café, 東京, مرحبا", "One", "Two", "Four", "Three", "Target"] \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/a/machine.json b/corpus/conflict-page/native-pages/a/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..8078699df47e7da61dfbd6d7fcafb6d2f7114e2d --- /dev/null +++ b/corpus/conflict-page/native-pages/a/machine.json @@ -0,0 +1 @@ +{"name": "m6-327953db", "hostname": "ONE-M6-327953DB"} diff --git a/corpus/conflict-page/native-pages/a/teardown.json b/corpus/conflict-page/native-pages/a/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native-pages/a/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native-pages/b-published/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one b/corpus/conflict-page/native-pages/b-published/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one new file mode 100644 index 0000000000000000000000000000000000000000..4af78d7a2dbe5360b92dcd980204e841a09ba37d GIT binary patch literal 10256 zcmeHM4^WiH6@TA%z};~N90DS6hbOr-(qMv!n50&LAczQ>qDezZl?0kWqf!frN*cdd zT5&)tR#34?Yn5u#O4Ol))t)+NGMJb~&7@7o@JB;z;utel^H0;z_xrwEI4++t6DOHA zxi|B^-F>_JcHi55fA+5Ld&SlDuWiq7-Celkcw*BlTP{Bl<9^#cV~6|bo?rbSdMWw9 za`oUO26d#>*32|zobdyyG!)Peioym$p|G5*-& zlg6M8{^-9{;84&;dVqF)Lwpw6fu8o-H_sGf-B8Bij7TX)FxnY0{>YM5)#yjAf_CrJ zu?1*D2DBSDtXsi_AN(5l+G#+!?C$N2f5@vl)qWy&(zg;U+zaoh*C)KP-KHoilP~=7-j0YL z%uF%R3I|2jrBdVpJ4NC#20VTGG=EtX#mXN8CELkfW~UKOJGtOlT|3I#Pu#vSzx|WE ze>R_gl8#TQJ5*B8B|&%%Py-t1AqORi`JiC_@2p)t6>;z8JQ?-k?&lB8$QSdui$RI{(=Bw?|Jg1)f(h4_^e5hzz_NG@uHpM^sLJ!7n%c9k< zNG6DEq9Bqb5ROU4U?v!uAgP2-DZs*nu{bno?+qUeHvbu*cWy#Ih{FKby|4U`2X=px z{LQs{{!aes!H;$SO-RVJqN`&3#$*!{#n=YkgteUzt zwlb>n`xQULBwf2-w>LE$w!J(z{Kxa-%$e&Oj@AXdJiSO{M0$P5j?fRg#}03uKd$Jb z^5NMnSuLabdOLQ0_73}Pr0`@FY)(&wH}7rt@_^k7n^(sv_=@|5dtx@#F_r8Q@#H#r=ehgL0my9u1|;7p zccmmdGxDJqmS2H)1jaL(Ro+V-~@Y(*f;bH9YE*M^(3a(FqYnR?mk>Act*`yX);$C(z#@mnm5I$6f4u9C*%frPnZVI z5oadOOYm*ub+ohN>q@gq7C&583GoGTmBCvNc*AHsuXTQc=iu2fHw88qdQI6s^2m+3 z4UFem=6){4J|RE$bUpdkrt=_z4gk;td}R!yQC&Nd`9?7cz8ATUvjBh009g5c3V_ZB z^$EMs_`ojo^r`6*>t`-1UFy^ndYjNDN`SXMk>dubd(+b=_zl$~-h-O`_}-y7mIe1n z$T&o2P5Hd2@0}rvcYhHH_1le?DUY;|xZLQD%Ia z@i&aWW!%D8b4e%`alA(10OGqb3K7SqF2MwQ%xKkMv#?v9R(UuU<)Ea=!9J;e#GBJ%92 zsxL?ODB=$65rDXJE~zDW>bz08n>X~htx={-%=3;r%^Mazy8Pw$r|qoRFze6@2g60) zcdd9d{a+&*YS!({Js9;&P{@JcJn%z23Nu)ch$pk)T09ChNVuO_0|c`n5b5{?zJ>-h zV!bQJLTeT#Xqn*~vK7Kx2MDjrM%=@Y0m4Z+7#BHIC3uHvCJqIg%hMhjw}%e;id*7* z;M)Kl#kBfwhVZt;)|Zm^NByaEmwWQ0hEaBqdkn^TA$cFJz3{ZVK4b3Q%QsI`N1rvA zZb_Rx`$||`P1An@rk}aBc0ER4FoYVjTkc4Fsx9kOrg350V^>dr{IMZuO;zF_mrT6p zjiCMl3tfEcZEa!y*RKJ;2EJ4c=;gh9i^^#Mm&{mE)HrcB_dLSX2RVL}GaWH@2=uEh z#Z1HF$@EMrnIgHw+TY=|BnMd~rUfzvF(#0i{ihcwG9lg~?s~^VCl9$^{8YKzfc)4< zh0283^Aq_>DqdUK5dK0^#WX}tJsAfj6#f~x9O4>Ys<8%_UI^8FsV<8C`(eCBePgQl z6-uwq``0Vqd5E8o8n4)5=BxLL#XcW6r2hs-pKlxo%@2MJeA6|c=bwfBBY%dS9H~)r zC_KmV@#mkxH=Du$&b{ykRtLzwSr`Uz&IWfSfy1Y=H>A!-Wiz~V(mEV!8~|}9E>DV%Y(59=Zo02x(mrSbHpOeawwjsgFG8#Ga||$&!5Sm`0-(oC#R

az1F3xZc04@oe_ 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 Ufvx`j#}H0IMn$?-(j?^n0gXX}0RR91 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-pages/b/command-0001.json b/corpus/conflict-page/native-pages/b/command-0001.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0001.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0002.json b/corpus/conflict-page/native-pages/b/command-0002.json new file mode 100644 index 0000000000000000000000000000000000000000..dc7bcbe3a9d3ab7ff629841610b3af0459d3ffce --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0002.json @@ -0,0 +1 @@ +{"action": "move", "page": "Four", "before": null} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0003.json b/corpus/conflict-page/native-pages/b/command-0003.json new file mode 100644 index 0000000000000000000000000000000000000000..4b88e6612d0b555ffdaf5d84fc00c75de4937491 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0003.json @@ -0,0 +1 @@ +{"action": "move", "page": "Three", "before": "One"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0004.json b/corpus/conflict-page/native-pages/b/command-0004.json new file mode 100644 index 0000000000000000000000000000000000000000..eff482c021828f4924fb0845aa6043a8312745ea --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0004.json @@ -0,0 +1 @@ +{"action": "delete-page", "page": "Target"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0005.json b/corpus/conflict-page/native-pages/b/command-0005.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0005.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0006.json b/corpus/conflict-page/native-pages/b/command-0006.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0006.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0007.json b/corpus/conflict-page/native-pages/b/command-0007.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0007.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0008.json b/corpus/conflict-page/native-pages/b/command-0008.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0008.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0009.json b/corpus/conflict-page/native-pages/b/command-0009.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0009.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/command-0010.json b/corpus/conflict-page/native-pages/b/command-0010.json new file mode 100644 index 0000000000000000000000000000000000000000..7adf980027ef43cc77767d4fd1b1d67ad1541ab4 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/command-0010.json @@ -0,0 +1 @@ +{"action": "close"} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/commands.jsonl b/corpus/conflict-page/native-pages/b/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..b01eb35abba6cfb0631130c1778f33d75ea7ae3e --- /dev/null +++ b/corpus/conflict-page/native-pages/b/commands.jsonl @@ -0,0 +1,12 @@ +{"command": "mkdir C:\\one-tests\\runs\\capture", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -ExecutionPolicy Bypass -File C:\\one-tests\\network.ps1 -LabMac 52:54:02:19:c7:6d", "exit": 0, "stdout": "{\"renewalResult\":0,\"releaseResult\":0,\"mac\":\"52:54:02:19:c7:6d\",\"address\":\"192.168.77.210\"}\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0001.tmp C:\\one-tests\\runs\\capture\\inbox\\0001.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0002.tmp C:\\one-tests\\runs\\capture\\inbox\\0002.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0003.tmp C:\\one-tests\\runs\\capture\\inbox\\0003.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0004.tmp C:\\one-tests\\runs\\capture\\inbox\\0004.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0005.tmp C:\\one-tests\\runs\\capture\\inbox\\0005.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0006.tmp C:\\one-tests\\runs\\capture\\inbox\\0006.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0007.tmp C:\\one-tests\\runs\\capture\\inbox\\0007.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0008.tmp C:\\one-tests\\runs\\capture\\inbox\\0008.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0009.tmp C:\\one-tests\\runs\\capture\\inbox\\0009.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0010.tmp C:\\one-tests\\runs\\capture\\inbox\\0010.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} diff --git a/corpus/conflict-page/native-pages/b/hierarchy-0001.xml b/corpus/conflict-page/native-pages/b/hierarchy-0001.xml new file mode 100644 index 0000000000000000000000000000000000000000..109b3296412c627c6a42b9c2d44d5ce4dcdc8709 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/hierarchy-0001.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-pages/b/hierarchy-0005.xml b/corpus/conflict-page/native-pages/b/hierarchy-0005.xml new file mode 100644 index 0000000000000000000000000000000000000000..633860ac88594b8ebd31efd814b5032cda6b6ff7 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/hierarchy-0005.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-pages/b/hierarchy-0009.xml b/corpus/conflict-page/native-pages/b/hierarchy-0009.xml new file mode 100644 index 0000000000000000000000000000000000000000..95e5b0fbf2cd7769d2f9edc187df53611350f2f2 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/hierarchy-0009.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-pages/b/listed.json b/corpus/conflict-page/native-pages/b/listed.json new file mode 100644 index 0000000000000000000000000000000000000000..67a675a4f64d48e4266aa24db7ca7731d86ad8c6 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/listed.json @@ -0,0 +1 @@ +["Fictitious: café, 東京, مرحبا", "One", "Two", "Four", "Three", "Target"] \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/machine.json b/corpus/conflict-page/native-pages/b/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..4c203c09d03f32d2e43453d8cd7f643f38ad7c96 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/machine.json @@ -0,0 +1 @@ +{"name": "m6-39da335d", "hostname": "ONE-M6-39DA335D"} diff --git a/corpus/conflict-page/native-pages/b/teardown.json b/corpus/conflict-page/native-pages/b/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native-pages/clients.json b/corpus/conflict-page/native-pages/clients.json new file mode 100644 index 0000000000000000000000000000000000000000..d931d1ff7f174fc34ff42841be4fca8f0991ad66 --- /dev/null +++ b/corpus/conflict-page/native-pages/clients.json @@ -0,0 +1,4 @@ +{ + "a": "m6-327953db", + "b": "m6-39da335d" +} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/initial/notebook/Open Notebook.onetoc2 b/corpus/conflict-page/native-pages/initial/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..c7beb44bf509f935090fe55e7549e4b50e0ae0e9 GIT binary patch literal 4944 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1Ryp$OaMy5#LCc z>i*FX7!e_WTxWXkGHyC7dZ-(g??D;-@>He(5T6N%1%VhO2Eyr`vwT5(khl<33?y!o zd~Y#`&jl4T%@#=p@nLGF>Uu2&v1Oq0+P#HFAig@3Z=rk(#2f{qAut*O0~7+V`X7|P zL4XHGB`+YuFm(x|3PjN`C9#!{u=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jg zw6K&`@AZW{cSu}5J`to<*Mp(=1S2D?1O}GZEh!8=$Q}Wi2mSTaM{uf|q z0Lu_fMDRhCvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf z@C)YhkE)-W&nNc#?f3rgAsKx46v#+yz7a6kVDJ?)m4TH1YCI}7!z|djE2r;@ntiZ5 z%;i8S1`r#h22|n0d||mS?(Mx3!CpNK%InW8|6v4@2l*n7i3j9n5Etr;D4;KZI`R7g ztbCLh79oIK+`!5OV0r+THca3$;hPMD3aDIwq!Os78(0{o$TG<61eO`uKpIqbfLfn6 z4(-7U+CRCiF{?6ukiPFKNDrt5SrwJk;;EAU}K}DpA4Ul33ja_3y!GZ=9!Cp>-ioL{|ScqLT_7;1O zHMZmrSQ1T4v5Uc2V)@^q zSDbaOR@m6)S!3@-uMQeq{c`u+-!KAK_tGc!rKjC*u=I4PX}6B7$*Dx)#>7W|P7dd_ zcn|%N_vgKJ9hUk$oa7Omy*@avUD>Lg%Q;8X4$`E>I>$8}9gz zu}X8HJ+Xh`wrv$QRr*=?$5pg+ej z`cuWyRHH#1Ww(Wah!zy8@T*EZSSUXy&|)@(}&1zUC2>BRhz zWRrz9zm7|SYus*i?V6d-OMNrJ9Z9CTGc%p!TEs}V)#}PV?|&?Dl-z2}-U{8ke(sJK z2s&r4?U>!??&MJit<|T4G6!BvDBZ31bU^zANf1${SZH)(V<$nwk0Y>g#2BIkT1dq)GE6Y z;dc7#(9Z_7#5M9pxYL-c!%+`}yAmmh;ICEmUg<_hI9xwieBn5*aSpn->8m7MqdvIq za`fs&T=Vsv^sR>!<)Ce)iTLtQ^|tQiPS+?0<$@0#6kXL^nUO^u%yYah_BHT zU(d*84c8+c-@Y*=W=G?iZ(rk?lZPUmmZ&Td-xtP8yPbi*H<1?+&QZ>UnDN6*v-XM-I5&Tt|BR@u-xYyth?kW7gnrc_Cy#5^R;Q7+9#mqlaQ z0@pm>rRC;maV{mQNQ5+?^_6dBLHETu{CR$=;;oy*ALaQFp*-+_FV7_>&|8N z2M(Qu96$f#9>b|kO!D?LlP{6r?)`V2n0(~KV)JI$iLYU_foz7vvXxj&^1MBas zFye^Xrqz@KE78W>e43Qdt!3QCIfuvmG}d|w0y7eriN+OTGnu|Ez@Z)0 zptWnoI<9xJ8f~A`a9Fvqes>`e(JREll;n>L-R*Tc)G&WSlI)xE+hy(<0Yd_hwOfWT zv08IAd~VO^C<5!U_)bE7ho3(vKxe`DxijvG?u-k1?dNsg-?`%&kdMxi8Rfw^O$cC|cDpl97>S_qE3VQn&u_?1xJ}d_#n|ITt*So7Qv=(i-3IL(kSlyAEs`H@<48 z<|p%Z=c6AWj?e;uKWb#9%K!3CsUtG00r#AO)1OR+Kju19{btQI-SKk2cr}=~#nl^oy0JVHkp}SRN|IP<-G*K*bGqe1yHd^> z`CI&M97mvNNsvu^NsS}43v$(6Y%AVg6d5$n@mnMkE8S4ich=hX@7L?^>R-pA&Nq#= ztA{+06|1D|-*fJ-y#2tg?8FUM+!r^zg+yYd8%jE3qs@z}YBzA}Pb$Uj5;NWR4DdBs z($wVkTE0!=lBPxW8`x4VuROW@f&u&5Ady(>^3c~yWieq zN$a{-8H182Q?q+VIZ)L2ht3QK{Y)t5(5axGoG+G=tX^ZMC{ z1`d6Dl~zQ|n09o6)v3nqFbg3O^fO@?W+H_1a%MpGd9TLd&m`qK3>#MR#s{nzq~Jd} zu7<|?S$!B>a1_Mr3v{J}7de^>`MH{(i4F5FY%w^@zhccbo_~cc;PtH`leR0To_HKJ zWz{;@-H+X_qs5pvpITU=k=F)^yVHaE4h!#E?HoR|f=5 zv>%hqq%Z^M40pLX5%Dq%I10Hi@D=Kq1yhD}zaark0PDjB(vMDm@|Wmoz$D<%Pwqdt zaV1{clCdaJbHL#%1G}IW#{J~rmfG+6EgB>ZSvY@d)63JZLhnKxQ~hVy`Yk0r_Ro&e zmFx4%(FU#RBQE++O-m*8b%Wj&YAFHs5lI~26!<_(Yp^Z2KoQegI)GcMzodt~NDiUi zkQ`B#S}_)hYEMn`I)4u7AmkZuy@`bxTYbWG;1Bu^q)L0rS4^s)o*njz%S5sJ1V6~o zR zyew?5xb0W&iJnEN+8sMek81J#ur>~e5+3negCzMQ|O?bYZTf_h$U7T_;UrP$+T+wS#53Zt<#(HK8xMTalU6|V5Swy zv_(8izTgDcTs_xxBpSFAFIg8Qho-+Fc2Fv|IBMtNk&h_I_YYap;- zs?AEED>xcp{5*>Y1w1qQjOF|5+Tpz35b)$?AFFEK^*4@b=6SnSwcg+(#_38t}&% zc$c=emfy*`nK-e-uOYG#!94;TSHe40sw{o!cdwoOzLo9yeZ!r=fWD*QJya??`Pm8Y z+C6iB{9?kbCC(1}ZovB^sZ!Bq)u34mM|S(E`%-V6UyEVzPEf0xj$RVAZ@}EVIgLH$ zRUdk|0lcT!Ic%!9=A_?_lB*Ne?W$X6T3L5^XW7Y;Y#KeBdV9gHiQ%2M<(F^BHfOn= z{Jo%-Eg##J&Y1Pos__p#nKQVynt#n4c$YX}YCe=R&Yp}sJAjcp(Xl4ULWu{Xme92| zk(5ZG7t$=Zbhb=8lATs$4_gySiPV%vN%CGran%{aIgI_O_PW`5(2?{@NgU7_Z-yUOoDz`@jKlnITc?w5~3lF&iArU?m`?B?%se zTL@(#B9P?792AQP5}^!51d_OzptuM^*$Wk_tCb3~o-NIL67~s2dn*~HTN~D+K%)gG z7)DcxJ2J#BUavLg52a)EvZhvQs3huwOarXkd25?1rM#7$!D9dG6g7Qy%?C3F%p7=q z4v5KljQIkhlfTBU4WUxR?AlO9L$+=_(~Y@CD`+%K-|nm9aEy{xlU9~`OW&gh_Qkbu zIsWkS3~OWHAn(NQQz1?ow2L5?nm#{6JkK<_qYn!7SV>`G6 z;gF{7VB~MGgJ~@uYX>`08YdzgOA^To*~DI?!N6ysi)|t?)p(peDHS}d;9lK22AVxy0*#LHhvkEbkV}sKvjq_u5b>sYm zc@^mFuTT%99b-#HHz&3HL;6i8K$3O5+{JoUILKWwpx#*S7FW-P{!LwyS>86c^{N~7 zRf^IHDRX1bEadg?kb4tY|Le@@)V!C} zcTjgoL{{Iq5^<4E1(<)T`!8YV9`1&|6SgY5QW(YH^c_0lc_5x5nbvoZKU)S0VQ?St zAQ7lZ(YQa-D6H@vhB5A+(hpAnk`og&Dd}mwdnPBoCjQUQn;JE>H`amX_$>L;!nXH=3LruKLf(?@^XT`=16{BPCF{_WBB<%1jqPp&k<`2UC zo)AHA8tuWzAJMZO{+SJ)ekh8D4nA$mR{G5%)c?{4jr;EKkd@W$ z9z3^3^)I{Rw6zX8KR@^^MPY1p^g~HCbg-W3KR>(KqHvZRz8j@jW8Pctj4eKUXFAsciH|*A{P{8b)!Fm#<1~ zJ7&NylPdMf%&2(Ken;(Uo5o+b^hf6bnP107DZ59?HhV@WF64O}YVJAr!R)g2t7k=R z)b;Dzt4)9FI!BuJ#|X=}*q0j}v^y$y#PU@m_V||Q+wD*tULyntKng1j{l3U)UKD}C zQ6lVjG6wu2jUi}Q%lP}p}6q%JT z7!X+h4Y~d1Wj5+XTp;&P%WTx)7v~>$C01~VT5@nwtZMLr)sgTNxN%M6Vr-OdV24eE z`jO4(h#Mcx@ld{KiC3`u43s701)H?mR_=(i3tgkZ=Z`m0WF z2i0KwnGkvbFMx?+0`+GR6b8Qt`VFK7h?Oa35FP!A>e2c`e@f>^@#`_e7^(#=!RbSJ zcW3ZAQe5lAQak1Z)}Q#L#PgB;5}-b;nfHs#Q%&sTraqW?=V@}JFvIheb2NRD2lvnh zG3Ll>LMS>EHCs}+*_k=;4sk$CKGGm8u&da)nnv22pR4=PT#e!W&(GB)RCFp5+NxsX z$rngJet8`zJ6jrt_Oj&>Pm8yH3w(muGp%d!>$g^AUng5GJB+es=zlH53bh6VV?v6h$M) zi}(Eo56-89q+hd`2xiS!?$_uZPlhttCgxVFCMhMtokpmRdsUbonA0+R(HV;img6$| zX!6c}`cs!ZqhwQtk6G}rvHOjER<@(BTd$36*j!a%dWERY&nKqz-|>7-Zuwq=`z)^d zWX_2bL#n3?|Lwa^tsho>9%^YZMVB|Q@w~j$N7nNeF8m_t{ilbzNP>g+giVj^vGszN zo9hL6+~|rcu6sHumGkCJ%xdFX@AFm@Znj?4a%!S$dW=In)eoqxVoa!!S^ty|YUl9$)r z|M{R;^#OT)dWY_h(|U)vPd}Ov@%ZWP+}=S``s97Ne*QWSzm)hkpAI>#xRg11OjkG8 zNzncfHE&MyCU8QonHT7}em{{ySoh z9E|yT_>rz&0jeISN2^X%3+t}PA6&c5=dD~OE=#P^phUvPwr8)UozHnXX4~&;OCMuu zm+$oO(;t7I^?2XD4?=1UmE9lgQEBG*nXW5ajIZ*$d!Fv6NgGv@Mm~visIYO+P3C9U zokP>gI7JPb(r=-C_u=zT`!sF&&7!Yo{n~s;-wi9fUs!&wXNJqEkvkGD=T25<4z$ft z&za}D&UO7WOV=Oj?g|c1_qJ@4^+S!jt^F=9neHgNxiUC6C*kloNwqWTyLT=$C_lpe z;l{%YljcaqC1gp*#LsTtWXpPoiNoLP|Epu=@pCg0CBr7(^u0X)*o0@DyM=7lJj|#t zSMt%y{=M`6aP3mZ(|5?RunHd^blH2#??K?i&@W2Ko;1vKof;@*Z!w<1FF%!bg`w=T@8ZC7(q!(}rb*^Gl- zfmzX%olxS;{BF6~>l#no-eIqL`k3!f=9{z=?#5Aua8wDQJplfoJk}nd$_JUU2Lf2# z-;YFMHPZ2+`}>iNP>pnaSO$S~7ENa%bgV~KL1oGaHb4mdhEPa7iXA~VLX84@fD`s8 z7E_Z+Go(5h6VdcIFNlhOF;bZd52QlF*<0VevD_BAlfmPn+*3_VEfTE*@fq#wz zV)g*mGr+hm!0xD{E2%5Ld7B`ObVn+U+`j?8BUz_YQ9FyLr=#wWj;x*@7$fq`h1b(5 zThh~&;lxIRo=)YM*3*$Un?DL*?FXBJdbK8uz70`F4<@r4>EXkSba4&6qruk;vyg(w zVol7rx^ptJ>cYc<73j#7&6$qmC_raR`8yL?6xc+_2T8c{9SZ7;GK}9pnMUg{_%c~u ze3&fCi^9*FN*3fN_MQpyWAkKdiTV}qc@xqotkCMliQUpPT@!n=FG>m8OP4y=g7RP3 z9#Om-d@;VC9tHWm`SxHeHtX5Fhp7hf_@);sMGpFm*js^rr#|yTz03oR9_+HxX0P~d zXZG)>pyyzuo6>WxU-)d($OoTCOk7&V_t}}TyK&1f@g7qu!CJ#(vI9yP?ww&K-mX-j z<7(Ntn%PV5kh6}wg!&Bl!{@tBsYDkvP;mL8D)W?97=`5ul!B>B zHD-9P0*`S@8RUl{k6}tR1gAkM=%_5CCN`Lr0{vYwAIuzhqZ|;^XCUuwXw-4@`b*EB z>#9yDdj3TIz@)6%xs!hnvn0I>8AtQ<#An#of^(2Q+53;~N z3geR-66o{eJOX-hoTFc^`8@N$H?$XfZLmLvU-;{<7>;>7?^)(iF!xV!*oa(gqf81; z*@=wcR`k1$PS0-4?f0bqlnHlAI6u5Dmy@5L-yZ3G+K~?Dxn*jGE6EVo6vL;270+YXOJ52P`}Q4FVb)6F(v7f_CI$F@AYvRRKwWe zzLX@7;ytg(Aj}@mE6_gNSa8xR=R*`CSQEzECnz?e|G+Tl%~2_qtewKze{}6kWZF(a z8EiRh527O2b@29dV#VG!{(W$~3yydSD?~(lT<9n+FAZZluP!g=QR>we zzs|JaQV_MMy`%)UoEh3A$j7sgiN zU-P50WRx^7scFu#Awun@B#m#ow^>%BkJqjZOmkQokGkq;yI!=l><>SLr!TC#`LflV z$mO>W*z9OG6$5|?Aqjx+35R|NC$%|(Bi_C#hbZJ=--vBKVr<`>-(I6^Mu#1es_!pb z?S0g8<*TmW5W{h)lA<^+>>Dw=2aje<**zP({G6XMWo!MpEwg8@X*Erb9uH%qx>|!S zA4BYrNQ?mAtRCbGN0kuTHyEqnUWV9yIv^OeakzXTGckpWpm`Rr+0jEqHX4vd~`Y@P!m``a$}epV$0)tD8SK-|_IXNc(*~dZIpjYJHn%EQ_aaqu5*H z+i9PNZTc;C%jXY!F1@=vO)Nd>O5*8DHC6TS$z9lZ%kkdM*Cx%ZemQcuQF^L2yN^7c zgJC7Anh2Yb*b)_gZ+Xha$Wc=EK8e?7kyn=q6=pazT>4#ZLg)gYMSFe58`X!G^ICm= z(1XRc={DAbh2MDM^xz#_6TT@06aK0)Lo3!tY_>y&I^*D^e&Bjw&J4gTWv0 z84HR(-LP`2Yg}f7-JvH>j`f!Hg+Hcn!uaA#? z;Dz#oMkBiq{9IdJii%|ZAMh%QKNa-tbD}+u#rT6@>|uQNgEB{B26VQtUl4iZv(xRm zfp=mK^8mNO?><}Y%o~^1`_8zpa2mP(+_QGyJw>7nv9E5tiGAQzV-iPwF#mx?MZcd8 z`88{Es^}UT!6?ieEc}Htnm_1D2QP9o7nq8i#Od>U{ zSs6=5tSLW2@0IfVgr2E!ov>l(4RDrF2>e6+{WRgR;0xASV2=|J_YE`bH7~Z#VuQYn z0NMivd4dcd6?8)5dc*eS zoJ2`F2jDq)&9BivM|^;G$36?7BRRpd0B%2682c=S zr~~*2IZ=2I8~%Rr`EH_exO<0A+ax&=ZRe@beS@^+@-o$aJ}<*Pql|PKO2@sMAY7+| zHb2t07iqB$=_dI-rz2lKv>Vl7+a0+3I-QEPn|(EyZ9B!l(9;2u02;!*-|j^DVsBkO z-#j`;eULv=z^U2QR@^ywZQvuuL(%Wab3DZJmxz>vK^kJD*emhI{mGZ&ntm>W{0j3C z_F3`PwH4wSr6MQb8UNgf*i@JLO&ItEnFyfAutu>&+0T3 z*o28=7a+&d^A)^;=ueMSYtVz$>V!^3P&lj${v3JYDIR%oj%*cE-|s@YChWT+oDExi zYnWOPhVex%46P~7_bFa|KTrs%ru2SFcX~S|l8$(;5Y_$B;uHwhX+1Y)aZ~M8@KC^f#W~9 z({&!=VjiAsCjkfvAd{Pi$Jq%!pE2A{W!OdtNfPYbaCZ&pXd62zNaCkTZ-K$_?exV1MfNqin8Y*Q!q{rX0W>* zL)~Eaov{?|c8mzLUovz%Zof9M{gP=*cPQHI)0IDM?hZLu$4-Hn-k0JGtu2&l zL!PvAJsx>+Efelxk38?%DmWFIulP^4D$J5x7Ut(f#0swKD z-#vU_j{*4Kbw%-~lD^r%yTtf|VBlSmX9snPKD5nB{(a)EA1gd!KQ1N4G3Yp)wpGSK z`!M&8YpxA==j2$PHTKjX-8ZQjTOqw zscfZR3Gd?HzlHuS)?8!#+mxO?q{^`1L6xkc8X3O$$S>QI{Td}CQH zr?T(HV5xR>^sGgrdVKjJyyuTc7DJADe;7A=@Rlp(2JLT${4fvQbP(V@zc>i+o{J6w zyl20I0Por2Ai#UpItcKdr4Gf#dkVgzMN_#D?}0vpdIRsd<)9T~k*Eg3<2^#28RI>| zFk`$&7$(Gf#IzIC6L^o9ZiO(Rz5qY7Gjrfw=Ri^R4CEum34j#ZXZWwid*(R^@SaHy z0=#Fmg8=Ut>hQ97PqKpm?@4qJ;61G!UKa0(a}eM?^&HH2&$~Xb-jengHU2B>+W@>r zxXu!C0eDZKJ)Cwb3ed{ch1%0D*Yq$%CQfcJE8)Vd9f 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 U;AJSP$(SHug1~zW0kQG_KP_8;V*mgE literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-pages/native_conflict.py b/corpus/conflict-page/native-pages/native_conflict.py new file mode 100644 index 0000000000000000000000000000000000000000..ccb4026acf69f3df84fdfbb01e9b6e39461762d3 --- /dev/null +++ b/corpus/conflict-page/native-pages/native_conflict.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +"""OneNote 2010 conflict pages: how they arise, what they store and how OneNote shows them. + +`sync LINUX OUTPUT` runs two disposable OneNote clients on one Samba notebook: client A edits +two paragraphs offline while client B edits one of them and a table cell online; A then +reconnects, which makes OneNote write a conflict page. The server file is kept before and +after, A and B are screenshotted with the information bar, the page list shown, the conflict +page open and its menu open, and A then deletes the conflict page through that menu. + +`pages LINUX OUTPUT` runs the same two clients on page-list changes: client A, offline, moves +a page that client B also moves, moves another page, and edits a page B deletes; A then +reconnects. The server's page order and pages, with the recycle bin, are kept at each step. + +`restore LINUX OUTPUT` runs them on a page in the middle of the list: client A, offline, edits +it and moves a page B leaves alone while B deletes it; A then reconnects. + +`cold NOTEBOOK OUTPUT [--delete]` cold-opens a notebook holding a conflict page in a fresh +clone and screenshots the same views; `--delete` deletes the conflict page through the menu +and captures the notebook OneNote leaves. +""" +import argparse +from contextlib import ExitStack +from concurrent.futures import ThreadPoolExecutor +import base64 +import json +from pathlib import Path +import shutil +import subprocess +import sys +import time +import xml.etree.ElementTree as ET + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +sys.path.insert(0, str(Path(__file__).resolve().parent / 'w7')) +import native_runner as runner +from native_runner import ROOT, clone, command, windows, vm +import linux_vm + +FIXTURE = ROOT / 'corpus/native-ink/cold-ui-ink/notebook' +SHARE = 'm6-conflict' +ORIGINAL = 'Fictitious: café, 東京, مرحبا' +OTHER_OUTLINE = 'Fictitious positioned outline.' +CELL = 'Left cell' + +# Coordinates on OneNote 2010 maximized at 800x600, from the stage-5, m8 and sync-01 captures: +# the information bar's text, and the first conflict page listed under its page. +BAR = (332, 100) +FIRST_CONFLICT = (725, 142) +# Delete Conflict Page, first in the menu the bar opens on a conflict page. +DELETE = (420, 113) + + +def shot(name, output, label, script): + """Runs an AutoHotkey step on the clone, keeping its screenshot as `label.png`.""" + (output / f'{label}.ahk').write_text(script) + result = windows.do_exec(script, target=name, timeout_ms=120000, shot_delay_ms=1500) + screenshot = result.pop('png_b64', None) + if screenshot: + (output / f'{label}.png').write_bytes(base64.b64decode(screenshot)) + (output / f'{label}.json').write_text(json.dumps(result, indent=2)) + if result.get('error') or result.get('exit') != 0: + raise RuntimeError(f'{label}: {result}') + return result + + +def click(x, y): + return ('WinActivate("ahk_exe ONENOTE.EXE")\nCoordMode("Mouse", "Screen")\n' + f'Click({x}, {y})\nSleep(1200)\n') + + +# Reports any dialog OneNote raised, so a confirmation shows in the step's record. +DIALOG = ('if WinExist("ahk_class #32770 ahk_exe ONENOTE.EXE") {\n' + ' FileAppend("dialog: " WinGetTitle() " | " WinGetText() "`n", "*", "UTF-8")\n' + '}\n') + + +def conflict_views(name, output, page, prefix, delete=False): + """Screenshots a page with a conflict page: the bar, the list shown, the conflict page and + its menu; `delete` then chooses Delete Conflict Page.""" + shot(name, output, f'{prefix}-main', runner.navigation_script(page) + 'Sleep(2000)\n') + shot(name, output, f'{prefix}-shown', click(*BAR)) + shot(name, output, f'{prefix}-conflict', click(*FIRST_CONFLICT)) + shot(name, output, f'{prefix}-menu', click(*BAR)) + if delete: + shot(name, output, f'{prefix}-delete', click(*DELETE) + DIALOG) + else: + shot(name, output, f'{prefix}-closed', 'WinActivate("ahk_exe ONENOTE.EXE")\nSend("{Esc}")\nSleep(800)\n') + + +def cold(notebook, output, delete): + def interaction(name, output): + page = ET.parse(sorted((output / 'read').glob('page-*.xml'))[0]).getroot().attrib['ID'] + conflict_views(name, output, page, 'cold', delete) + if delete: + # OneNote writes the deletion in the background; give it time before the re-read. + time.sleep(20) + runner.capture(notebook, output, collect_notebook=True, interaction=interaction) + + +class Lab: + """Two OneNote clients on one Samba notebook: `action` runs a controller command, + `state` copies the share and exports its section, `settle` syncs until a check holds.""" + + +def page_list(model): + """The section's pages in order, each as its sorted texts.""" + from document_model import ordered_pages, walk + return [sorted(node['kind']['text'] for _, node in walk(page, oid) if node['kind']['type'] == 'RichText') + for _, _, page, oid in ordered_pages(model)] + + +def edits(lab): + from native_collaboration import reachable_page_text + a, b, output, action, settle = lab.a, lab.b, lab.output, lab.action, lab.settle + state = lambda label: reachable_page_text(lab.state(label)) + texts = lambda state: {text for values in state.values() for text in values} + state('initial') + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': False}) + try: + action(a, 'edit', expected=ORIGINAL, text='Client A conflicting edit.') + action(a, 'edit', expected=OTHER_OUTLINE, text='Client A disjoint edit.') + action(a, 'snapshot') + action(b, 'edit', expected=ORIGINAL, text='Client B conflicting edit.') + action(b, 'edit', expected=CELL, text='Client B disjoint cell.') + settle(b, lambda label: {'Client B conflicting edit.', 'Client B disjoint cell.'} <= texts(state(label)), 'b-published') + finally: + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': True}) + settle(a, lambda label: len(found := state(label)) > 1 and 'Client A disjoint edit.' in texts(found), 'conflict') + (output / 'conflict.json').write_text(json.dumps(state('conflict'), indent=2, ensure_ascii=False)) + page = action(a, 'snapshot')[0].attrib['ID'] + conflict_views(a['name'], output / 'a', page, 'a') + action(b, 'sync') + time.sleep(5) + page_b = action(b, 'snapshot')[0].attrib['ID'] + conflict_views(b['name'], output / 'b', page_b, 'b') + conflict_views(a['name'], output / 'a', page, 'a-again', delete=True) + settle(a, lambda label: len(state(label)) == 1, 'deleted') + action(b, 'sync') + time.sleep(5) + shot(b['name'], output / 'b', 'b-after-delete', runner.navigation_script(page_b) + 'Sleep(2000)\n') + + +PAGES = ['One', 'Two', 'Three', 'Four', 'Target'] + + +def titled(model, titles): + """The section's pages in order, each named by the title among `titles` it holds.""" + return [next((text for text in texts if text in titles), '') for texts in page_list(model)] + + +def offline(lab, titles, a_actions, b_actions): + """Creates the titled pages; A runs its actions offline while B runs and publishes its + own; A then reconnects. Keeps the merged order and pages, and what each client lists.""" + a, b, output, action = lab.a, lab.b, lab.output, lab.action + action(a, 'pages', titles=titles) + lab.settle(a, lambda label: 'Body Target.' in str(page_list(lab.state(label))), 'initial') + deadline = time.monotonic() + 300 + while not any(node.get('name') == 'Target' for node in action(b, 'snapshot')): + action(b, 'sync') + if time.monotonic() > deadline: raise TimeoutError('B never listed the new pages') + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': False}) + try: + for kind, parameters in a_actions: + action(a, kind, **parameters) + action(a, 'snapshot') + for kind, parameters in b_actions: + action(b, kind, **parameters) + action(b, 'snapshot') + lab.settle(b, lambda label: 'Body Target.' not in str(page_list(lab.state(label))), 'b-published') + finally: + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': True}) + # A's changes reach the server when its sync writes; wait until the file stops changing. + previous, steady = None, 0 + deadline = time.monotonic() + 300 + while steady < 3 and time.monotonic() < deadline: + action(a, 'sync') + time.sleep(10) + current = page_list(lab.state('merged')) + steady = steady + 1 if current == previous else 0 + previous = current + (output / 'merged.json').write_text(json.dumps({'order': titled(lab.state('merged'), titles), 'pages': previous}, indent=2, ensure_ascii=False)) + for client, label in ((a, 'a'), (b, 'b')): + action(client, 'sync') + time.sleep(5) + listed = action(client, 'snapshot') + (output / label / 'listed.json').write_text(json.dumps([node.get('name') for node in listed], ensure_ascii=False)) + if listed: + shot(client['name'], output / label, f'{label}-merged', runner.navigation_script(listed[0].get('ID')) + 'Sleep(2000)\n') + + +def pages(lab): + """Competing page moves, and an offline edit of a page the other client deletes.""" + offline(lab, PAGES, [ + ('move', {'page': 'Four', 'before': 'One'}), + ('move', {'page': 'Two', 'before': None}), + ('edit', {'expected': 'Body Target.', 'text': 'Body Target edited offline.'}), + ], [ + ('move', {'page': 'Four', 'before': None}), + ('move', {'page': 'Three', 'before': 'One'}), + ('delete-page', {'page': 'Target'}), + ]) + + +def restore(lab): + """An offline edit of a page in the middle of the list the other client deletes, with an + offline move of a page the other client leaves alone.""" + offline(lab, ['One', 'Two', 'Target', 'Three', 'Four'], [ + ('edit', {'expected': 'Body Target.', 'text': 'Body Target edited offline.'}), + ('move', {'page': 'Four', 'before': 'One'}), + ], [ + ('delete-page', {'page': 'Target'}), + ]) + + +def sync(server, output, scenario): + from document_model import EXPORTER + + if linux_vm.instance_path(server).exists(): + raise ValueError('Choose a new Linux VM name; existing machines are not owned by this run.') + output = output.resolve() + output.mkdir(parents=True, exist_ok=False) + scripts = output / 'scripts' + scripts.mkdir() + for name in ('cold.ps1', 'collaborate.ps1', 'network.ps1', 'text.ps1', 'stress.ps1'): + shutil.copyfile(ROOT / 'tools/native' / name, scripts / name) + shutil.copyfile(Path(__file__), output / 'native_conflict.py') + + def ssh(text, timeout=90): + result = linux_vm.run_ssh(server, text, timeout=timeout) + with (output / 'server.jsonl').open('a') as log: + log.write(json.dumps({'command': text, 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr}) + '\n') + result.check_returncode() + return result.stdout + + def server_state(label): + """Copies the server's notebook, recycle bin included, until its section parses, + keeping it as `label/notebook`; returns the section's exported model.""" + deadline = time.monotonic() + 120 + while True: + target = output / label + shutil.rmtree(target, ignore_errors=True) + (target / 'notebook').mkdir(parents=True) + archive = subprocess.run(linux_vm.ssh_argv(server, f"tar cf - -C '/srv/agent/{SHARE}' ."), capture_output=True, check=True, timeout=60).stdout + subprocess.run(['tar', 'xf', '-', '-C', target / 'notebook'], input=archive, check=True) + result = subprocess.run([EXPORTER, target / 'notebook/synthetic.one', target / 'model'], capture_output=True, text=True) + if result.returncode == 0: + return json.loads((target / 'model/document.json').read_text()) + if time.monotonic() > deadline: + raise RuntimeError(f'{label}: {result.stderr}') + time.sleep(1) + + linux_vm.create_instance(server) + try: + linux_vm.launch(server) + linux_vm.wait_instance(server, 600) + (output / 'linux.json').write_text(json.dumps(linux_vm.load_instance(server), indent=2)) + ssh(f'mkdir /srv/agent/{SHARE}') + for name in ('synthetic.one', 'Open Notebook.onetoc2'): + with (FIXTURE / name).open('rb') as stream: + subprocess.run(linux_vm.ssh_argv(server, f"cat > '/srv/agent/{SHARE}/{name}'"), stdin=stream, check=True) + ssh(f'chmod u+w /srv/agent/{SHARE}/*') + with ExitStack() as stack: + labels = ['a', 'b'] + for label in labels: + (output / label).mkdir() + + def start(label): + manager = clone(output / label) + name = manager.__enter__() + return label, name, manager + with ThreadPoolExecutor(max_workers=2) as pool: + started = list(pool.map(start, labels)) + for _, _, manager in started: + stack.push(manager) + clients = {} + for label, name, _ in started: + folder = output / label + for local in scripts.iterdir(): + remote = 'cold-current.ps1' if local.name == 'cold.ps1' else local.name + result = windows.do_put(local, 'C:\\one-tests\\' + remote, name) + if result.get('error'): raise RuntimeError(result['error']) + command(name, 'mkdir C:\\one-tests\\runs\\capture', folder) + command(name, 'powershell -NoProfile -ExecutionPolicy Bypass -File C:\\one-tests\\network.ps1 -LabMac ' + vm.lab_mac(name), folder) + result = windows.do_spawn('cmd /c powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\collaborate.ps1 -Root C:\\one-tests\\runs\\capture -SharedPath \\\\192.168.77.1\\agent\\' + SHARE + ' -CloneHost ONE-' + name.upper() + ' > C:\\one-tests\\runs\\capture\\controller.log 2>&1', name) + if result.get('error'): raise RuntimeError(result['error']) + deadline = time.monotonic() + 180 + while windows.do_cmd('if exist C:\\one-tests\\runs\\capture\\ready (echo ready)', target=name).get('stdout', '').find('ready') < 0: + if time.monotonic() > deadline: raise RuntimeError('The collaboration controller did not open the notebook') + time.sleep(1) + clients[label] = {'name': name, 'folder': folder, 'sequence': 0} + (output / 'clients.json').write_text(json.dumps({label: client['name'] for label, client in clients.items()}, indent=2)) + + def action(client, kind, **parameters): + client['sequence'] += 1 + sequence = client['sequence'] + local = client['folder'] / f'command-{sequence:04}.json' + local.write_text(json.dumps({'action': kind, **parameters}, ensure_ascii=False)) + inbox = f'C:\\one-tests\\runs\\capture\\inbox\\{sequence:04}' + result = windows.do_put(local, inbox + '.tmp', client['name']) + if result.get('error'): raise RuntimeError(result['error']) + command(client['name'], f'move {inbox}.tmp {inbox}.json', client['folder']) + remote = f'C:\\one-tests\\runs\\capture\\outbox\\{sequence}' + deadline = time.monotonic() + 600 + while True: + result = windows.do_cmd(f'if exist {remote}\\error (type {remote}\\error & exit /b 1) else (if exist {remote}\\done (echo complete))', target=client['name']) + if result.get('exit') != 0: raise RuntimeError(str(result)) + if 'complete' in result.get('stdout', ''): break + if time.monotonic() > deadline: raise TimeoutError(f'{kind} did not complete') + time.sleep(.5) + if kind != 'snapshot': + return None + hierarchy = client['folder'] / f'hierarchy-{sequence:04}.xml' + windows.do_get(remote + '\\hierarchy.xml', hierarchy, client['name']) + pages = [node for node in ET.fromstring(hierarchy.read_text(encoding='utf-8-sig').strip()).iter() if node.tag.endswith('}Page')] + for i in range(len(pages)): + windows.do_get(remote + f'\\page-{i}.xml', client['folder'] / f'snapshot-{sequence:04}-{i}.xml', client['name']) + return pages + + def settle(client, check, label): + deadline = time.monotonic() + 300 + while True: + action(client, 'sync') + if check(label): return + if time.monotonic() > deadline: raise TimeoutError(label) + time.sleep(2) + + lab = Lab() + lab.a, lab.b, lab.output = clients['a'], clients['b'], output + lab.action, lab.state, lab.settle = action, server_state, settle + scenario(lab) + + for client in clients.values(): + try: + action(client, 'close') + except Exception as error: + (client['folder'] / 'close-failure.txt').write_text(str(error)) + server_state('final') + finally: + if linux_vm.running(server): + linux_vm.shutdown(server, 120) + linux_vm.delete_instance(server) + (output / 'linux-teardown.json').write_text(json.dumps({'absent': not linux_vm.instance_path(server).exists()}) + '\n') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + commands = parser.add_subparsers(dest='mode', required=True) + scenarios = {'sync': edits, 'pages': pages, 'restore': restore} + for mode in scenarios: + lab = commands.add_parser(mode) + lab.add_argument('linux') + lab.add_argument('output', type=Path) + reopen = commands.add_parser('cold') + reopen.add_argument('notebook', type=Path) + reopen.add_argument('output', type=Path) + reopen.add_argument('--delete', action='store_true') + args = parser.parse_args() + if args.mode in scenarios: + sync(args.linux, args.output, scenarios[args.mode]) + else: + cold(args.notebook, args.output, args.delete) + print('Captured', args.output) diff --git a/corpus/conflict-page/native-pages/scripts/cold.ps1 b/corpus/conflict-page/native-pages/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/conflict-page/native-pages/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/conflict-page/native-pages/scripts/collaborate.ps1 b/corpus/conflict-page/native-pages/scripts/collaborate.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..6a42a3d526db486f022b032583704b0f4aee26d3 --- /dev/null +++ b/corpus/conflict-page/native-pages/scripts/collaborate.ps1 @@ -0,0 +1,145 @@ +param([Parameter(Mandatory=$true)][string]$Root, + [Parameter(Mandatory=$true)][string]$SharedPath, + [Parameter(Mandatory=$true)][string]$CloneHost, + [switch]$ResumeCache, + [int]$StartSequence = 1) +function Keep-Container($page, $target) { + $container = $target + while ($container.ParentNode -ne $page.DocumentElement) { $container = $container.ParentNode } + foreach ($sibling in @($page.DocumentElement.ChildNodes)) { + if ($sibling -ne $container -and $sibling.LocalName -in @('Outline', 'Title', 'Image', 'InkDrawing')) { + [void]$page.DocumentElement.RemoveChild($sibling) + } + } +} +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\text.ps1" +if ($SharedPath -notmatch '^\\\\192\.168\.77\.1\\agent\\m6-[a-z0-9-]+$') { throw 'Choose a disposable Linux lab notebook.' } +if ($env:COMPUTERNAME -ne $CloneHost) { throw 'The collaboration controller requires its owned clone.' } +if (-not $ResumeCache) { & "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost } +$shared = $SharedPath +if (-not (Test-Path "$shared\synthetic.one")) { throw 'Create the shared fixture first.' } +New-Item -ItemType Directory "$Root\inbox", "$Root\outbox" -Force | Out-Null +$user = 'HKCU:\Software\Microsoft\Office\Common\UserInfo' +New-Item $user -Force | Out-Null +New-ItemProperty $user -Name UserName -Value $CloneHost -PropertyType String -Force | Out-Null +New-ItemProperty $user -Name UserInitials -Value $CloneHost.Substring($CloneHost.Length - 3) -PropertyType String -Force | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy($shared, '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0) + [IO.File]::WriteAllText("$Root\ready", 'ready') + $deadline = [DateTime]::UtcNow.AddMinutes(30) + $sequence = $StartSequence + while ([DateTime]::UtcNow -lt $deadline) { + $name = '{0:d4}.json' -f $sequence + $input = "$Root\inbox\$name" + if (-not (Test-Path $input)) { Start-Sleep -Milliseconds 100; continue } + $command = Get-Content -Raw -Encoding UTF8 $input | ConvertFrom-Json + $output = "$Root\outbox\$sequence" + New-Item -ItemType Directory $output | Out-Null + try { + if ($command.action -in @('prepare-stress', 'stress')) { + . "$PSScriptRoot\stress.ps1" + Invoke-Stress $app $section $command $output $shared + } + elseif ($command.action -eq 'sync') { $app.SyncHierarchy($notebook) } + elseif ($command.action -in @('pages', 'move', 'delete-page')) { + $namespace = 'http://schemas.microsoft.com/office/onenote/2010/onenote' + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$tree = $hierarchy + $listed = @($tree.SelectNodes('//*[local-name()="Page"]')) + $named = { param($title) @($listed | Where-Object { $_.GetAttribute('name') -eq $title }) } + if ($command.action -eq 'pages') { + # New titled pages with one body paragraph each, appended in order. + foreach ($title in $command.titles) { + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $body = [Security.SecurityElement]::Escape('Body ' + $title + '.') + $name = [Security.SecurityElement]::Escape($title) + $xml = '' + $name + '' + $body + '' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + } + } elseif ($command.action -eq 'move') { + # Moves the page titled `page` before the one titled `before`, or last. + $moved = @(& $named $command.page) + if ($moved.Count -ne 1) { throw 'Expected one page to move.' } + $order = [Collections.ArrayList]@($listed | Where-Object { $_ -ne $moved[0] }) + $at = $order.Count + if ($command.before) { + $anchor = @(& $named $command.before) + if ($anchor.Count -ne 1) { throw 'Expected one anchor page.' } + $at = $order.IndexOf($anchor[0]) + } + $order.Insert($at, $moved[0]) + $xml = '' + foreach ($node in $order) { + $xml += '' + } + $app.UpdateHierarchy($xml + '', 1) + } else { + $doomed = @(& $named $command.page) + if ($doomed.Count -ne 1) { throw 'Expected one page to delete.' } + $app.DeleteHierarchy($doomed[0].GetAttribute('ID'), [DateTime]::MinValue, $false) + } + } + elseif ($command.action -eq 'kill-process') { + Get-Process ONENOTE | Stop-Process -Force + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = New-Object -ComObject OneNote.Application + $app.OpenHierarchy($shared, '', [ref]$notebook, 0) + $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0) + } + elseif ($command.action -eq 'edit' -or $command.action -eq 'snapshot') { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText("$output\hierarchy.xml", $hierarchy, [Text.Encoding]::UTF8) + [xml]$tree = $hierarchy + $matches = 0 + $editedPage = $null + $i = 0 + foreach ($node in $tree.SelectNodes('//*[local-name()="Page"]')) { + $content = '' + $app.GetPageContent($node.GetAttribute('ID'), [ref]$content, 1, 1) + [IO.File]::WriteAllText("$output\page-$i.xml", $content, [Text.Encoding]::UTF8) + [xml]$page = $content + if ($command.action -eq 'edit') { + $targets = @($page.SelectNodes('//*[local-name()="T"]') | Where-Object { + (Get-ParagraphText $_.InnerText) -eq $command.expected + }) + if ($targets.Count -gt 1) { throw 'Expected one matching text object.' } + if ($targets.Count -eq 1) { + $matches++ + if ($matches -gt 1) { throw 'The text occurs on multiple pages.' } + $targets[0].InnerText = [Security.SecurityElement]::Escape($command.text) + Keep-Container $page $targets[0] + $editedPage = $page + } + } + $i++ + } + if ($command.action -eq 'edit' -and $matches -ne 1) { throw 'Expected one matching text object.' } + if ($editedPage) { $app.UpdatePageContent($editedPage.OuterXml, [DateTime]::MinValue, 1, $false) } + } elseif ($command.action -ne 'close') { throw 'Unknown collaboration command.' } + [IO.File]::WriteAllText("$output\done", 'ok') + } catch { + [IO.File]::WriteAllText("$output\error", $_.Exception.ToString()) + } + if ($command.action -eq 'close') { break } + $sequence++ + } +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + [IO.File]::WriteAllText("$Root\closed", 'closed') + } +} diff --git a/corpus/conflict-page/native-pages/scripts/network.ps1 b/corpus/conflict-page/native-pages/scripts/network.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..95fa83d5243ee6a2a653cc075128b063e2f09cf2 --- /dev/null +++ b/corpus/conflict-page/native-pages/scripts/network.ps1 @@ -0,0 +1,20 @@ +param([Parameter(Mandatory=$true)][string]$LabMac) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$adapter = @(Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.MACAddress -eq $LabMac }) +if ($adapter.Count -ne 1) { throw 'The clone lab adapter is ambiguous.' } +$enabled = $adapter[0].EnableDHCP() +if ($enabled.ReturnValue -notin @(0, 1)) { + throw ('Unable to enable DHCP on the clone lab adapter; result ' + $enabled.ReturnValue) +} +$release = $adapter[0].ReleaseDHCPLease() +$result = $adapter[0].RenewDHCPLease() +$deadline = [DateTime]::UtcNow.AddSeconds(60) +do { + $adapter = @(Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.MACAddress -eq $LabMac }) + $addresses = @($adapter[0].IPAddress | Where-Object { $_ -match '^192\.168\.77\.\d+$' -and $_ -ne '192.168.77.1' }) + if ($addresses.Count -eq 1) { break } + Start-Sleep -Milliseconds 250 +} while ([DateTime]::UtcNow -lt $deadline) +if ($addresses.Count -ne 1) { throw ('The clone did not receive a lab IPv4 address; renewal result ' + $result.ReturnValue + '; adapter ' + ($adapter[0] | Select-Object MACAddress,DHCPEnabled,IPEnabled,IPAddress | ConvertTo-Json -Compress)) } +@{mac=$LabMac;address=$addresses[0];releaseResult=$release.ReturnValue;renewalResult=$result.ReturnValue} | ConvertTo-Json -Compress diff --git a/corpus/conflict-page/native-pages/scripts/stress.ps1 b/corpus/conflict-page/native-pages/scripts/stress.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0ab5bc69b4dabe970373e481873dfd951a1a69a --- /dev/null +++ b/corpus/conflict-page/native-pages/scripts/stress.ps1 @@ -0,0 +1,83 @@ +function Invoke-Stress($app, $section, $command, $output, $shared) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText("$output\hierarchy.xml", $hierarchy, [Text.Encoding]::UTF8) + [xml]$tree = $hierarchy + $pages = @($tree.SelectNodes('//*[local-name()="Page"]')) + if ($pages.Count -ne 1) { throw 'Expected one stress-test page.' } + $pageId = $pages[0].GetAttribute('ID') + if ($command.action -eq 'prepare-stress') { + for ($i = 0; $i -lt $command.clients; $i++) { + $xml = '' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + } + return + } + $random = New-Object Random([int]$command.seed) + $events = New-Object IO.StreamWriter("$output\events.jsonl", $false, [Text.Encoding]::UTF8) + $events.AutoFlush = $true + try { + [IO.File]::WriteAllText("$output\ready", 'ready') + $deadline = [DateTime]::UtcNow.AddMinutes(10) + while (-not (Test-Path "$shared\stress-start")) { + if ([DateTime]::UtcNow -gt $deadline) { throw 'Stress start timed out.' } + Start-Sleep -Milliseconds 20 + } + for ($i = 0; $i -lt $command.operations; $i++) { + if (($command.PSObject.Properties.Name -contains 'maintenance') -and $command.maintenance -and $i -eq [Math]::Floor($command.operations / 2)) { + [IO.File]::WriteAllText("$shared\maintenance-paused-n$($command.actor)", 'paused') + while (-not (Test-Path "$shared\maintenance-resume")) { + if ([DateTime]::UtcNow -gt $deadline) { throw 'Maintenance pause timed out.' } + Start-Sleep -Milliseconds 100 + } + } + Start-Sleep -Milliseconds $random.Next(10, 100) + $started = [DateTime]::UtcNow.Ticks + $content = '' + $app.GetPageContent($pageId, [ref]$content, 1, 1) + [xml]$page = $content + $matches = @($page.SelectNodes('//*[local-name()="T"]') | Where-Object { + (Get-ParagraphText $_.InnerText).StartsWith($command.prefix) + }) + if ($matches.Count -ne 1) { throw 'Expected one stress-test paragraph.' } + $before = Get-ParagraphText $matches[0].InnerText + $token = ' [n' + $command.actor + ':' + $i + ']' + $start = $end = $before.Length + $replacement = $token + $bold = $italic = $false + if ($command.edit) { + $boundaries = New-Object 'Collections.Generic.List[int]' + for ($at = $command.prefix.Length; $at -le $before.Length; $at++) { + $boundaries.Add($at) + if ($at -lt $before.Length -and [char]::IsHighSurrogate($before[$at])) { $at++ } + } + $first = $boundaries[$random.Next($boundaries.Count)] + $second = $boundaries[$random.Next($boundaries.Count)] + $start = [Math]::Min($first, $second) + $end = [Math]::Max($first, $second) + $replacement = ' caf' + [char]0xe9 + ' ' + [char]::ConvertFromUtf32(0x1f980) + $token + $bold = $random.Next(2) -eq 1 + $italic = $random.Next(2) -eq 1 + } + $after = $before.Substring(0, $start) + $replacement + $before.Substring($end) + $html = [Security.SecurityElement]::Escape($after) + if ($command.edit) { + $weight = if ($bold) { 'bold' } else { 'normal' } + $style = if ($italic) { 'italic' } else { 'normal' } + $html = '' + $html + '' + } + $matches[0].InnerText = $html + Keep-Container $page $matches[0] + $updateStarted = [DateTime]::UtcNow.Ticks + $app.UpdatePageContent($page.OuterXml, [DateTime]::MinValue, 1, $false) + $updated = [DateTime]::UtcNow.Ticks + $events.WriteLine((@{ operation=$i; before=$before; token=$token; started_ticks=$started; + range=@($start, $end); replacement=$replacement; bold=$bold; italic=$italic; + update_started_ticks=$updateStarted; updated_ticks=$updated } | ConvertTo-Json -Compress)) + if ($i -eq 0) { [IO.File]::WriteAllText("$output\editing", 'editing') } + if ($command.sync_every -gt 0 -and (($i + 1) % $command.sync_every) -eq 0) { + $app.SyncHierarchy($section) + } + } + } finally { $events.Dispose() } +} diff --git a/corpus/conflict-page/native-pages/scripts/text.ps1 b/corpus/conflict-page/native-pages/scripts/text.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..75e9e71207b599d16eaaf68824ca30831860610a --- /dev/null +++ b/corpus/conflict-page/native-pages/scripts/text.ps1 @@ -0,0 +1,13 @@ +function Get-ParagraphText([string]$html) { + $plain = [regex]::Replace($html, '<[^>]*>', '') + $plain = [regex]::Replace($plain, '&#(?:x([0-9a-fA-F]+)|([0-9]+));', { + param($match) + $value = if ($match.Groups[1].Success) { + [Convert]::ToInt32($match.Groups[1].Value, 16) + } else { [int]$match.Groups[2].Value } + # The Win7 framework leaves supplementary numeric entities undecoded. + if ($value -gt 0xffff -and $value -le 0x10ffff) { [char]::ConvertFromUtf32($value) } + else { $match.Value } + }) + [Net.WebUtility]::HtmlDecode($plain) +} diff --git a/corpus/conflict-page/native-pages/server.jsonl b/corpus/conflict-page/native-pages/server.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..d5df993564a30ddaea1b427699648ff9b1a1417d --- /dev/null +++ b/corpus/conflict-page/native-pages/server.jsonl @@ -0,0 +1,2 @@ +{"command": "mkdir /srv/agent/m6-conflict", "exit": 0, "stdout": "", "stderr": ""} +{"command": "chmod u+w /srv/agent/m6-conflict/*", "exit": 0, "stdout": "", "stderr": ""} diff --git a/corpus/conflict-page/native-restore/a/a-merged.json b/corpus/conflict-page/native-restore/a/a-merged.json new file mode 100644 index 0000000000000000000000000000000000000000..2f394e7eaf1defcfe3e06e321b5af76be4f84054 --- /dev/null +++ b/corpus/conflict-page/native-restore/a/a-merged.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Fictitious: caf\u00e9, \u6771\u4eac, \u0645\u0631\u062d\u0628\u0627 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/a/a-merged.png b/corpus/conflict-page/native-restore/a/a-merged.png new file mode 100644 index 0000000000000000000000000000000000000000..e1de0c6ef0b01ae79888f9cbead1ac135d60d788 GIT binary patch literal 47185 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 + diff --git a/corpus/conflict-page/native-restore/a/hierarchy-0025.xml b/corpus/conflict-page/native-restore/a/hierarchy-0025.xml new file mode 100644 index 0000000000000000000000000000000000000000..11e06fe95607fb8908a394804a20cbb4d93601b0 --- /dev/null +++ b/corpus/conflict-page/native-restore/a/hierarchy-0025.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-restore/a/listed.json b/corpus/conflict-page/native-restore/a/listed.json new file mode 100644 index 0000000000000000000000000000000000000000..559836710a7579ea1e2c4dd98dff086a1d6bf2b3 --- /dev/null +++ b/corpus/conflict-page/native-restore/a/listed.json @@ -0,0 +1 @@ +["Fictitious: café, 東京, مرحبا", "Four", "One", "Two", "Target", "Three"] \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/a/machine.json b/corpus/conflict-page/native-restore/a/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..084bcf445566e4a984e4a5465bf8a0ceabf53384 --- /dev/null +++ b/corpus/conflict-page/native-restore/a/machine.json @@ -0,0 +1 @@ +{"name": "m6-d281b38e", "hostname": "ONE-M6-D281B38E"} diff --git a/corpus/conflict-page/native-restore/a/teardown.json b/corpus/conflict-page/native-restore/a/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native-restore/a/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native-restore/b-published/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one b/corpus/conflict-page/native-restore/b-published/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one new file mode 100644 index 0000000000000000000000000000000000000000..753afa4aa521f599083936966e3e96226937d4e6 GIT binary patch literal 10256 zcmeHM4Nz3q6+Z7Ru)DCpqDvIm#TB+`s2CA3TD8C~ps27KHSI)+3K3FmA_Q^7pBZ13 zBv8Sy7GgBfHW>}JQ3efZsn%%KX>p>))J&3AF{m|@q>YWEjOM4(^S!r^T{h2{iIYs5 z?3wxYzI*O{=iYPfJ@=gZ_8&8fR@T4WQrNnCX7$OGr%qM2^=w~6a|#>h?kkNSI`=UO zpscRV<^)gAFIcF;7?uIFjOH6;pD?ticAZUUA z6$>a4Q;C^V_l{a~IQGyw)$if0u1lNZP!}074Df)k<{u429sB`a9{647ibtcaXAES7 zGr%__<$ne_2(T6}5Bw)Z;SaIiI{1(We)lIyaf7%7p&oL!+=x1!fqfS*e}cN&|JbU` z;iyAC+ON2666#0~P_OqS=As_ztv6mgUxaa^7I%@o2 zVUmv4*vPUjgDkID$%4xmaPHhW{$*7p6aVNa%}Ul%D-E_=$$^t~jxpxY!JcPbO=izw z)Bf6j9{khOYs=hS0+d$>Pk@ddv{9=1J~(*4ZGQEb9hU1;>fV}Ba{9rPYi$wF$nSF# zle%t*OQ3e%*IH&yVyif~4SfPLSnx4gptbMfcj~$A{Dr@rADTC3%IV5E&HGiet+M~V;Wmce=MjImN`pWCUn4dS`;EP;rCNT#|v+@|8Q7Y z*|ewfvo0^*x#mv1r0Mq?Ix=G5+p8zXJo>W)qpQhtbX~}61+&$RNUIIm5!&JRm|^s) zU1##WU#f~9h96Xdl49uL;)I4U_bi?+=@r~a-`*d zJWmPvC(fq`TEXXMY6jWQTB(-P(%M>v$wswLaCyT@=1x94pUxobz(jI5Tp#?z7=qju zq(kzZK5q1AyR#5>VS`9G9f3rKQI@^jM)qokk;lee!2O* zeCzq)Ti(p2ix(6xPqoi1t*9z}q@=Xco<1hko}QL|Pv0i=pLfD-&KpD87?HB>h&Kb+ zegj!==x*g0g?YoyumNliTW2!AhEdFRz%Ihc7&5y211_={OxavAgw31cWR8h>uoH3v z+$Riu>xeZ|*GrY#&f_R+$Geke6<0n~S_So0@FSDI9dL(HWZrE3RGGfDV`&n6F6ySb zf9%y8?|K;jVx4=rRObo(F{fMU-3YMzZ8XLv2vGYVLKA;Zm>#t;}H zV=LoVj9)XJV=T3i^*Un%`*Ae;x0#{NN3nv$yA_~pV0uG~lc<$J;Gr!OuybV^xhao^ zQz6~Mzqyo7V>wB2(I_tSxE(iV^%*jcA3MhHf_S9tNaZmbqv9kHX=Mgw;H;53N-MwS zeUQTF3i~DUx?Y~MnnNN-gk@$?F;&t-R7zD`!+EwRdzy3gl94%EuDU0!Jt4RtRgE|& zw~m;H>`~2Y;Ey20oinc=XtKRC%6(whxZiYz7QKY`{NqkiZ^?Z(toOS%zM5AxY5dl+ zYTkFVc=Sr`W8eQvY(hcH$x%;+HV*kImrbdr?KKBr+HC20sAwzk73k>A{`&Y z+fXO>__r7jt3@zF$qX-uCMa*}W1JE7*oTpQjK7NE*vL^b!#_+jvp<;~-g^JI-M`Z} zTvOKvXB+5H5jC99#k8li9#7jByK_O?{;W*TP%GH)==6&sr(SQov~_>IbAHFw?@f_= zzpFF6F)y}wk!M`jyvJL{|2@C75xp^FoS|DhFuhjxtc`pyhac?%~vLPI=LK$HU6UVV|jsp>#H{H$O z5k@f57_<(;&K)(q(``9!IC7$i5zZun7Jh!KNd0{F}5G| z$~DPIcO;VGw;5!xaEYl|7kRP&}lTZ7K5$U`4vx0KI1XNrf)@C$TR`i4R{bCOGyLR8 zjhsW_I#!53|8#-b6gqJ3g*&j^K@7~o(1CL{99IG)d@g%K>U>l*;*?J9%X0UM(Kv#9 zdGH?5fYZ&F2k#KWkwhwaFb3g#5wn(OAp~ZQ7-X{;h3lzLrbjd)qWEO46{GO+!6%bb rQ+%aSGz&~pA#;Vu-&FiXl#3Dg?xKkJ+CS~`NI?o(;Hz0c%m4oi2hB0Q literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-restore/b-published/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 b/corpus/conflict-page/native-restore/b-published/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..810bfcc34d55bf600d42219614d17db63df60ef9 GIT binary patch literal 3776 zcmZ3e|KIuM`!b(-j|5(YE0x6yP0o_M_Mq2se_D6a+{@=88PI_J-B|+C6Tf;cy&yjE z?TK|aq=E9t7^eI`5P;Y$FaanH6Nd>m#4gqT(JSk=YNPqbC`J8*td#5-AT=NWR>J{R z2{H&~7?6gjfT%gZ2Qv*qhfWa?tXS0Gyz8yje~}MovpJ#CXAcNkJdK&j(qw4bz@c75&@`pIC2=KHZZENPV3po-oD;@%c6F} zPbqPC7=h9(2@MU{#D_;fjQV0U1crGCAlI4RyNsI-iyrER<$F*DzdV&G0K{hkVnHAV ziGgr>=PX|kA0#dW6$6ReB;Q*M;&VgAOtVGK0GXp;Gz3ONV2FeOto{e(ZxGqA2D|X|)iWbVyP~i!g!kBcc(*#)%>U zTNl8pkW?YU%+wXhkbpBaL5<+Z)&ypp5kib1#-J7j#^jIgz4m*qWnZ@atOoo`<~p0Rj&CYeR+h8m?|wUa5W z_Jy@idaZZEK=0X@xf2M-{!GC4Og>BO#i9+8OUN8ZfWw9az1zZVWBhQH6a_H%!k06j=+tH z-zVD-=vt(FBL4ZrOht;B!j;qS4F8exeO6WOQ;WV!1Zqhpr9$#I9RgqcJ_Ozh1U%nN z^_BRF>mfZy*}caLK42E7Auoo8ZzjsH=jzd0*=Rb?M7lr%^oDHg+dAI4T0yVx1Hs$C?!VM8c5BtzR5W zDNz9QXlf`*v*;F7bBF3$!Y^#pH7%7=Y6RU^A*Zr+S(WOx;_j}{fm^jWHAYEHy&@5- zz)-IbXAz@kBIpsRZEGy0%uWt2ol-(wn*aPs|A04@5W?OMkWgMqMZ_j2!DjP$5P0H- zfg(0Nqs85N`Ap)sqj&5%T&WG^LU=hYsAprd1cC=^7co54_xroFnp*Bki7})6g=Plb zt&b(9K9Lynl+1|)W{>e4yI5B`m^rAjS@$QM`Qf|c=s+k&`z^!lc-UUan)h4l>n$5E zZTP8f{IRu+#fh$1oKdPhW99vwspq20(&Mj=bfOOTEmizJ+Yi_`j9e|21c260F!Hi! z@{&b9zU0NBUv_+>xu$k1W!j zgNoo7ZScWoS$uBW_GGwMt1^;^w*GU|aj3^Ewuu8VW@Py+G9U|>`R`ebMcwP;LhmLw z9IV&1Kf4@_K7=xJ$Vg?jM)0l>t|zF5drw)}cD=l{=}~xOqWhf8!(gDX=p$O50TRoV&Q{K>SFK?N^+Ge~^1fDJdW&%#;(&nqv?DX2bIc4x%2{#y3YotA;0x1#Szmv4-p`Q1*o0B3+a e4J02%8z9R9utI`EN--zASy(l^bHn5Arau8gJv43r literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-restore/b-published/notebook/synthetic.one b/corpus/conflict-page/native-restore/b-published/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..7f97f031472ec657a2563be65149f4de16f5466e GIT binary patch literal 56640 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

# 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 L#ygn28T9`FY_&on literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-restore/b/command-0001.json b/corpus/conflict-page/native-restore/b/command-0001.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0001.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0002.json b/corpus/conflict-page/native-restore/b/command-0002.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0002.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0003.json b/corpus/conflict-page/native-restore/b/command-0003.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0003.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0004.json b/corpus/conflict-page/native-restore/b/command-0004.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0004.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0005.json b/corpus/conflict-page/native-restore/b/command-0005.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0005.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0006.json b/corpus/conflict-page/native-restore/b/command-0006.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0006.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0007.json b/corpus/conflict-page/native-restore/b/command-0007.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0007.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0008.json b/corpus/conflict-page/native-restore/b/command-0008.json new file mode 100644 index 0000000000000000000000000000000000000000..eff482c021828f4924fb0845aa6043a8312745ea --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0008.json @@ -0,0 +1 @@ +{"action": "delete-page", "page": "Target"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0009.json b/corpus/conflict-page/native-restore/b/command-0009.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0009.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0010.json b/corpus/conflict-page/native-restore/b/command-0010.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0010.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0011.json b/corpus/conflict-page/native-restore/b/command-0011.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0011.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0012.json b/corpus/conflict-page/native-restore/b/command-0012.json new file mode 100644 index 0000000000000000000000000000000000000000..2272087176d433cd1d42b77fb18efd145b9bbbf5 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0012.json @@ -0,0 +1 @@ +{"action": "sync"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0013.json b/corpus/conflict-page/native-restore/b/command-0013.json new file mode 100644 index 0000000000000000000000000000000000000000..554ff0ef8a35478d74f3e5388f75789140b158b8 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0013.json @@ -0,0 +1 @@ +{"action": "snapshot"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/command-0014.json b/corpus/conflict-page/native-restore/b/command-0014.json new file mode 100644 index 0000000000000000000000000000000000000000..7adf980027ef43cc77767d4fd1b1d67ad1541ab4 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/command-0014.json @@ -0,0 +1 @@ +{"action": "close"} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/commands.jsonl b/corpus/conflict-page/native-restore/b/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..2335dad6840245c83e266c7e5d6601001755f2d0 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/commands.jsonl @@ -0,0 +1,16 @@ +{"command": "mkdir C:\\one-tests\\runs\\capture", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -ExecutionPolicy Bypass -File C:\\one-tests\\network.ps1 -LabMac 52:54:02:7b:1a:4d", "exit": 0, "stdout": "{\"renewalResult\":0,\"releaseResult\":0,\"mac\":\"52:54:02:7b:1a:4d\",\"address\":\"192.168.77.40\"}\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0001.tmp C:\\one-tests\\runs\\capture\\inbox\\0001.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0002.tmp C:\\one-tests\\runs\\capture\\inbox\\0002.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0003.tmp C:\\one-tests\\runs\\capture\\inbox\\0003.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0004.tmp C:\\one-tests\\runs\\capture\\inbox\\0004.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0005.tmp C:\\one-tests\\runs\\capture\\inbox\\0005.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0006.tmp C:\\one-tests\\runs\\capture\\inbox\\0006.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0007.tmp C:\\one-tests\\runs\\capture\\inbox\\0007.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0008.tmp C:\\one-tests\\runs\\capture\\inbox\\0008.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0009.tmp C:\\one-tests\\runs\\capture\\inbox\\0009.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0010.tmp C:\\one-tests\\runs\\capture\\inbox\\0010.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0011.tmp C:\\one-tests\\runs\\capture\\inbox\\0011.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0012.tmp C:\\one-tests\\runs\\capture\\inbox\\0012.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0013.tmp C:\\one-tests\\runs\\capture\\inbox\\0013.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} +{"command": "move C:\\one-tests\\runs\\capture\\inbox\\0014.tmp C:\\one-tests\\runs\\capture\\inbox\\0014.json", "exit": 0, "stdout": " 1 file(s) moved.\r\n", "stderr": "", "error": null} diff --git a/corpus/conflict-page/native-restore/b/hierarchy-0001.xml b/corpus/conflict-page/native-restore/b/hierarchy-0001.xml new file mode 100644 index 0000000000000000000000000000000000000000..831d1d427c0ef26e4d79ed1c8f185ea22c942068 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/hierarchy-0001.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-restore/b/hierarchy-0003.xml b/corpus/conflict-page/native-restore/b/hierarchy-0003.xml new file mode 100644 index 0000000000000000000000000000000000000000..18798800a4f9f74d5296ac80adbf8348f81d6f43 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/hierarchy-0003.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-restore/b/hierarchy-0005.xml b/corpus/conflict-page/native-restore/b/hierarchy-0005.xml new file mode 100644 index 0000000000000000000000000000000000000000..5c16c8efeebe230903ed784409d50d7c039c87fd --- /dev/null +++ b/corpus/conflict-page/native-restore/b/hierarchy-0005.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-restore/b/hierarchy-0007.xml b/corpus/conflict-page/native-restore/b/hierarchy-0007.xml new file mode 100644 index 0000000000000000000000000000000000000000..2a44e1e49093e9f699bfa6eaa4821e4d3c1de3f8 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/hierarchy-0007.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-restore/b/hierarchy-0009.xml b/corpus/conflict-page/native-restore/b/hierarchy-0009.xml new file mode 100644 index 0000000000000000000000000000000000000000..e5aeaf4af2852eaea5d8f51cd321eb61baf23cfb --- /dev/null +++ b/corpus/conflict-page/native-restore/b/hierarchy-0009.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-restore/b/hierarchy-0013.xml b/corpus/conflict-page/native-restore/b/hierarchy-0013.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd73cb16e07cd7840fbd0e3ed63ad9d52459e1e1 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/hierarchy-0013.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/conflict-page/native-restore/b/listed.json b/corpus/conflict-page/native-restore/b/listed.json new file mode 100644 index 0000000000000000000000000000000000000000..559836710a7579ea1e2c4dd98dff086a1d6bf2b3 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/listed.json @@ -0,0 +1 @@ +["Fictitious: café, 東京, مرحبا", "Four", "One", "Two", "Target", "Three"] \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/b/machine.json b/corpus/conflict-page/native-restore/b/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..54a3c6b9ac2277847928cb46fb3d062726a4070a --- /dev/null +++ b/corpus/conflict-page/native-restore/b/machine.json @@ -0,0 +1 @@ +{"name": "m6-a205cc61", "hostname": "ONE-M6-A205CC61"} diff --git a/corpus/conflict-page/native-restore/b/teardown.json b/corpus/conflict-page/native-restore/b/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native-restore/b/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native-restore/clients.json b/corpus/conflict-page/native-restore/clients.json new file mode 100644 index 0000000000000000000000000000000000000000..0c922a99bf3f3f13b0eb03af843301a714be0c4c --- /dev/null +++ b/corpus/conflict-page/native-restore/clients.json @@ -0,0 +1,4 @@ +{ + "a": "m6-d281b38e", + "b": "m6-a205cc61" +} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/initial/notebook/Open Notebook.onetoc2 b/corpus/conflict-page/native-restore/initial/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..c7beb44bf509f935090fe55e7549e4b50e0ae0e9 GIT binary patch literal 4944 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-restore/linux-teardown.json b/corpus/conflict-page/native-restore/linux-teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native-restore/linux-teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native-restore/linux.json b/corpus/conflict-page/native-restore/linux.json new file mode 100644 index 0000000000000000000000000000000000000000..51be344e66ab7a2c56327e2ce949f3785fedee54 --- /dev/null +++ b/corpus/conflict-page/native-restore/linux.json @@ -0,0 +1,11 @@ +{ + "cpus": 2, + "disk_gb": 16, + "hostname": "one-conflict3", + "lab_address": "192.168.77.1", + "lab_mac": "52:54:04:08:6d:03", + "memory_mb": 2048, + "samba_port": 14450, + "ssh_port": 22000, + "wan_mac": "52:54:03:bb:9c:87" +} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/merged.json b/corpus/conflict-page/native-restore/merged.json new file mode 100644 index 0000000000000000000000000000000000000000..889578b037833da0bb993bcb332e6377ca5fbb09 --- /dev/null +++ b/corpus/conflict-page/native-restore/merged.json @@ -0,0 +1,49 @@ +{ + "order": [ + "", + "Four", + "One", + "Two", + "Target", + "Three" + ], + "pages": [ + [ + "", + "Fictitious positioned outline.", + "Fictitious: café, 東京, مرحبا", + "Left cell", + "Right cell" + ], + [ + "2:55 AM", + "Body Four.", + "Four", + "Sunday, September 27, 2026" + ], + [ + "2:55 AM", + "Body One.", + "One", + "Sunday, September 27, 2026" + ], + [ + "2:55 AM", + "Body Two.", + "Sunday, September 27, 2026", + "Two" + ], + [ + "2:55 AM", + "Body Target edited offline.", + "Sunday, September 27, 2026", + "Target" + ], + [ + "2:55 AM", + "Body Three.", + "Sunday, September 27, 2026", + "Three" + ] + ] +} \ No newline at end of file diff --git a/corpus/conflict-page/native-restore/merged/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one b/corpus/conflict-page/native-restore/merged/notebook/OneNote_RecycleBin/OneNote_DeletedPages.one new file mode 100644 index 0000000000000000000000000000000000000000..d58aedc6e4ba0cee7324ec43ffa4997e89ac864a GIT binary patch literal 11728 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-restore/merged/notebook/synthetic.one b/corpus/conflict-page/native-restore/merged/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..82effac1ffff4519beccb2392a513f3985fa7916 GIT binary patch literal 63560 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 1 and 'Client A disjoint edit.' in texts(found), 'conflict') + (output / 'conflict.json').write_text(json.dumps(state('conflict'), indent=2, ensure_ascii=False)) + page = action(a, 'snapshot')[0].attrib['ID'] + conflict_views(a['name'], output / 'a', page, 'a') + action(b, 'sync') + time.sleep(5) + page_b = action(b, 'snapshot')[0].attrib['ID'] + conflict_views(b['name'], output / 'b', page_b, 'b') + conflict_views(a['name'], output / 'a', page, 'a-again', delete=True) + settle(a, lambda label: len(state(label)) == 1, 'deleted') + action(b, 'sync') + time.sleep(5) + shot(b['name'], output / 'b', 'b-after-delete', runner.navigation_script(page_b) + 'Sleep(2000)\n') + + +PAGES = ['One', 'Two', 'Three', 'Four', 'Target'] + + +def titled(model, titles): + """The section's pages in order, each named by the title among `titles` it holds.""" + return [next((text for text in texts if text in titles), '') for texts in page_list(model)] + + +def offline(lab, titles, a_actions, b_actions): + """Creates the titled pages; A runs its actions offline while B runs and publishes its + own; A then reconnects. Keeps the merged order and pages, and what each client lists.""" + a, b, output, action = lab.a, lab.b, lab.output, lab.action + action(a, 'pages', titles=titles) + lab.settle(a, lambda label: 'Body Target.' in str(page_list(lab.state(label))), 'initial') + deadline = time.monotonic() + 300 + while not any(node.get('name') == 'Target' for node in action(b, 'snapshot')): + action(b, 'sync') + if time.monotonic() > deadline: raise TimeoutError('B never listed the new pages') + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': False}) + try: + for kind, parameters in a_actions: + action(a, kind, **parameters) + action(a, 'snapshot') + for kind, parameters in b_actions: + action(b, kind, **parameters) + action(b, 'snapshot') + lab.settle(b, lambda label: 'Body Target.' not in str(page_list(lab.state(label))), 'b-published') + finally: + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': True}) + # A's changes reach the server when its sync writes; wait until the file stops changing. + previous, steady = None, 0 + deadline = time.monotonic() + 300 + while steady < 3 and time.monotonic() < deadline: + action(a, 'sync') + time.sleep(10) + current = page_list(lab.state('merged')) + steady = steady + 1 if current == previous else 0 + previous = current + (output / 'merged.json').write_text(json.dumps({'order': titled(lab.state('merged'), titles), 'pages': previous}, indent=2, ensure_ascii=False)) + for client, label in ((a, 'a'), (b, 'b')): + action(client, 'sync') + time.sleep(5) + listed = action(client, 'snapshot') + (output / label / 'listed.json').write_text(json.dumps([node.get('name') for node in listed], ensure_ascii=False)) + if listed: + shot(client['name'], output / label, f'{label}-merged', runner.navigation_script(listed[0].get('ID')) + 'Sleep(2000)\n') + + +def pages(lab): + """Competing page moves, and an offline edit of a page the other client deletes.""" + offline(lab, PAGES, [ + ('move', {'page': 'Four', 'before': 'One'}), + ('move', {'page': 'Two', 'before': None}), + ('edit', {'expected': 'Body Target.', 'text': 'Body Target edited offline.'}), + ], [ + ('move', {'page': 'Four', 'before': None}), + ('move', {'page': 'Three', 'before': 'One'}), + ('delete-page', {'page': 'Target'}), + ]) + + +def restore(lab): + """An offline edit of a page in the middle of the list the other client deletes, with an + offline move of a page the other client leaves alone.""" + offline(lab, ['One', 'Two', 'Target', 'Three', 'Four'], [ + ('edit', {'expected': 'Body Target.', 'text': 'Body Target edited offline.'}), + ('move', {'page': 'Four', 'before': 'One'}), + ], [ + ('delete-page', {'page': 'Target'}), + ]) + + +def sync(server, output, scenario): + from document_model import EXPORTER + + if linux_vm.instance_path(server).exists(): + raise ValueError('Choose a new Linux VM name; existing machines are not owned by this run.') + output = output.resolve() + output.mkdir(parents=True, exist_ok=False) + scripts = output / 'scripts' + scripts.mkdir() + for name in ('cold.ps1', 'collaborate.ps1', 'network.ps1', 'text.ps1', 'stress.ps1'): + shutil.copyfile(ROOT / 'tools/native' / name, scripts / name) + shutil.copyfile(Path(__file__), output / 'native_conflict.py') + + def ssh(text, timeout=90): + result = linux_vm.run_ssh(server, text, timeout=timeout) + with (output / 'server.jsonl').open('a') as log: + log.write(json.dumps({'command': text, 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr}) + '\n') + result.check_returncode() + return result.stdout + + def server_state(label): + """Copies the server's notebook, recycle bin included, until its section parses, + keeping it as `label/notebook`; returns the section's exported model.""" + deadline = time.monotonic() + 120 + while True: + target = output / label + shutil.rmtree(target, ignore_errors=True) + (target / 'notebook').mkdir(parents=True) + archive = subprocess.run(linux_vm.ssh_argv(server, f"tar cf - -C '/srv/agent/{SHARE}' ."), capture_output=True, check=True, timeout=60).stdout + subprocess.run(['tar', 'xf', '-', '-C', target / 'notebook'], input=archive, check=True) + result = subprocess.run([EXPORTER, target / 'notebook/synthetic.one', target / 'model'], capture_output=True, text=True) + if result.returncode == 0: + return json.loads((target / 'model/document.json').read_text()) + if time.monotonic() > deadline: + raise RuntimeError(f'{label}: {result.stderr}') + time.sleep(1) + + linux_vm.create_instance(server) + try: + linux_vm.launch(server) + linux_vm.wait_instance(server, 600) + (output / 'linux.json').write_text(json.dumps(linux_vm.load_instance(server), indent=2)) + ssh(f'mkdir /srv/agent/{SHARE}') + for name in ('synthetic.one', 'Open Notebook.onetoc2'): + with (FIXTURE / name).open('rb') as stream: + subprocess.run(linux_vm.ssh_argv(server, f"cat > '/srv/agent/{SHARE}/{name}'"), stdin=stream, check=True) + ssh(f'chmod u+w /srv/agent/{SHARE}/*') + with ExitStack() as stack: + labels = ['a', 'b'] + for label in labels: + (output / label).mkdir() + + def start(label): + manager = clone(output / label) + name = manager.__enter__() + return label, name, manager + with ThreadPoolExecutor(max_workers=2) as pool: + started = list(pool.map(start, labels)) + for _, _, manager in started: + stack.push(manager) + clients = {} + for label, name, _ in started: + folder = output / label + for local in scripts.iterdir(): + remote = 'cold-current.ps1' if local.name == 'cold.ps1' else local.name + result = windows.do_put(local, 'C:\\one-tests\\' + remote, name) + if result.get('error'): raise RuntimeError(result['error']) + command(name, 'mkdir C:\\one-tests\\runs\\capture', folder) + command(name, 'powershell -NoProfile -ExecutionPolicy Bypass -File C:\\one-tests\\network.ps1 -LabMac ' + vm.lab_mac(name), folder) + result = windows.do_spawn('cmd /c powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\collaborate.ps1 -Root C:\\one-tests\\runs\\capture -SharedPath \\\\192.168.77.1\\agent\\' + SHARE + ' -CloneHost ONE-' + name.upper() + ' > C:\\one-tests\\runs\\capture\\controller.log 2>&1', name) + if result.get('error'): raise RuntimeError(result['error']) + deadline = time.monotonic() + 180 + while windows.do_cmd('if exist C:\\one-tests\\runs\\capture\\ready (echo ready)', target=name).get('stdout', '').find('ready') < 0: + if time.monotonic() > deadline: raise RuntimeError('The collaboration controller did not open the notebook') + time.sleep(1) + clients[label] = {'name': name, 'folder': folder, 'sequence': 0} + (output / 'clients.json').write_text(json.dumps({label: client['name'] for label, client in clients.items()}, indent=2)) + + def action(client, kind, **parameters): + client['sequence'] += 1 + sequence = client['sequence'] + local = client['folder'] / f'command-{sequence:04}.json' + local.write_text(json.dumps({'action': kind, **parameters}, ensure_ascii=False)) + inbox = f'C:\\one-tests\\runs\\capture\\inbox\\{sequence:04}' + result = windows.do_put(local, inbox + '.tmp', client['name']) + if result.get('error'): raise RuntimeError(result['error']) + command(client['name'], f'move {inbox}.tmp {inbox}.json', client['folder']) + remote = f'C:\\one-tests\\runs\\capture\\outbox\\{sequence}' + deadline = time.monotonic() + 600 + while True: + result = windows.do_cmd(f'if exist {remote}\\error (type {remote}\\error & exit /b 1) else (if exist {remote}\\done (echo complete))', target=client['name']) + if result.get('exit') != 0: raise RuntimeError(str(result)) + if 'complete' in result.get('stdout', ''): break + if time.monotonic() > deadline: raise TimeoutError(f'{kind} did not complete') + time.sleep(.5) + if kind != 'snapshot': + return None + hierarchy = client['folder'] / f'hierarchy-{sequence:04}.xml' + windows.do_get(remote + '\\hierarchy.xml', hierarchy, client['name']) + pages = [node for node in ET.fromstring(hierarchy.read_text(encoding='utf-8-sig').strip()).iter() if node.tag.endswith('}Page')] + for i in range(len(pages)): + windows.do_get(remote + f'\\page-{i}.xml', client['folder'] / f'snapshot-{sequence:04}-{i}.xml', client['name']) + return pages + + def settle(client, check, label): + deadline = time.monotonic() + 300 + while True: + action(client, 'sync') + if check(label): return + if time.monotonic() > deadline: raise TimeoutError(label) + time.sleep(2) + + lab = Lab() + lab.a, lab.b, lab.output = clients['a'], clients['b'], output + lab.action, lab.state, lab.settle = action, server_state, settle + scenario(lab) + + for client in clients.values(): + try: + action(client, 'close') + except Exception as error: + (client['folder'] / 'close-failure.txt').write_text(str(error)) + server_state('final') + finally: + if linux_vm.running(server): + linux_vm.shutdown(server, 120) + linux_vm.delete_instance(server) + (output / 'linux-teardown.json').write_text(json.dumps({'absent': not linux_vm.instance_path(server).exists()}) + '\n') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + commands = parser.add_subparsers(dest='mode', required=True) + scenarios = {'sync': edits, 'pages': pages, 'restore': restore} + for mode in scenarios: + lab = commands.add_parser(mode) + lab.add_argument('linux') + lab.add_argument('output', type=Path) + reopen = commands.add_parser('cold') + reopen.add_argument('notebook', type=Path) + reopen.add_argument('output', type=Path) + reopen.add_argument('--delete', action='store_true') + args = parser.parse_args() + if args.mode in scenarios: + sync(args.linux, args.output, scenarios[args.mode]) + else: + cold(args.notebook, args.output, args.delete) + print('Captured', args.output) diff --git a/corpus/conflict-page/native-restore/scripts/cold.ps1 b/corpus/conflict-page/native-restore/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/conflict-page/native-restore/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/conflict-page/native-restore/scripts/collaborate.ps1 b/corpus/conflict-page/native-restore/scripts/collaborate.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..6a42a3d526db486f022b032583704b0f4aee26d3 --- /dev/null +++ b/corpus/conflict-page/native-restore/scripts/collaborate.ps1 @@ -0,0 +1,145 @@ +param([Parameter(Mandatory=$true)][string]$Root, + [Parameter(Mandatory=$true)][string]$SharedPath, + [Parameter(Mandatory=$true)][string]$CloneHost, + [switch]$ResumeCache, + [int]$StartSequence = 1) +function Keep-Container($page, $target) { + $container = $target + while ($container.ParentNode -ne $page.DocumentElement) { $container = $container.ParentNode } + foreach ($sibling in @($page.DocumentElement.ChildNodes)) { + if ($sibling -ne $container -and $sibling.LocalName -in @('Outline', 'Title', 'Image', 'InkDrawing')) { + [void]$page.DocumentElement.RemoveChild($sibling) + } + } +} +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\text.ps1" +if ($SharedPath -notmatch '^\\\\192\.168\.77\.1\\agent\\m6-[a-z0-9-]+$') { throw 'Choose a disposable Linux lab notebook.' } +if ($env:COMPUTERNAME -ne $CloneHost) { throw 'The collaboration controller requires its owned clone.' } +if (-not $ResumeCache) { & "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost } +$shared = $SharedPath +if (-not (Test-Path "$shared\synthetic.one")) { throw 'Create the shared fixture first.' } +New-Item -ItemType Directory "$Root\inbox", "$Root\outbox" -Force | Out-Null +$user = 'HKCU:\Software\Microsoft\Office\Common\UserInfo' +New-Item $user -Force | Out-Null +New-ItemProperty $user -Name UserName -Value $CloneHost -PropertyType String -Force | Out-Null +New-ItemProperty $user -Name UserInitials -Value $CloneHost.Substring($CloneHost.Length - 3) -PropertyType String -Force | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy($shared, '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0) + [IO.File]::WriteAllText("$Root\ready", 'ready') + $deadline = [DateTime]::UtcNow.AddMinutes(30) + $sequence = $StartSequence + while ([DateTime]::UtcNow -lt $deadline) { + $name = '{0:d4}.json' -f $sequence + $input = "$Root\inbox\$name" + if (-not (Test-Path $input)) { Start-Sleep -Milliseconds 100; continue } + $command = Get-Content -Raw -Encoding UTF8 $input | ConvertFrom-Json + $output = "$Root\outbox\$sequence" + New-Item -ItemType Directory $output | Out-Null + try { + if ($command.action -in @('prepare-stress', 'stress')) { + . "$PSScriptRoot\stress.ps1" + Invoke-Stress $app $section $command $output $shared + } + elseif ($command.action -eq 'sync') { $app.SyncHierarchy($notebook) } + elseif ($command.action -in @('pages', 'move', 'delete-page')) { + $namespace = 'http://schemas.microsoft.com/office/onenote/2010/onenote' + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$tree = $hierarchy + $listed = @($tree.SelectNodes('//*[local-name()="Page"]')) + $named = { param($title) @($listed | Where-Object { $_.GetAttribute('name') -eq $title }) } + if ($command.action -eq 'pages') { + # New titled pages with one body paragraph each, appended in order. + foreach ($title in $command.titles) { + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $body = [Security.SecurityElement]::Escape('Body ' + $title + '.') + $name = [Security.SecurityElement]::Escape($title) + $xml = '' + $name + '' + $body + '' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + } + } elseif ($command.action -eq 'move') { + # Moves the page titled `page` before the one titled `before`, or last. + $moved = @(& $named $command.page) + if ($moved.Count -ne 1) { throw 'Expected one page to move.' } + $order = [Collections.ArrayList]@($listed | Where-Object { $_ -ne $moved[0] }) + $at = $order.Count + if ($command.before) { + $anchor = @(& $named $command.before) + if ($anchor.Count -ne 1) { throw 'Expected one anchor page.' } + $at = $order.IndexOf($anchor[0]) + } + $order.Insert($at, $moved[0]) + $xml = '' + foreach ($node in $order) { + $xml += '' + } + $app.UpdateHierarchy($xml + '', 1) + } else { + $doomed = @(& $named $command.page) + if ($doomed.Count -ne 1) { throw 'Expected one page to delete.' } + $app.DeleteHierarchy($doomed[0].GetAttribute('ID'), [DateTime]::MinValue, $false) + } + } + elseif ($command.action -eq 'kill-process') { + Get-Process ONENOTE | Stop-Process -Force + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = New-Object -ComObject OneNote.Application + $app.OpenHierarchy($shared, '', [ref]$notebook, 0) + $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0) + } + elseif ($command.action -eq 'edit' -or $command.action -eq 'snapshot') { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText("$output\hierarchy.xml", $hierarchy, [Text.Encoding]::UTF8) + [xml]$tree = $hierarchy + $matches = 0 + $editedPage = $null + $i = 0 + foreach ($node in $tree.SelectNodes('//*[local-name()="Page"]')) { + $content = '' + $app.GetPageContent($node.GetAttribute('ID'), [ref]$content, 1, 1) + [IO.File]::WriteAllText("$output\page-$i.xml", $content, [Text.Encoding]::UTF8) + [xml]$page = $content + if ($command.action -eq 'edit') { + $targets = @($page.SelectNodes('//*[local-name()="T"]') | Where-Object { + (Get-ParagraphText $_.InnerText) -eq $command.expected + }) + if ($targets.Count -gt 1) { throw 'Expected one matching text object.' } + if ($targets.Count -eq 1) { + $matches++ + if ($matches -gt 1) { throw 'The text occurs on multiple pages.' } + $targets[0].InnerText = [Security.SecurityElement]::Escape($command.text) + Keep-Container $page $targets[0] + $editedPage = $page + } + } + $i++ + } + if ($command.action -eq 'edit' -and $matches -ne 1) { throw 'Expected one matching text object.' } + if ($editedPage) { $app.UpdatePageContent($editedPage.OuterXml, [DateTime]::MinValue, 1, $false) } + } elseif ($command.action -ne 'close') { throw 'Unknown collaboration command.' } + [IO.File]::WriteAllText("$output\done", 'ok') + } catch { + [IO.File]::WriteAllText("$output\error", $_.Exception.ToString()) + } + if ($command.action -eq 'close') { break } + $sequence++ + } +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + [IO.File]::WriteAllText("$Root\closed", 'closed') + } +} diff --git a/corpus/conflict-page/native-restore/scripts/network.ps1 b/corpus/conflict-page/native-restore/scripts/network.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..95fa83d5243ee6a2a653cc075128b063e2f09cf2 --- /dev/null +++ b/corpus/conflict-page/native-restore/scripts/network.ps1 @@ -0,0 +1,20 @@ +param([Parameter(Mandatory=$true)][string]$LabMac) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$adapter = @(Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.MACAddress -eq $LabMac }) +if ($adapter.Count -ne 1) { throw 'The clone lab adapter is ambiguous.' } +$enabled = $adapter[0].EnableDHCP() +if ($enabled.ReturnValue -notin @(0, 1)) { + throw ('Unable to enable DHCP on the clone lab adapter; result ' + $enabled.ReturnValue) +} +$release = $adapter[0].ReleaseDHCPLease() +$result = $adapter[0].RenewDHCPLease() +$deadline = [DateTime]::UtcNow.AddSeconds(60) +do { + $adapter = @(Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.MACAddress -eq $LabMac }) + $addresses = @($adapter[0].IPAddress | Where-Object { $_ -match '^192\.168\.77\.\d+$' -and $_ -ne '192.168.77.1' }) + if ($addresses.Count -eq 1) { break } + Start-Sleep -Milliseconds 250 +} while ([DateTime]::UtcNow -lt $deadline) +if ($addresses.Count -ne 1) { throw ('The clone did not receive a lab IPv4 address; renewal result ' + $result.ReturnValue + '; adapter ' + ($adapter[0] | Select-Object MACAddress,DHCPEnabled,IPEnabled,IPAddress | ConvertTo-Json -Compress)) } +@{mac=$LabMac;address=$addresses[0];releaseResult=$release.ReturnValue;renewalResult=$result.ReturnValue} | ConvertTo-Json -Compress diff --git a/corpus/conflict-page/native-restore/scripts/stress.ps1 b/corpus/conflict-page/native-restore/scripts/stress.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0ab5bc69b4dabe970373e481873dfd951a1a69a --- /dev/null +++ b/corpus/conflict-page/native-restore/scripts/stress.ps1 @@ -0,0 +1,83 @@ +function Invoke-Stress($app, $section, $command, $output, $shared) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText("$output\hierarchy.xml", $hierarchy, [Text.Encoding]::UTF8) + [xml]$tree = $hierarchy + $pages = @($tree.SelectNodes('//*[local-name()="Page"]')) + if ($pages.Count -ne 1) { throw 'Expected one stress-test page.' } + $pageId = $pages[0].GetAttribute('ID') + if ($command.action -eq 'prepare-stress') { + for ($i = 0; $i -lt $command.clients; $i++) { + $xml = '' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + } + return + } + $random = New-Object Random([int]$command.seed) + $events = New-Object IO.StreamWriter("$output\events.jsonl", $false, [Text.Encoding]::UTF8) + $events.AutoFlush = $true + try { + [IO.File]::WriteAllText("$output\ready", 'ready') + $deadline = [DateTime]::UtcNow.AddMinutes(10) + while (-not (Test-Path "$shared\stress-start")) { + if ([DateTime]::UtcNow -gt $deadline) { throw 'Stress start timed out.' } + Start-Sleep -Milliseconds 20 + } + for ($i = 0; $i -lt $command.operations; $i++) { + if (($command.PSObject.Properties.Name -contains 'maintenance') -and $command.maintenance -and $i -eq [Math]::Floor($command.operations / 2)) { + [IO.File]::WriteAllText("$shared\maintenance-paused-n$($command.actor)", 'paused') + while (-not (Test-Path "$shared\maintenance-resume")) { + if ([DateTime]::UtcNow -gt $deadline) { throw 'Maintenance pause timed out.' } + Start-Sleep -Milliseconds 100 + } + } + Start-Sleep -Milliseconds $random.Next(10, 100) + $started = [DateTime]::UtcNow.Ticks + $content = '' + $app.GetPageContent($pageId, [ref]$content, 1, 1) + [xml]$page = $content + $matches = @($page.SelectNodes('//*[local-name()="T"]') | Where-Object { + (Get-ParagraphText $_.InnerText).StartsWith($command.prefix) + }) + if ($matches.Count -ne 1) { throw 'Expected one stress-test paragraph.' } + $before = Get-ParagraphText $matches[0].InnerText + $token = ' [n' + $command.actor + ':' + $i + ']' + $start = $end = $before.Length + $replacement = $token + $bold = $italic = $false + if ($command.edit) { + $boundaries = New-Object 'Collections.Generic.List[int]' + for ($at = $command.prefix.Length; $at -le $before.Length; $at++) { + $boundaries.Add($at) + if ($at -lt $before.Length -and [char]::IsHighSurrogate($before[$at])) { $at++ } + } + $first = $boundaries[$random.Next($boundaries.Count)] + $second = $boundaries[$random.Next($boundaries.Count)] + $start = [Math]::Min($first, $second) + $end = [Math]::Max($first, $second) + $replacement = ' caf' + [char]0xe9 + ' ' + [char]::ConvertFromUtf32(0x1f980) + $token + $bold = $random.Next(2) -eq 1 + $italic = $random.Next(2) -eq 1 + } + $after = $before.Substring(0, $start) + $replacement + $before.Substring($end) + $html = [Security.SecurityElement]::Escape($after) + if ($command.edit) { + $weight = if ($bold) { 'bold' } else { 'normal' } + $style = if ($italic) { 'italic' } else { 'normal' } + $html = '' + $html + '' + } + $matches[0].InnerText = $html + Keep-Container $page $matches[0] + $updateStarted = [DateTime]::UtcNow.Ticks + $app.UpdatePageContent($page.OuterXml, [DateTime]::MinValue, 1, $false) + $updated = [DateTime]::UtcNow.Ticks + $events.WriteLine((@{ operation=$i; before=$before; token=$token; started_ticks=$started; + range=@($start, $end); replacement=$replacement; bold=$bold; italic=$italic; + update_started_ticks=$updateStarted; updated_ticks=$updated } | ConvertTo-Json -Compress)) + if ($i -eq 0) { [IO.File]::WriteAllText("$output\editing", 'editing') } + if ($command.sync_every -gt 0 -and (($i + 1) % $command.sync_every) -eq 0) { + $app.SyncHierarchy($section) + } + } + } finally { $events.Dispose() } +} diff --git a/corpus/conflict-page/native-restore/scripts/text.ps1 b/corpus/conflict-page/native-restore/scripts/text.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..75e9e71207b599d16eaaf68824ca30831860610a --- /dev/null +++ b/corpus/conflict-page/native-restore/scripts/text.ps1 @@ -0,0 +1,13 @@ +function Get-ParagraphText([string]$html) { + $plain = [regex]::Replace($html, '<[^>]*>', '') + $plain = [regex]::Replace($plain, '&#(?:x([0-9a-fA-F]+)|([0-9]+));', { + param($match) + $value = if ($match.Groups[1].Success) { + [Convert]::ToInt32($match.Groups[1].Value, 16) + } else { [int]$match.Groups[2].Value } + # The Win7 framework leaves supplementary numeric entities undecoded. + if ($value -gt 0xffff -and $value -le 0x10ffff) { [char]::ConvertFromUtf32($value) } + else { $match.Value } + }) + [Net.WebUtility]::HtmlDecode($plain) +} diff --git a/corpus/conflict-page/native-restore/server.jsonl b/corpus/conflict-page/native-restore/server.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..d5df993564a30ddaea1b427699648ff9b1a1417d --- /dev/null +++ b/corpus/conflict-page/native-restore/server.jsonl @@ -0,0 +1,2 @@ +{"command": "mkdir /srv/agent/m6-conflict", "exit": 0, "stdout": "", "stderr": ""} +{"command": "chmod u+w /srv/agent/m6-conflict/*", "exit": 0, "stdout": "", "stderr": ""} diff --git a/corpus/conflict-page/native-three/commands.jsonl b/corpus/conflict-page/native-three/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..bd094dd5fa92bff9cd29a895346604fd12f198af --- /dev/null +++ b/corpus/conflict-page/native-three/commands.jsonl @@ -0,0 +1,6 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-C6F6C14F -ExpectedPages -1 -Pdf -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\read -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Rename-Item C:\\one-tests\\runs\\capture\\read before-read\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-C6F6C14F -UseCurrentCache -ExpectedPages -1 -Pdf", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/conflict-page/native-three/conflicts.png b/corpus/conflict-page/native-three/conflicts.png new file mode 100644 index 0000000000000000000000000000000000000000..05b2a356d3b39f7a6660730662dc6aabbbb107f0 GIT binary patch literal 42280 zcma&NWmua_*EZa{EyYTa7ARJVw-BI$6ezHZOMzlZaEIa!MMAn$Tw5eKY}^6_OK=+8 zoghI96u06U_`<%Q_j{k?`2Kvql1%2BYppeFopY{PGhv_96)DIV$?n{_L!qoBr*-Gf zz2|rC+y#@|CA82!q2;-ACwEU-?t{*kwCyI+FHa`3)=DItS5rmz2K@F0MHd=R2cqcB z8avd@t=!YTxA$`H({w#@O?!&tTokX{he6aFzU?H*i-ba`Lm^c^0W^03VIAA>*OxPQ zl|O^*6aV_lg6>;XsdsTnL;cAvTwjaj4Bsq`Pbk(%7uTKB!oOSd@Eq+Kvms>*q>@p( z8|P7p2i$%oQ8uLl$dIrFe!tJd4(1}_Vr{9D+@qi7+tqJG48ihx4ijgE1h+bUB5tg9 zmu|#t7H>rTFgHn(do4pLmkZXNHQ(HBdL~#WbISn?s6R=eU@GbGggJ<*-gJ=qM&>H3 zkW}orXad;2yZg~+<;Oi_50jam#xAnPXE|{3Qfs1h-?ZmHxP8B2rm~&AG>04DbSkg_&a_Ll0T4>!ThgiCCYXZp82)^$W2Xj#3c%( zIwu2_C`|MAzOi@eZd5>^K7B%3gz7k}5-g|{&IP%vo zWcjf6nA+aK9%*~A(ha--+q+1M-A>wup+de|z}4xEbvSs#oJf+D4P4;eTvuvVVlp+6 zn82b6m3qB(eZfPMKCsdjc-?X6|$4WiDojjs64v;;ABzQl6FM6~)l zc<2~vgs2apC?cHH*b6L8vvU#qAMqGd4T97mC(~Bi?Y;HEtAX3*n@2<`=rAjsux1Vk za9*%VF!OqwU`3ORb?{M}cU7~f-Lbtr|h;n>R>&BRlwJ z?V+Q^B9;5vfVAJPwsxLXO{wha#q5p>gTOO1-U-TpO^?B(&6DB@@B zY7v8Cw&NGe)vw@iE)kzLIg3k*Bp)SvOP-W0o!C&gB4dt0&W)x-(}Q5h_pg&mJz4z) z7oHcK9(dFA-un#SeX9Gqp5iXQQ%x^%f?Jtsj9ct<@`WpOV#j?Cj=~%0A;%M--tJ%f z(LvITk~b0@tNwmpR-0_26t@PqA2s5fkKECCe4k*m18z#-BgbLONBY!4vB&R3ZFU=f z)*a3;3hz0W_T%V>Bb0g%_1J-1G#zf_C2Fbm(~Yi&r~#bR{Qf?i5{&?(WGrv4WzCyf z$}~yf!-%=eZPyu*7hoye3z%nJ4sw_$7PPK?+>LrLP;vm{Djgw*+qxIT$(D7zNUZqJ zy$gzgw}@odsS0G9w&a*jX?jI;!P_-(n})?&90T{P{@>&`Q&$O{15GZk6wEs_Sa5B!O+`LS`gH*5{5DHVT} zYDd}Xy=PM8l0nWav{?Q9ipOex;zzWe^cU|=NoS3QHP*+?=hi2`5D^233V4^JmN^kQ z65!m7f~G^~jllg9BelXf?mtB+1+|NCnoQ|mlpcj6+bRY*@(7wBf<^Q)TLNS;!8-=1 zy^AlP519JV$15}1Vtnog*wvl6rXgDcc_i~EB2|uC5B?0Zl4dB$X?p<<9w_DE>3LLe z^0+9r+_3P#`q8KNf1y{A?q-(-zQsi$7S3VX$$`v4d9iJZk7np1(!f7BK43Qcn}Z)g z?EYH)(XGNJ#HM}JnoQrtHLC;_v3|5pKl!8cSnA_=gp!g*Zc(0^vzYIkW@i0UWA*W8 z1=juO-PfN%hZYF7qP$W(Ug|K2&yZh>yGlceLFzJb8J%0M+Tb@Oz00V7p}A3bId)UC z#|LVu)&F6{A8vKE_OK$1(Tvi4mG@CrspeY;vmY%Jrl4ZejmK^X>?6&o>N@xp<$=4q zn`jyCY(F{%?KltA)-IRix^HhZUyr|hB<{$hi~J}FMhUl1e6yOF^>yRrEiQGnlm5Yb zUViFlcae2cBTI+@SGg7iK>0)`I(U_MGBw~yNAyS5;k{$Z)*8q^kv~|d$V1wXg@xEr z*hWRG#|gv1KU(#5F}*f{!D%pxh;7NMoSAMV`zYa5eS7!rRgngf(G8tI^_1oP)fT5h zLmhibw%5%HOPV;~cutwCG-+0WG3$j+p_xd5_-0?0^i=YR&GDyC1^qmqCPms*r=7ez zXXir%)X0R}cJKr?$ls%1`FWAu6QZV; z4z9~GZ5!c;#&yno^VA#TNM&S~1be+6z`SwX^>FL1Not%V*fgNq3zHY=q({;_{Jti) zR=oN8?}{nnHrS+UDesD#lOYFLi9x!ZSiIQt%A9~KJ9&Js?wHx$4SW4 zFZeFD*j$2Eq$~BbKFduPDSplvq~ZKW?&U=17n*8euLAwkgSuvBFX9re@n?r(4_g=v zO?rnJ+KDRMp z)&p~ftKnnDs-uP;Y_qUKAh5E$ zhp8-L8+-<6TSf772}vatp8jWJ=c#}?hCItvt@g0>ycm93?=_)QUfHYrDdhU1^u3kP z!(~P$`G_TX$8#&Iz=qu!^jh4uO%q#iE+#;hB#%Fyuo0y(p zF3fL0^wM<)SEW$a^N6FqzxkTwyG(9A%)AwCIr<$X_^45q1iMDO#-e$DOZwwu0&YV@ zo-;4?PrDOfyF{wEm-pe>6^p)5*J$~Zo~hsT1dvi3xPVclia3aW`Gr(v@WzmVeUT3K z!`#afUgjJ3`>N}9dR~?nj&7>}y8M2%myjA4J}U0R7>Cp>3KJ)g<5GrwYa*LnEPZYw z|Fc9^D#fcIFH+rp5$`&{=z4CrRn0M(AR;l57-IziL{&@B3UuL$NwC-5G2y0S@sTUS zWV?{kY`sefSDI}N&vjZIg#~QqvDr zJtyl5=)bP-%xSh@nsfwo^SKDVJSud_91r5=v~c|X6Jl`D?32?4Co4Z~0275`bZue#I~>s7&}SWp3WUKuem z;C0@yIGYpkO~rOMFG-T33dG})tX@cx?$Nx8iHR| z2U~8A8=M2YcHhLh{cky+ z6#^H-{w||VSuuV?P~G~|G}hGpvg|7aAzYirYzA^=5#INYC)e%7bf=iw1lO;+1dAWN zFh7%U`V+WPli#WzbkSS00tPbLVQYYs>J;)2 zb&;22#ox%CqN+2nxeV>gFKkvH+LcciU9;urGO&MC+8uf}w;fOhTN zcKS2wHJMUlGhQkN7HZ167K72fJtZoN05Jh}y7n0Jygu$p1@r&XPngRIxwX`N79g1Nxhcv)O{vTV2Yxt)mv2_w5G_ z_^v&=hIyQsX}ClivsWqW-}~f19$Hn)%>{8~n`Qen1*H5toVmtmzgb^qqiy%hLTNwb zH$E*Saf$J6pOhM0>AwcKgm$_`@(5A>kM*?tY%f-pH$sNjZ_~AcN-wM&5lxuU* z+?)?RsWbuT``@I=OQ$#eDX2o|{fX-*l1?uAU66d35cE;_u;ywi_K&ZTnbqMZ$;AXz zgEj{vM+zq+cM1>V^p;KdziM`JO8P8+-zJ6wuJ*U5rMr_n_k`@N1E9VAdm6fKd-1;x z;y>~)jKO(^sQMOC5ejL07kbkSAo21R* z{APtJWJpcw&mW{G`;glFYp;#@0lz*+az5B`z~++JskQpe$rGGq?#EuG+a{$4PXF+2bqdV;)wVm&$WbG>q>Y6RabpGZl07Lez15Q3 zCgr%iHFV&68L!UENaH3Ht<0`+e=YxRXm34J_Kr#A#Llu9)~f0iAwXm?^r;_7oy-xR z2JYjGN~kFw56B|_opnBHFJXTSbrw)|HT+Ee%|ZM_QgPs?Dl(JwrmcTQDxP3`_nL{u zT3$Pj?mR{$f5Fv;p`$Xt`2E>O?LT&+XW$7)bHUY;qGMQ~m$my#`_UdNntCJxC8VXA zblfi(MA@nny1&$*!m@}o&)?yi{-1;BjCf(kVv zXdzYl_~hksY?t2KlhvaN(HNFgK&(`m#w&Dg%ubvX@csN7Ylnr`8|>&v^*ssS?b1co z%W&2ND=u`TGNoW7h11hne}t9>Qbw~>>tx`&!Kf&;2+Dhnw=Boy_(?c%qtK&m~o`Z-_=Jx3Ed#6^=D~l2SW! z7lc;lQWumPUZQI`T6-e(YzeU`t^zz(W=d!*u3ffV6dar1IUG#-J!0YXhzq~^+ColO z1?E{yp1sP{S=d0{Is9>qsMb5Yq+RtmkTOzF)IT9doqX2@Qr*MFXHZhFu1bGX->g6G z=g$ffb?%YEj-YlDIYuvXqtpXHu87}AQzB7~mY0%CU=rX`>g0f8J+w1!^+M9m>6k*| zo#R(g7S&0m4nwO$ni1g3_w!kkOt$6}Q7a!#kc=0g;29l!ZUtM16B7+0hP$m0l zUP-X+bhzm;Yk4G%R2)hc_saIC8ZMdaSl5U}vj}%Q(`eW1^JiU= z&8|n?siVR3@jF|eaR(9TkcIblmToiMQaQnVEFec5>(keWA(e?H1@0I|&%@$Rijx<* zd$UJ_Nlb)i%|7O;|M=4kYj~?D^ifDds8ZWzOdeS+bq}BNpYkSwvY+^jw18LRnNR8* z0{gs|SM=tZhUw@J9Y}(st)#C3xc+ap)K7vwnd4hSP{M9k_n~ViG zM_6m8aI2@v&ONq`|20~>@#5lv;&?&BjIFQDOmn*mhwKu~276^=<~RQBsFN)mLcp)U zCy**z?k%rw@lhYaJntRm{u!P<2{pLRNW3EEMi1}wy67!)`r9mP{2{8rU>-@Y^g z54J>3*lbU>FDD(9M``XTZVZ!oo9E4)4I)#6OyyrZd%wwFv%HB}Y;z)Dc`6k`DhKGW z{!ZjN*Pq4e|R`kHNyv#;p5glF57X^lm~MS@Yx>mAA3H%0`j>=g}?!^4J64!bkY zhHH3rz!^#6Z?oN1RW3=;&%zFpD>}R_%Y9ITo6&+L<5;!Un9q ze&B_FY$b*K#%TqUt|mnuw&>c~Lp$YNz^tRw)TjkvU(%BNk9+ z0*X96iG6GXJ0mhA)M(`xy91m{F=b-+wL^QOi}WS^ffMUQs_6OgL8QT2vy|yf3WG+l zZ(aSw-L~4s_DeN>*8u2=$0dhx9uw=479mJEpT=3p*OWZ43UyIj?*uUfV=yu%KR<(d znE`E7iv;Rl|H8_+lRu7U_LWn13R9N`ne~C%WA3?kPNM_M+Qp-GC8cF_D-L=Z1VRKRBy8_krHC+E)0EakRutnO*e_=KmOk8aFF+%~gPK1+_v zinH}9-DA-qifXlx*>MS;$QFz3P#lPb-uU=WH`-~4lz&#l-9TOu7ny9~%*qk2)(p9- z#__1PKq?C@gSBv$d8^HF9ntv_EH7i?#+xFnLpwvc!w4%h#mC)QZ))*DjN4Ooj|1M7 z%1Qh(eFZr5gcV{R_x>gL#|PQ1tm8j=j-Jf&HMRvyl{4!J;DDXuths3&BMWx2&+Y;7 z*I7!cvjj`v0uG-^PQ8~Q8Oat00=y>vna$6-S_EN}w^h==<{cZ6Hf-MHzmck4d_3pK zcs;dtY;`J-;x`%DZ@@Fu=-AtjE9kF>Y$7D`2b(Qswj~M%nk_cd2p@I!Gil`!U6Eqm z5g#ANjP0hxdv$Ftv+$5Z@w?*Rbjszdl_PXz-q|WDV^%5jbcg>Vp=(Z@y`(i`YxA`8A&$v727=JH$Ql zBsKN8O^h7+%Kf_O?dn3R*xoz?J9)tOy0d0I^7w@0?BA_^HsNaR!F(`bM6EOC;Bu9( z&ilI3ufuqiui7Ul+!k}(AgiWf(DtP!0tYhMObrUx zDT*tD*&XMsVWX1Jla?Tg4;V)H&0&I=v>`ywrlW}*cx}4adr4<~tb%&@xIFuSdXF9aUIgo50C(m48)#|wjY;1D zwAp-45Qw;33HPK#7@q!hazP9VNBF()!^`qbN+va3FK&BMt{|YV{L(9*W6J?wz+shF zzj|RIl^qr@D2s5`P&WxxI}w385+8I=sD%4nt&!^Qrdh$GZt z4r{XJceOf;+3+ZT<0)^eo(8E+ zF_(5)W{nZ6KEvhw?VmE1%FM95jzVn#yA^h`LfYG=vT4qmbSVO65x)Q5l>5g=joVIsO6OZg z8)@fvKJuMH=w(6Ei%BqOGgEl51?AR>A&nD`vt5#CxXuTlLF* z(Y<49ne)KJ*qyP1R#ZLwa7|(nOhXQI+_IV;`?U?;RC@vRJmG#aJxd9rlBuVwGG)=b zatrNHq;Z#|7tVKf2s7@km38FXTE%eB(C?P5a@ifiqc-Z{e9}RoT8iUMa>gRZv$YN` zi-+Wb2r;<$B+s+M7~!z@P4CZ^FDz7~foVzrurPmO`QPKjEcOW_6(_9x1J|Zw8TYz) zOPGez8;&*#Tnn1_e{Xu?TnP2+4UdPgyq0UU!)(&iw(t)h+5^Vu1=T0%-RTiW9!-UE z)AFEgLWsk)A?c>G zB(UDJTk~?sR5aH|^ZQ%r-ij@6{XI0AH-b!cg^hMs0nm3FTQ}F5?^FGSu25e@ZR-su zee3dHFM)ME2{kp8kWdDVo#$%*`B=eFrnslTd^H-Xzc7XJode#QTAcoysi&sMc9?O| z=3NDjP~!Dw9?h{VJfxAfIBr53Ps>*j&h6T;P{0zV0H!?_FQt_!*P$xhXqz3=PjLf#|+4sSy6 zeWs2GC0>tuUT-P}2=u114Q+m~wYZ*QGu4iIR?zF&X^C^T^ldde8ywY<`1a7znHG`A zFY|daUQi+$h{|o&J3Fe?AiNL6Z3G$7oOUxtP-|kr!6Zi8#T*sx$Ur0fe)+eXvjXYU zpQ*+qK%X|7GO>e$9c5n10Zlk-%HsgL!DVGM<-#!Xij7U7T&fIScRazd(wp+C#n`xv zF6r$RR_X6wt2kNv*w|;8P7OU?_V;E}8CBGVD}VvWJR@dngMVs{9kzY^+8aHJ5WiG#GM(R7FHoJQqfA4k%sq=V6LfTl$_rH|YP#d@H#pX}&!{^&=-@A%a?73!aZ6@smX}>! zsN%*;VMI<)zb5U!#bOpNPTSyW<11}~a~V(ku{A&7I-(G6X8Sp~(J=*2YU+^OK&WQU zM3Cq_W0t_f^UGJ-3|V@u6>hGCfDPkON86H!ebpsmx3Avv2Ho%bvoCtsZKQ)C+YuDN zxY-(?ZJgcjd%x1m0g&nRytvK|Q+ZaI%w8u6I8BRKJORnAaZSBjy5^tOg^7B?ATbgMJnEHLn zBJFpxW;x>wNy_4AsZsAv*Dr?x7R(6<)U%!6+gNXl+Ub!01pMZn>{A=FK0Gumw<7(qj9Rku+jx;5$ zx!C!-aQyq$`CNDM3VT=&Jk35(4wCVovbcdE52Z@Fha(IG7!n;pn{agFoan~>%iyMV zFd$k-$REh!0e~7ASS)V@=GHOXiv7+!{kKy_PYx@5G=@>Z31M1apURDCAcdDEK2_Y! zCWYI65*XEQ`)Tq!v_B+gfJ;9!s%p|6=?|6_W8vdjEXLn~)#^yB%(Ed{FWi}ahe@3-}Q}DA+Z{_z4+%xDe z5&5q$4Sv7pJ@pO^qdL!D>X?WV=N!i+$7u9zqLzZVxb{D1zSQ#P0vmpAr1_~gX z)`d~+vFWR^FZH4d4y9)GjK;J;-n?wK4)2W&LI^9q($RCUWZ3fdiJZlC z<0`1PfGDxFR|8?>3BJ$wTwVvmTipEAnH$f$@ppeMdb1;b!l>Bp6LU#raS;kNAad{K ztbA#oQbUU48T9gOFFoA0s9ajtU^3PKnTM*VnKV(6A=!7JLTrTw7AL5DLdxuM_1xe3 zJkfD>u!w^=`}uh`8XSA6e2YHDgh=r5GTGbdt1wiOHA;c z8!vHZcpm$!TR?jE00S$dl1J*Rr|%pKWxlrv=LLuG2^dPhENcQ6y~B1{twibS@{@+G zf+r{juyG#=Y{;jwlDzr;I$#N(ll(C@0_54{H(4~3j~dfwG{BW=5^a4t58C4G5T@tb zqU{pb#D3D7PWwKNMHNNs>P>$3D<)<8T|cly>l5g~mD10tx6=#srFm8pa7-~b?53i8 z*mJQeBJ8PFnqLU>{Ac^>7;Z5^A81iJ_s3P)7UJWhEu&>Lr@!nx*n=|!YCB6#{7d8H zgrcZb;&Rb^aUjnHD0QHEQOvPSamthQ15p%Kq z*i)Y`0=0ud26mDp|5tLh*K;X|?Ylqf74lko7oLw7`(Q>V z4l98J#*J>+R@&sl#DOHVkcB$7dU{VSy=E$YhnUfu9_lM;0~*;_rj1esmF`7PcKEgu z`0*FEr4%L0Fqfna_r%-rA5$X;^RuLHfEza+`=XLtzi6r3q-0=aWFKUbo7ae{L*$>! zdB!Q(>RKpUeN%FkhuG@M>ZxhVJG$!{*{jK>wrp5J-2E&{-t-mMNV*&x)tywW4W%_u zy@T%0o~thD8*4A<7)<7#=!Z$}iU+6<@O0f}mxvgA;I1&x)jVb=_qQ=9&6p0cyjETH zfrRb1ev}uHj$r6!hh_tiMlUvZS~rFi=mOW3wNk@}_39Ijtk~r>w~@u5pdP<LDS{X_bpuCz9VG8tg9hF_IC(8aYwY8%?C~o8NX104g z9Z;xiiTgl!_BUgUZ;!ShwAi^QKhKU1#swk_Fzi-Le1EGc;e2JnlZV${wXY;xrMLar zfETR}pM3G7YKOUnMk|xC)ut%+)Dw~0%GRXactm>bW|)0u?cL&huClOq@Lc85`f}fo z1Uiur4`P+pbUgxxaf6GE_>F$3-ew}yx>X+0L8d)V(oKakw;3>GGrD=@wcEoZ7JAb} zcBct5-dyt1l7n#`y$X7gQD96{B(M>(X};B?{C zlX2(i$y_hOPPFu6g2%(O$&dxW)szYVD&kdcYMa6N#RAyRG{`u=w=duPN3e0Myb0C31*PY_ z=%Z;65le_{^53@U)Iso+;p-wY-rP7_{9+pFT8pRQGB(OGfV7?_ft^F}xzX2L!ciRWeFb7hLOJ4X5xt!&@=<1YI}En9OsV05kT!#n}1I z@r1sdr$V&xpjoz!F~!NpUs?i&_DF7Om!rDfj!e`&+jh5HH=2vBXB3x&7H zm5#){@b5hqE?F@uU&=%=v9D50OQ*Hi@7uTW)b!erQlc)Z;%FN-NkSE&#%_v9i0#(+ zkOzxkV^w-0RZ+vF3-oLmDK@`}LICDjjxynYj+ z&#GA{?;HrE5S(JzZXPvTcMW?l-1vO-Z|Y4nvPh*xV(W8G8A?wC_kc)~Yc!p%>AE{* zcu{flwaCx9>p#h_u{k&PpFam*@8zsCPRBGyx~hGcy4KAuiA3$9mM%kKUyB0*IX{s!i`${F!?19 z93R}_66@&uM*MOVjI*|vM+pb2Hw>J|T#dLqL7WY>ETd3R1YnQO-M;CFzVpmP`%t1+ zzrXoE0?^P%8`&10Tu00smB0VUj9Yk%m4w{{MYRLK67~dUH3R2v?9EKv7VIO5-?-1m zr!kel*ra~Ek$P11lJp5;cy+Jki;dO-dVbFlvN6{v=6{shrr@1v8in`VY~hc2NWFR^ zTvHV{Yecj)#`w+1Hrlh&GJn}~@PZx-X;j8H{JORQ-FPpY+_hLX^P+!`zjTW>Z28>QV>HRP)jITzoiiq5lx zV?y#9CwRQE>DFD8>;qTxBOU*$O%*z+{V=c}(o~q(eJlKtLq@M-C<_-Kn9qOO1 zZ|=+Mg$Zw31*?ZH^O>LWf(2>vC%K|Q)4Q*7ntQ`&5l@*`1Ioltv%BU|FJO7bz_zAG zM%0MffIjh;;7)8O*nsjX$W?r3pA7hMukamFAAYKI??0#MUMuuKdQnVo6*c1LO7vgP zO*z0mYYNie$JaXW)_lEwB^sZRx>4oot9%w>&35zb;e6_W%f z{mZL;6D&Lb^k|9c+qHPD#QnZ^#3c(Bd)&T{;Np9|pFy%Z4P-yc^gn0Gpu9wjLAt>5;`_T#bav=wg#ut!2FUg z(#wktsN9h2)_0B3C$fRjaX9l!S+k@z4c1YPeTxMNT{gd)4v98vYdCKMn+#-y%}6_(U@ml1Z%4(_3~*}VF_iJD z{j0Qhh$+{Bb|5!q6qf+?zxfJPYA@>zz8^1)$6snyjb4j&o`dT*Lhv4wq(;(?guT@T zkt19s$2(SS>7F^mglj2kua=)#GuG1%4IJ;M-!@-(PS{nwkU4&O1FoHL*4B6kEmDgL zV+X&EpR5^sry(+%>q4=5$c5O9PzH7G9`D94mEDAmshr37C#U6{*g1u zsBiU!&2XNrLb(5$)B9Q;?E%6G%U;_514SoMZbn-3C{T)IP~eKieXi5dHRl#qKIo~` zDA9kdCek7oES($eEZ@h?-+@ugmVdn3$tst6&Et}@GX$lqn(_R4Q!1ms0-UXTVo8T*V0Bj{IsoG6!8;QT_mI&*o=_3l-WCpF?>jww;=9H{w?4Mxl1<%W&+ zQN0CYfwrez&ybAW1hg6xhoB99T*M2ojE1e0{`g?f6+U!Z`x#!2_L}g-Y@ zvU@Jvkr`|F#l$Vk1p)bXHFQcZeGwaXxcgCERr!QIKbIm_7Fm5W`%cZ_=nH;s=Bb8o zvo{TH!CpPy&(2zM_m#kOPf~%1ALpYzxRYb4_k!OX0DNLoBLOv=BjE4Q?KO(ig!2A9 z09c<@%ks&Pkb1#GlFbLi(37v8&xh2J`A3vdw7ka+l+-DwFZ=@aC6CVbvXxS8oX0*T zC1k7F4ymdQDyf}ZJ|MdKcK7mvUMfC~YrHpcX(3T7DIrbdMcpa1jgYZ?m*Z5xka-ez z9B5x0)sewJct=`zZGJUs{~p=53!~j;(q6~i*g!lvVx8h-hnyjn0Pl?nw0aJE$$?i4r zsk%DO**x))aX|siJT*(k&3eaclG}XlMgB0#Y9I|y8kV^o?!TEePYYdI8 zcuKOcRL9;DCVQkJQe3A?MfGG)^_Y047yVLbuh_$z*~`kE6=A%Gal0sNnS_A| zTNjj_^2l~tcbk!Ejc6j@xo1k_O3%UUJQ{$0-?w^&34(%pH6`y9AF%A2o3CTuKpE`L z=(tx6J}8`Dk0>!rmMdeSQAgSrdm67GJJlQN3nwN(J3ra-r!%GvSp%Zo#ZUL{S~WB% z#icdtrclX*`cWb5Ig~dao(99U2`RfPIkp(u(DGI`a78gD@!5Mfhtpp1j;T$wJ0*m~ z;w;u-uE_lgXiKoN*9T--l!rVr5^Wo55$k7jzeLqF`LmV8W4>|O{H{Ln}?@CKEer8oDXcHTekJlM*C^{me2FAi+n64`C|@RzvEr z&r|LdA9C$B8#== zEi{#|3Kl!xniDmwD0TFT9vUMkO}LF&yX#7eFw)@U2~v6{QiRUq{GjJyGRmR>5U+6? zK{Aa8)r!)4n76&~qW#Rm9xEy(w)X8sG?#JZ{UMCXcKWQ}B|pWTJHwan$lyz?%BNA0 zS7+_rXF}PEjzy}e?YdzvuBP~mK3DyJ*oOOo=rJK*_Yg?EABY`&IncBZNkGJ+_|$3 z*(OrmXm%S8H64PhO#Otx4%WKH6GX4{-hS&Qu|Vd|TU1+FEjC~7DQS5Xm|*vm9K$>X zH$NMFzoZuvdgK3OQCRfKCn;c31Mo)I+~Kgh3t*8$jqzOxSY`k}ng3Dry8h`!*{w%2a~0((}?5Y2B--1p>Oz=372IU)!YxG&}>Wn*Qg`9nJ@en959#ymh5+UUv5+ zSai02(5u+*&=>jHas2d{(ePw=Tacm@-N?&BV+V=MZM1HxJzce%87e=a`@y)a5}pe7A}cciZla%paM`jG`a!W7nI zsIEPH`4B^7?`kUxXcAelwL?*is1+{+HMzz%zRhIfpe6JBv z-z&qX1*X%^OgO5n!HRgs*$|Qm^Lh;Cau?cPbISJ{?8ET#L;0Jj1gJ@S8Qp5FZmT0B zLFUG290Yk};h1702NVzc@ger%*dk#@o}v5zDwJ^By=d7$6~UA(PH%iQKTdhKQ>B`& zn2uHr`A~E${Jc|N%>J;lv)~MZAMi>jotkJo_1%>M_d3o(@lIf5=X-3-Gr*lYU*&$L z&n|xi?S0Era79!T2(J!wxXeFQ=CI6~Kg=A&*WmgH*wQs`^@xrzZ|r{|mp?k-yX+&pZw09pM{qmG=1^nc;==B~R(@Wea!Rm=VdM#3(im`M{3X06|vREdfNF4v>KXOir= z-n>-ec54z67?(WU$xi*tffw3F_XdePrt(0j<9J9ZuP|u<+>z7_+1uNN9IysyYQ9j#8`;mUiy( z;ZfrS|K5cuS(vVlVZd+j#@`%WUOVzgLdU;+u@2?>O-+w(CA_2b^mvWHA8%!IpS6;il!jveWW-KzHW$!Fi}LKud(J^a@Y zoTs12%Vukv6JYouW|$6Pl~ytlZacIY!Mg)Fw`$5?C+e$f?M4BWANKw5i*tzKt9&A$ z%{p%^p#^CJtjvA5RmDj zq15A+9wP|5-NAWjU&CGt^-S?HO;paM%OkTzN5ASKCaAHLXc9g2k+ADxA)6{A* z^^u;@;{Xr4*mH1e-w!r|3wjcpIXJwx2qZ9&TqwdA?&>mlYRVj;Ysdf%4vZ6hmr6|M)7{(2yZgr73{dl8SgMv%N4f@{f7)& z=e`ym9V?QG8}S&Fv3^)6(W1e>PL8%ns&llbnela}j~fZ~7!p_Ssw~B3 zNwtxcJm`>+dpd7n$(~qhs#j7eEzrR6rqs1Y86?gDZh?56fdm^XKaa4RX*zxgRJFCs zqem139ENus>z#YSC{Lc?I zH#aGmL}m|<1O8P(ChQ~S=xGHwUydRCDf#-|{k*8oAdmveMPQkeSwu8`(Ou!#uCAh}gj9TreWSGS30F z26Y(Sp;6%BsgvGE3$f`In@&tj=n<*DukqN>QbPuu8ZzGYyfaCfPL0UZK21nWEP?6L zw*!%M{Q9}1Sdi|X)OoAKt)O=d4-K!jBgE>umgx{&+9za~6e&pkD_&h>{>Nm(b#a34 zg`4hNPB>?>2-KL?pwXB33E^upD1?hmORmw#9zU@(i9|#F?%XL8EM~eFL9Qaf0nW?I zlPT806ql8uXc0l=bcReY&*ROBl$qk6=Wy~w4H|Z%K_iD!1n=e1n1NmsTL;zE zZTX@JEz#@fFU2Yo(g0Z<8M%vMq^)jCEo*WzNbGcR0mpESl9$LoX>nA zembZD|G&EX_RuxV)V|%yx$?uJ>!zHCbA}lBJw&ob!KiF%I<&4Ynl%Q;W9!s!_AppA z?^x<1OIhg#I6bdwZ{|`nW@t5fypPgf?zc__S#A~g>m?j>J{kw91IMA!My5*@yI`Oy zFU6cyWeNA)LOw*g`DOH?&z1p3Y}9&MT|!@AAqT^o{R7$$e}YdN5Lk5mG`~jPc5~-( zdUMRJgfUv%(F?f<{GNWXG3iwqjkTLK0Bed47;c@zhUt75$$i|we#*glk+@EwQ4KNb zco^sXRxI#*yR|)%9?f1KZ1u-i91-ijsH^nQ((oIsYl%kCqp6td{7}(zk%OB9uk(3m zLU^+Y)00F^H~)aOlj

qPY77 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-three/expand.png b/corpus/conflict-page/native-three/expand.png new file mode 100644 index 0000000000000000000000000000000000000000..2996fb0cf1da7b204973c3ba4e08a9c4c6f4df53 GIT binary patch literal 43873 zcmY&`x)tL&Mm&kae<-B_@$IiI829b%q(s7zTrEWnafS zBV^x~F$`vY)APQ6{NDS|d(P*~b*^)L&-q^Gd+xaBhT4o*xvyThaDh?hnWo8w3pBSc zT(}6la*_ImosC`O!UgC(9nHrtf^s&S>HJ@;d@F_e%{6Z{h0cZT{!i>aC>d-4`Nf3no-g9=|{4$)aRzwqUH3!Z9zdnM>ZkE z4Q6KMLeJZXtFl*#&5a`GU);oZEYWXT7o7JJ)SBHK))TLNAs@UeqJ)yWDRWJo6hHhA ziW;~Ll090)IEGbVXJg}4^!KN5(nT#qADiiaAn@uL^w#>lPVrd%{x)t$ENkLZ%JQ&2 zOL0iF@8Xyf7g>8P994eP%xuSpObY;*3jJ}VuBat@X%3GO4 z%77Usb6s7&;+CQ~#@&=*d3o7&`xoq9*{d>;;!L>NJ?~f$5mMFiy>`}HR2f>1I|;sg zIrwr>r*j(nI3ZC|z=cC#NzBJW?8iOlP*>d$*A!GvbWunL+X1(xd67$?SbS#lK`7^5 zR=I>wh)1b=yvTgot@Z^6@O3J%BlE>9DFQK0?T3fQlw$0vp>GDWSC?w-RaB75fQA2 zNVPrSezJ2*Q&V#@kN>Vg*m@>J_MUKb={vmnC9_j97(B7<8;3@l;TtwN`HRRZC!=-- z+plxg8Y#0GJ59vx=AFbQN+gsMZle;Ub*U?dqGTme5Yja&?Y#cbs9Vu1-o@K6&(d6g zP4>?UXV#bTSxi7E0BJS*sGc(P(M!3`YpO0}+4el&WLmRaJ)YpvFO0U=+;o?DSSU`5Ym!c3%*S?4%w&iFMawn=JPb>4|G143RZNj9%_cAzgcmB{#4z)&QjQ0rtb; z2lo7m$*d~E>U?1!7bm?6a;}34-zzOp(Lk?zyN|f?_<9*n%y>>20~Q6O1=}B- zcWkcE5>}J3?BnM*NIK;+p&Bmv>w95`>Pe2^lIKshF6yOfj7`8f(j0W}ipP1lyV(U3 zQ{XLROGVj?xs$!~MUF7=;aRA5KSru-JN5KXU~uyBqAg~t-^0$v1CJHfbvlCNiE

fYvUByQnQ2M4+0U*K$f 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 ZC3Ot&#VD)oVC-A5RF$+8q4MB}{{!u)C3ye< literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-three/machine.json b/corpus/conflict-page/native-three/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..45055b0c57355c25e302d24c46f2f498763ea030 --- /dev/null +++ b/corpus/conflict-page/native-three/machine.json @@ -0,0 +1 @@ +{"name": "m6-c6f6c14f", "hostname": "ONE-M6-C6F6C14F"} diff --git a/corpus/conflict-page/native-three/notebook/Open Notebook.onetoc2 b/corpus/conflict-page/native-three/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..c4a2b9c6013a262a0c6d4d71d2a50d67c98131e8 GIT binary patch literal 3780 zcmZ3e|KIuM`!b(-j|5(YE0x9HIZ)fK;l9;(Rf9>GM|aLa1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypOOaMy5#Lq^n7yE(@{P3TVnbt9u~RPP2p!m2TQnE)!!ULg zqoSiBFyup^frVj;EQ8EW83vJTAiWPr3jjUE!l^t}%YCKKjvobBs1tS=w&Ed251(;5op;3GSGXManoVZL)|d{3jq~^01wbH z=ne!KCg8v@p#W+X*mR&6xWI#!dFbW|gA4-#jtM}0&~h>W$j4?j3l2N6nGcF5kf%WI z1(oF>dq6bEjmx&xPdNgT1OcF1f#To*00kNoINm{N38Z?Y0s^8S+S^Lffzeoki>jpIRMf;pqaXb6mk05k-UvkI&X1m$~CAO>YP&AA^kph`!n(GVC7fe{b_0E_NB AaR2}S literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-three/notebook/synthetic.one b/corpus/conflict-page/native-three/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..1db8bd91ac5b4eb9c3623ca701e9773b66d6010e GIT binary patch literal 46416 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 Ai~s-t literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-three/run.json b/corpus/conflict-page/native-three/run.json new file mode 100644 index 0000000000000000000000000000000000000000..ee5c95acf2fb14729ddaece26555e5eb5face4bb --- /dev/null +++ b/corpus/conflict-page/native-three/run.json @@ -0,0 +1,17 @@ +{ + "notebook": "/Users/clo/dev/one/evidence/m8/conflict-abrupt-01/conflict-closed/notebook", + "expected_pages": -1, + "author": null, + "author_timeout_seconds": 600, + "inspect": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "086789e467779add0fc071ad8c524c2b02d3d0d609aea4a41f3128cadba371e8" + } +} diff --git a/corpus/conflict-page/native-three/source.json b/corpus/conflict-page/native-three/source.json new file mode 100644 index 0000000000000000000000000000000000000000..e6bfda307ffe01e66ae1cd42a2465e9173d9ca69 --- /dev/null +++ b/corpus/conflict-page/native-three/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 3780, + "sha256": "f21c6d4eefc25ce78aee0420fdee0b409da52fdf3119b66302ac6ca4690c55ae", + "mtime_ns": 1788733155312083279 + }, + { + "path": "synthetic.one", + "bytes": 46416, + "sha256": "7ac7a42932848380b90aa478800ab141fce4fb591aae2bf1e167f1e516e5edc0", + "mtime_ns": 1788733155311971612 + } +] diff --git a/corpus/conflict-page/native-three/teardown.json b/corpus/conflict-page/native-three/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native-three/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native/a/a-conflict.json b/corpus/conflict-page/native/a/a-conflict.json new file mode 100644 index 0000000000000000000000000000000000000000..2fd05a4a7938577ce0347213e7f3ed53dab2c0a4 --- /dev/null +++ b/corpus/conflict-page/native/a/a-conflict.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Client A conflicting edit. - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native/a/a-conflict.png b/corpus/conflict-page/native/a/a-conflict.png new file mode 100644 index 0000000000000000000000000000000000000000..779f4bd296c5f9cf5aa32b4c8b81d4d7704a8095 GIT binary patch literal 47134 zcmaI7WmH>R)HZtBLb2iniWj%w?nR1A@gRZV#jUuVhT`rH#R~)oUc5nyyL)kmLU6rl z&->nczdzp?86zWm?Cd?)lxNLn&Nah7sL5kvl41Vy&p+6T3bLC2{PW`NKmR-jqCZDo zA$m>3^v^$yTZ*z$+Fn_QZ5UpJdigsCqE{;?BiT((j0mgkHofuJ{&b{?&Y+nu2F2Jy<%l2k^4?&7v3l&zF7c+p4r>sDM{u^p(zvC%{8N;ap8e zv1majL8-ywDR7lKaBHGet3;`0zgJkEp=#x`6p9oc@*~^jb#aMHiw=2<{E)qb1poO) z&v$`Bbka4v_gglacL=Yz~O6H`mBrLFn-t@#bj zmDPDo1?75qT=*<9(Na9JQJrq8Ya&{B4r++?Xp89YFV}TWy%|{xxj;E9U)03viB2o# zRXU?3+|D7x4mlKml_h7}>T}DXXQ55d(md{q*#3p9XL!6+)^?|ryyuv{SGn@3vG3Cr zWUm)XGBM6l3HE6Cdj1Oreppqk&QIA5MObqR;M)wm{HGQ}UN&*hd5(#O1~ZLDyB6YR&!Vn44+&}M5Qd##4^*Og+5 z+G;a*rz#8^QEAu=7ex5ydwzyFKpW#N`}qZAFj9^=!yC~YK@Lp_t$LPYT(YK|F>Hr?94mpgOz~yd76$)C9!5KK49%}LopXX@gG}|H( zztKm&c(X#7#h^%wZ!K@_Xtso&k+$po?kV&fn)HIDyRwF-miH4WhA%-4Zw=N6Ngrhz zCB*WRG%RXc0W)Q@UrNqQNf7lq10#$zf5rmvJG-gW;%f;#>%aPAX7Xq+{QY0>);V=> zj7_+TW>)NtpKe4DD(oi-eSRO$>ks;EF)uRRFu+w++^1O*JMPK|^8A3#vl&kaFvH21 z9etAwj?PeOZT3aG6eqhvj7n7whNv0f4!VY8Qh6t^V18ESUboK zxG!ZY$xV~n?2wOBO*0ZOo~LN(@!BG53F*%%xM+S(rzh*T-JKX#5&2qq#O}5I$hPyy znt5n!wusAqUmo4vLUKY=Ka%Q|u))p8y zth(SU((J=Ozx?yJgG#mv%HXC3e=;6?f0fsfoSVsZMJJr)wv;H7>Q3UYIuQDB;Tdl7 zb9J}1=@6GqGMg(FU02yal%-)$k`TUcZFXG9ZEQKqg|VVnD)#h=+GFo8ogHL&uXqoESa0*n&blP^!e4}&m>lMY&} zV-Y*Ko%Ww5NLN0Dg%(;6x^oj&sh3n4;%GCsn3`@5{o6;dT~ZwSW4R#1O4RS8S`OOI zmGU$U{Ok#ZAWt7ph+e*MTRYNNEvX%?c)kUT(&hA~^1kvKnl3y8T-Q&>ZY{s_9+fNow* zS(7JvDy7^LQQrKpmLIRJzyf*H3l>zkm~-0FRn@S!*(Ronvj2e8@!gMFnXBbBf3)u1 z2SJwgXPA%2&&t2ew`QQSZ>H`{58285C}vg*et^jP&$}^e-?(Ck-Q9C+~HpavT0-r@O&?EzrO8>XaAqmX(08 z#W2q)Tx|cIV?opsHL^cCbBc$hc+DmFj4~3C$iFYh1etF1eZ>y+naoCbl1LJ~5f(p3 z+rTY<`hIp0zKSAIe;p~`{;-=@71;V<-cxCdgym#(UFyWapeg3Lucw$TAs8;jtz=p` zpE22h5U$t+vpVpFA|twjoI^8l3S~G&f+qh=lW7c|X7~2Y zRGfE8U&njukgU{AIVMBck9^0(Z$HUIORh>LJBeL<)T()h8yx#tU@vBA^U{X-O}I=QyxE2Xjyb`rQ}wIi4SN;^4R>)Gyo zy*SNT*>2vNc!aeo_XD4AlU}8Rb`kGO z_ZaP?;Dc7aB~W~0E2^3QE&U~^?H8ypG~S*ssN-k;Yk@1~d3*S?vdS+dSos?Sxp;eL zm*^?=YCTLnHFV=P8lF=LJ1HymI!cE^PXnAxGbNTIu+GSVL%#_$hH?7vg^EPvGQ$HUEtQ5z5Dp`dHKZ7t*bVO40tUXsp?wH}Xr=6pMBTMOsU$e@&xa|(D1+guB zPo-Zyf8Jjl-R-pB;o(E6F&mRUKrV7S`xsSTzKrgs;lMREo#hgCuAz|_{U(?;2aD+e zDH7z^y+sD#qKdAZhogt%T8~*U&j7^o^#xdQ=7-wP)T8co^c1jLf1;M*?2iQ^{f4U_ z_*YoV@Hj?eb(2O+VXWY(Eq0yeC~a?3#b@_r-N}gALJlyQjazI6UuLbtyG_*aip@oU zhVjkN98tr|1%rvX<$3R^iJg=I)Z59zsVa}Gz@F5^da86@==mX6y&_4<#{^ji^nBJN z@Nz2y$!(Ya$Z>vAMPf}BxmbZ&fcrQ;s3RZ|!(1D}Z_Ap8V%OVNt&KZ_IvG9LxJr(g zb;`kLcH2^OmtJ1!C~0?jS|Yvj$N&9n6&XVt!NWa;z+Fifl!F>>E4Rb1r)6I!%6_U9 zcPLe*wUX@%U>Cw}%?0Y}Quhblc-(kCkq$6~Q*9CLGIXJ*d__~Pd3jJmsdEm-5J(jj zjxvW=%Bf8}AD-3Tp6%>IUvZCYd3WLL+@Qk_v>?$%>?YQTAmWHST$M?;BI}})6Zp%k zx|c$*hT^DCzSFCReQ!PEIK;GOyVb8fT>e}@oNMUDG3FnAnH6|C7+w+Q8(}dD^Ak-6 z-Q7my;M_1`*vjDPgs(vP&?5gLibykQ0YUeBNY5K0!Wux+PpejuX5WoRb--c}|6MAA zL=C%XIWG!rNkPs9VL}JOu6j#Me2t%3_gMLu=QA^YW{_Iz0*D%n$`&3v< z3D=oJgIzceO-(|xV=##;$#oC$t{1LLiWV^pb?73I-^qz}`}p*^xKf`}NPVhZKnB>8 zz&GA8?@-BjIE$E7Z(=e2=WB+e*v;2*B2GhV3Q4JAydvQX_Mt60E?z$%uh)En3${-* z{2bZYnU0j>*}AG^?`1R1D+bQA2P8ZPI9Ugk-{-?rc)Lf@A>UPrISn}*1Z!O9$QenL zU=2@_ne)QHpBuA2uD6C03kL+`Y#utPNGvdRH+AE_=^uhG{OTcu?xqc=M(fd+$4pIk z6ZY~beA6+G<~FjP%K+Iqxv`boau@+@9UU6=Cn!;r*EZkw?P9ubSp~n8!n4@yEiWkd zTzra&ZyrzNO9H!J@a$24rO1D(i3iasOknH}=4>FMy1xOsAaZARzbG?& zO?adP@l+gRhuf>Lu)s+4r`g}IK#bMvQPaF97QlI~071Q(w1DZv^yI9hb5l!6EQn6cQpR^8 z{1zLnEak9SR+_l4%A%-~k&ez5nde6Bz79shV#8a@>qTTn&hzQi_T=y{t3r|Ddxe7a zAHmu{iXr!hY9TL1=^0uJ{>_ik^Gy$lp);3i0_+Y-Ybg|$8?t}zA+7lUsrMdlzoIty zeffZ}-)u)lRZsuom2#+FNc1o$OqxtCL-4Q!CC~jWcFx(qb%BEHo|jE{yLADE?0$!@^ms-+dY%5!fUp zFRc>%+Sj`Gmv2H5b_`frkosPy-fbp2Ix5^`mG7gs&bioYu~j7`Gl{`m)XG$b9pj-R z=GvWpGu$1TSSg{MR2xZGrfl=?rWJ#4bsT5u|GWwh@YggUY&|*^vK7TIb8;y2b15rx zDV3RwPN)lWZ*{i!DQ|j`U&0xaz(=>{MpGO3=bdy@O19!ULSO6&l$<)N;`$BxLA*3bI|PAg{?J_> z0F6hzmm!8mxpy`F%LHf2WbZoj@r>jv)uDOmc5@9#z1u`Xm`QL&?swfRcfuKHCSHvr z?Sq?i-$FROU0;3``Osa%vgpUT2y0I;mwwSSKQ#fjjDM= z*58&GhcuZK>_5$>KvybCjv_S6uf>8+8{(rYfBX=cNua!0s$hmMJhoJjGlf*18qHhGY@>oRmpq_V?5&zGuU)<%o@nzq}+Ne7hTo{on`_x>SUjSi&$&ughBIbxD?TXmA(nPDJzwrdD zo{Cg!qo>S=0`gGVc0X^eMk})NUQqa(p3-FmdAoeMm|F=s~SsloHWtq8XQ0%_@?X*`B$gpdNqQ<_T6O+86<4U zlkJVoYvTbLM*nSL{Iz(qiFtVEA)D7E+3zzCOY&UXN_U`Y!!tdmrHwYl+>}QdW1B0l z`l^_6iH6eZu!m*ZI4zU}#-M*!Gh8?Jdj=M5A+jHG235+kP*(nOL_aFg2-m%E(Gf+h zQt2;Avn|v&f`Vl8XL*-9*FH)%4iDAb@!?A0eZjQTj?#8XC2GhZOA}rcYh1R~lS--X zyf%siX+VksZxJdUGF9oaFsjI9E)RdM>MRrJw$(-3=1$o!Up?+i8TXI+BGr&%6Dpw) z^pxZX&Qx|YkxFAD3ZWPRsJSfcC&yx{1MAb-UGPd~%*;iWAIHb77i>dqT2~dt#(*^sAUj+|Iv99LjJClAPGSeG%;ikT(%G?m&qeh|7 zdPy0fU$nLfMGviIMnpd^g(!&<9JVC}sDgFwLN1{^ru@OP1X7}<1+^4|i?YDrnXOJa z&6b8&gjgfEi>%7ZP{aOYin2>Lp9*NsrrfX$9*RwE6h@74&(W~v+yE`GwVloClBnb) zLXk=xW)*#7wP;?QXD$eR?AhnP;?XB`%@wvg#>Qz1IEamLNoL7u4q z*zb#seByTOUq4mupl-BS^RgWtXIVvyDN(f>SZCP4O>(1$`W;W`nCr%q%}ii4<@Vbm zVxXd+uDUOL;e1hiv3wJGwWU+O@T2eRRY^w9;E1TMie&p7u$S@@eSd`!Su?h-c=AHa zgmwjuM7hd$NRK|}$C93k#2vIRMUFm;OM_dQSan~2zc#_ElYP7po;qsuA+q)x%gzXW zi!Lqy3n*|@7EMMgX4-zLBjn`FrS7zeEE=Ij3d&xOq&cnoHGGL(gk84tAEYf&*W`?Z zpv_cv1PI0GuHngSR7Q`nr;6R>F>0W* zWt3Vld%o(yZW2S}w>|yA6fd+2Dw0-H3LW*`xwwlJJHaR#{o2Q8H7)$-$3rbv2GSX_ z$bh(txoX78R>DTL`{?&R&s%#pfbR4<^804oa9F}Q(I8Ki=h)A@rbp;R^`+7o`-eHT zZJ*rBikpRwxAOqON=JHYiVy4eV--r4hWWp5Db_EsE}1`ei2m{NZi<=esW}~T{(kY? zzQlL1H2tH;`(w5aZ*S!WXU3+Eg9nFu!o_^X5C@7{jv-&r+?@NG`D~(W6x}?Ygd?c? z+8rO)G0e+Z{HDpK^EM2FypzchC`e<`AA}YrmYvpYf7H!SPHjwnHnz_|h-*Y^%$>Z$#I|kx&WuC(8Tlt$bAXPb&;=s?H?VBm*_Nc^Rf#8woQEpe zyZ=uYk8`Lv*|IH6O@kgr@aM;ypDMdq$$6gnACaZ*Z$lM|OvWef%!jNdM& zuie(NO5(8yqxwD@ZT1sJL>LK$v52wH*Z|iNT^jGmt43)YBo|+%OA2~^q(WAW3-sIQ za+zN@o(zmR(622df4z(7j{L5i?{pv2o8Kr#(7rYPyCPN&S??=$=vb+H5R7-}2w!^4 zRNb`BQlG-f?W2s9Kk^du>2)gbxEFA(Rrl(vlGYU`QwjJf*6y-Mh_4}$XBW_BwN(Yt zlTfOO5Uab@E0>`*R1SFg#45BoOJf&ES#v6QU`D-KhrrO3nHuRW?T#0rj*RfN*P^&l z^tiIKoh^J;NLtAKCm9S^?+xS{;fDX0W4f^RB!@VejcCR&@bmP|O$*{%W3gSEB+D!@ zT+EC626R}KsDHZO;6Ck$p1Q(Fz5s4QhuZD@8n2! zAA9lmEGBBWsLP$)${aeb&Rt8|uRLl`%`gc%`1809CT1!$h!ZuLYn}_(TGF#LJ@hlz zV9YyHgSy%k7YP_0iq1A?+SC);?2A0ET7Wfn6aRubmR0_EwQj<;KNpAd+$iVYbiaT% zI5~tt^ep$0b@HS|OsOnQQLoNG{K0!Wf&J!ri){fa8`neAgJR&El&tk?;^L)bz5i3; zQFvgOvX4*ivsFs8oEYV^W$$0Hr~RXcDmPkUiu<+Nn)mBkj9QY3Oegs%4<3wKgPTmr zF9Jk|ygkrr8Phou*BjZUW9>)o3t3eH7$1L7dnQmaB({mQ1sx_GX7K&X7sMCx7lNHG zz0isO%Gs5E=vFUVyMiGiRT%LFfLFgh(z1@M9A}Ye;oKgn-fUDRS$mW`+ITM1`u0^d z_tRtS1+Jt%?zHWV9u4{hm~5L|TDHR^5y9P1lB+}uHN~Nf8c9S?H?%Z7T`Qr+SY@b? zxbOCXRIBvEAm{ODT7p7A25<1Z0QW)ggZ5*M1cc#eHVbjfAh__51b0(KQG$=3A$7B8YAH3r%|uQ3UTJ^a zrc}P9gfiXZ_uP4%c@>q|ap3PX$(vHq%LVvG)t*9MCpSV}`5TYTIV6U|FV0>;K=Rgh zfxE1XPxHuB^JdT{w3M=lA|pcu8KiL!(gZfIYzUBfBh~+~JKTubyA9$jAMvZ}^Pz&O z-I%t70DFVm(>+oq<6)s2+ik_Cq1M?Nwc$PqemdTh3#e9=b#?&V-B6~m^~!+o;O3DG z{mu)V>`d=nqaS&-DX1dCD&LZwv~aWiBfl)93}gxymT6Tb#@+JvbM(dZ9rvmAS1P`U zQCDV){zK+OFXTA>R}#z%mQIL%?`XUG<`8+4^~#X=-S(QB6Q#t}WjuoZnLNUpiaqUp z02ND``(~%8Jl2Y^&sw%Q$*Q==DYB?@LfvrP07`tB<$u8Szxt4PANIE=h1O$tlfjjYfr(RZQNWyc z7OPY#Sf$*Owoq zFQT4LPTFIc&rMOz5I-!z`9po}o~lITcbc4%Ki6DR|D zYYSvE4}?p(g-QlJXdZFqE4ni$Rg*Xl%eHOmz4g?%O17q)tn8;u-8g4TqJ;%9$&H@Y zZb?eF0;+w^c-vZoEOehg*y##S$67K`0f4HYM)`LJHoYQ0JfT=cI^k$;qb7hVC;JZ9 zt?lV@kCOT}K?cN|KNjd-`;8Lm`*1SQKmHS@&5S=4GW3^I{(?)Z5j1Bqb(afM!)B z%SDN?7SLzTh}cp&jfImqeQ1H$l#QUQFQvvE_jiiE0>VvRkhaWEOOAW(%Ttc-(2$go z7tM4INB*)E9474oT+S6$ClPIXCmjn5D~So-um2hs=mps-v{x{1(fo;eEeDo?xhj1x z5|@Dqq4k~g*MKr#K;Bmoo@`XI96D#3C>Z;ghPuD|;94Y7hYv-K)Z+lQI!pcPr12dm zgqJyWY(d5}-KAThu7qgjyEP)Za>sjEJ;_Kf+&ySnSoG~#iyK}8A}atmp^~x+(R>)3 zQ!}3dh%E!Y2nEox=ONhF0)-str{KXnL`wxq7@?;Ff|k&PBC|{d2Ul(?h^S&z5MM6( zDmEJu_1Q#BAYFX@H_2a1mugvsFf4rG+nNxD`Ox=G+C?TGR#x07r*_chlS!wPs=+r( zaQ3hBcr?}JoZIa8X2+>AhQW(j?gqs25M%a&(-WSyc#6 z@;gUoe7YzoZ9gE>myxc($Hr%!OIBPI26k7j+-(kCtvrFzjn8Fawu_D|#hXrqZEuS0 zi&IMM8^r3oLuR{s<{dD!!ru7v2C*Q12{P1AkzrNQPw}7%a0ZMS{JV1h-8(vUHVVC8 zH71h$>yoxx#>d`;z~2gz!b4Q%o>yOQ{W>gyH+?j5|N8G|g}{ioY?|)9+BNsv-`iGL zB5|)p^yozlz7Qq~wV5@bMCzaql0B}l2hc2c^c*p-UuNpZ7-+}(ZNZI73O#9x)cxQI z4O~rbJ>ow)!gKM`xn)^mTkOi5_Rca(%xnCd7%*yEuVka^)gxoGKXMEtc+l;}y0WyX(Pjuf9&>f#|g4O^qtjwThtulgEoUwL~EB`jx&bs=%n%?*p@O z#v#{J6DFVWSXKeeq@OW{-PJs?Sq=^dM~AKUyvivR7R1CH2AzPRJCP3`LOv3^KrQ~_ zka&EHYS8?%ZdNd;%eW&OAF$H9`i0I@0ClicvIRvs&pRdbh!C)r~@#!yZm%f`f0!YL%fGMJH!Sg1^~*Hn}Eq_18?stKW-~ekCMD>*FPZ4ULj6N z`9gs3`)}Vfx0S}vn>(q2cLp^odb5@?c7lKGBkO;ixa9V|3?B3Klw5JXw{Q*Tlv94cxMJ-Fy-va%Ki(`yev7K*7m~S$AQevpD-Eso3xtPc}M_`MUHQ8 zogyOAbI%KsUJZ-8CUyXt5^DW6?@o<*rM-PM^oL@m>X&T`DF??UCRZfBDvF5yjdu@O zv3`(HVyGyfhmyZ;1t{h0PND+QBLFCJuEj3R69C{}v+C`NDCH!0^?X(j`>EGSZ%l2Ok#wkWD%)cxvVfuN%(nOP1q6@L3W*&R(KE9Y5#0x zXr8XrA{ILhEdkb^s-`ZXZA`;=m((s+EfeTxx27h_RE)qaRly_z{w|&Q_~X@ zC5j7+Xc>mT{@`ZA3pkmQg>|D{^lwClq*gBRlJ8rPVGB<<%HS@(DM~r?G^9=cDPa~X z-ouGMs??@weoRhS*I!27%`Mhq#s5U+3{lazN+W5Kd0K45El^S6Kg|-@H5JYUa$kVXA?p(bZxq@?_789P6^x9t3Aut)QFCNXt>#xIT#g`l{sCYFZTZC1@rG<|MGglz{yi4dfA<$OkUpJ4`&F9i_GsD ztk{Cgy#vA^!`YR&Irj8aV;`Mkn*KMh+Eicu4N&{Hz#pVb(y->+mqZiuzWR7_u!w`o z*c@%X)o&#f2imF*zA`eYjHZ<3)fHNP3HT^da^CBNyaCUDX!IR~@~4~P0TfgBcS*sM zE6bcXbinuZv+_Eu8IDtpnlU<{NnMlGQpJo+-#5KwPBy7x(@5182#8+p;dQ6OczCBE z3?JBv@f|?^+9JM&qP}T$pFUxAqR;eX*cGsEMbCzt7g^?(L2WB4j>hPtqnA}fM(CH< zGKzIiOaFE~x=O)I%A4x*-p;*(ia;5`hRkPzU zIvVquT9cKEWMs`@<_uI~MjDIE-kr$x;Gt9t%9@7_;T4f;!U@hn_$_8r5q!aQrIn@i z4e$?It45$%ZJkA+{^F0~{NfyZ=zW=80q^b0a&sNMMmvI9^P(-{WW5C5C}f)X%IQ0a zME8dDR51Fh@i(-*vNj=7u%?Ni)RXA zdECB#$Y19vIL2E(5-}Cw9lVu%nG4wCE%{FJC;gT40_cgmqHgvP^9|DRzJm08GU2cH zm=S1zL3+xP=gT8})n8x4QE=`v3hVw&%<0627-F05x!Zfvhzs=b0HXsRu!wmj`aYTH z%<|*I2^NSdbd$6}hax_C2nDI$wR?|*rF(%tnnw!9;jvjJU+KSz*_TG##MoIa#@9sy z%x)WY0J*DH5S?4UAcdP9+FrF={JCFB;mz>65HZSNBF`&`B^9+jSCZcJPbMp_Wj&Qu zB5(0@j@&cEsDvz3xw=IQtyj->kK!< zVwxJjDhUs=#X|ZLZoBtYLK>*E*oTSKnAf>9)Bh_|QSP^4k>xbs_GBaKbPZ|r6to@| zIBQgf%?Tb28!)*ZyXO{@7C3|g-FUw&k14XG4ddBPi*?$rDW;jr4w!F|sV&H}FZ*72 z)-aNVGMoLb#rE>+j!W2R6kD0*oUOJvex+#F$IZAwJL|gOW zyRFMdDPc|^-S)i8C|&)P{u>WV&!9LgvxV`ps6=;RYwXQih025=c0<)hMAWj#+2jYp z%0b@Uv-4&n|5mlY-xiawI~3Y5HwZs(aolmQL7Z9u`zqh$I2Ck##XPu@2wJ>L+TQM_ z1`H6d9&5J0i%py#a~J2REFYLAU8jPYc4#g@(+z(F<;d@h1|1kb;Q@wu7f=SxWjMl0 zwNnjysZ<-t2Vi;N8u&T zF%wE`4HV@XdLSmyaq(oLa@d?AX0K*v4|_Fp%(r-9IGcvMYJl7E?CipDC%<_xOXCRX}efS>hAKcML1)Bw(&^J7i$%-*F0$D=iTAP8p!-mOn74S`NeKl53e`- zYYEQ%9Lv2~osbB<_~=<8#45ofAju= z@%g1%Q<>5uv8S^|r6axHw(+(VEv;RB;<1pYUCdR5E%LVkbnCg;$JF%~ls}9@{CgYR z4-37|tfv_QVXa6*4y;9-6V`8g9|yNm0TQ{hh@ii_TU(!tuU31gUY?!#n3C-HsLzPq z$B&qTzOwOcsE8LZcM0?_bHJNz72bMS^1bA)UaX!oW*)ywy{IxXCY51Ih0+6R?4 z(eZ3ZojIMXGxp~i2V?fgo9YcGRPh~e=^n>1=JaD?;D({tP)oJAAtH$y*0Qt>NA#9|9J)+E0{?!SrG(`w*I&Dm3FPVE_+>g89998}}2^+$r69nf%X z!M46`t(@a`qq8c-JW~Pnnnd+x-oU-qG|o^a;xo~Y$>pxs_k#}QttNJ(m>u=3cH}jl zZ%)0p(bJ>NJX)vrYUA^ci7XLwJyp+UAK($R!~oZdsL|=n!nZL!ABjgprAeaeGvcfu zTc333Xacc~Zf1W32WB^pSwfQA0LK^ScGeja<%H{+eGD6>*3FPTbgLV6xn z6WSm}JNJ5u{tq>&6yup+Gj18wp-U|=SMzRbO;ztEL6fEwbSh0w;J%%%o3uqPeyO#T zUp$@|r-#^}W~dE9e?}LkEueB^Bz##|?v#lQH{&QzF-EgS8LaH!K|_sPH9+DmTr_IQ zyFm}8-Ijm&8n9(f((y9MbAN~$$~*xnO2h+fI3$jLOu_>!Cns}yMQ8g;eSF{i-h4R7 z|B;PT!xBIVy635(i<+^lsUU5D6`fVPk@O$A%w6>DztjBY4)w;e9t8>AC31pjfcjW| z>7%&KWoVJ{@J}r|Y*TD+56OD(zCm<9(mIqG3hbu(&&7fCiEL*_S}` zd>p@<4#gX89G8cz6&vOLp6wfkdqtZ8r1NV8l%&{4)qqP`Mtwzz1^W$xEW;vJ73xWN z-T5OqU%4{L?#DSuVBMeU9#`s!4YC^R)l|{$%_dLb<=yxE)KtkcWmT)$P#1BZ%JSCM zbl9jfnRc_yPGb<(W-1$4LvS`$oMk5eVm%&|^y^7^H7*)8gkTnzWfpBxT_%s;b?R*Z zv$Wmy=jvcTfh~=Fc_7J5Q)~;Bua=RT`|iAD|4p*UB=6BmoVFt-Fb%&(+>N>+E&W4T zVF-Fta;AGUD`R8t+9fM$Z;hRynVwVY(-!$N>hJGhyXyGO3dsD*68%(=Y+L`R%ql0| zV8e;|cO-7J{FWbJSo@*OB%g}tWD4?g4i7-IC496wnpn@*uNn0F5&aS)WeOf)=OG8% z%t1X4s36q$3vOw601UQP@@Th4ShS_gvaS&3{l?Uo<=RU;yiVlUuS~IhdFS}UzaAZT zpT~~L4|h8kyT6R$FQB&Sha0m`SZz?9aPf>5bW`J>L~CCbG5gRFWMN-GED_7<$57jA zG`mzV119RA*P`s<062~i%0?Vm1FiO8)c48xv>o4_%n2)qAdw*Be1o#7=DKq(9Y4^- zyXvkPl&~(2)2Q7qoB}fBwq>x^Q^O?o4SJc1PF!_jn-FW-fV-vBU3rd(l6rh8c~;&b zby6~lz{vHCC4;*bpqG60F?ir`h%)34V5U25h#|BLu(_LHHL(cX!NxpN?d1X3JBQgBwg^ByV(1 zjw*cTMEiOd<8}!77I{$)zPSI)*l%%e()u4Ez4DJA$e5IbRmXWPelk!-IosA|%5G1u zPs}u**p2o;82okrh{+B-70CgU4lnB=hag(P)&2@I8e(uEO8&Qfq!d~A5U;oDN-*h% zVe??^hXI){o#H+|VFs${r_IKDO{&liS@#`*d6jON59~x5dfzJ*NKqr>;%*GXz{66eJ5f4{3e=8Qdf#W@<53bh^LKD-sXAvA?o@H}u|cOVo#> z(fxvaq2(ZRkHKE-iq2gQHbD2>N-hZ)z#fhP+X7x<5-ZBvSonp-N}X`l zmCFuP?vH$HpWU?_PZf`p6zH)ELs8y8HgbG(TH=IE&+$m))nId(4a!#ED>`{T?glir zo&n$k8KbxNj9b;JO|4WQIY$aBdT4tlpnh}QBlH;=jGZhu%v8=CTQ!Q(Np7ZK!b zU!1%qtd9usY5rHb>jrG`g&D8fCHQ7!RzF~xy-rwZHw_$ruIm*0G?Mu(*?4DoE;*Yn zh{uzA?E;L^@oL%?i*T*tt%KjwxB95{S$ek)IUmNWJ3 z)u!@wa*JS@FRbnKnQr?Q9Xe!4&J&M|GB~+W2%aZFZbzleDs>52;g-jF8uuc`F={S& z{(|YhH2N35WcIx>8CVVndb_`nS){jM zrrgte2GdC2GPb&|lCe>7CNdYC`!_a(pig6^L=Wru?^I3*_SJ0o+j>|Fmv^4{T_lk{ zAhmUjkDgM}D-6dBYo8PJ;&x@M(6u}-L0{H}sjPGQ5})@vhfD~Wgedi+oRB*>1U!8^ z|5z~f$!YJi*tlSF#Go<^?3}$w<@2BA?1-=TM1toWZzn)llfKeE`i+Z79*rRurlL;igpm)0ndv^CwJm2+(Jo8E%{)3J86a+j{Z;c8Nv@xl<2Nyo2gEis7(@F@`N%mDpqBKlVv9bf1DM)#B|3(&I<9r zp+!N?_{kCbzX(d!;qOI^Y*PrAW|=dMPEGO0okc+0FR`6Cc~?Fkl=8bIwo>`fRXPP4 z{qi4zXyr)R*7Det78BHdYA%}hXwc7;sa>p$T1F^*qFGTya}Xl-9;hRL)`b1DFISL~ z4kTMf8?<2E=W<~A(;bhC$8*)7>-==yy*G0iQ?6HK+~nG$V8(}ogJaw-CdB33)Dif# zs;Z-;v9Y|RqFLhoM^DV6JJ>cI6ifQ(C`mpSkQklnhN9Eq7e4JSm4iu zOwUiNDh*%}PkS;<7j5#uCx9-872aVek#>3^c!33`In$N4+oYxZk0H-HuD|-e-Zr;k z1_s%Dp8EAwA67?B`{nQX$m6)#L=`Ef=}YbS1YRwA)^Sak#a$v0AODjCH&^Buu0L0{ zzWXYgwYSXY(VvQT!xMY=q0pRO@^^K|k*iV+wW5Pn`2q-AV!l|D96jDD>??mf<~+;J zS~%AKeS;nSfKGhJ{^eW{f1#a;_%Gu>P5P4KY0<1B-2|hGMjaH}8z=^!()aHRMzL!U z<_XO(9MZ0jzzGh}x8CViR447E5ikC;wH%(ws$Xm2-Jdb+0DOYW7Sy zN@(^(B?5tSg%H@aCdaOgMt$_wYTXDo*z;$|E8GTL zf+RX*VHD66s1+eRSVax?*#0SZ#toJgPt>1~FYZ$YW>K9kk&t|I(52EGadpK#Gv;CW zTX5!@{377g&F_`Bgb$^qUd!a55i4-UFv>QN$rGIz`S?aNaLJxWPPL(Y0GVy4yZ1{_ zj&W*v?Cc(AQlqkCo*Yc#Q6wW=9Aeo%{Q15F-FxHXe#qkCA-qOh_L~(SvfXw|BfaM$ z(?cW8dD{2&NA;&Qi8{U0!+{Ur`tl=1C4M1J;C2gdSPBjBPZ+)pk#!oVRFOMN-0!Wy z%T3$iwQMLfpMMSgS3RhyS_T$_Kx;+0f+yr`R&vQIxC<$&x?0EI`!S(qGZaM! zGevedYnw&Stf?#INb#6J=8HV44A!^%YQ(IjT1~=KZTW(4N~cqB7re9#;NP zi3k0+kLS+={*51acO$Z{gRJr7F)jhS@S$Fm)ohFqRb<^_lrs=Of}X;dC(>3ZX;;ds znBKeIfnc;Pn)OBwZi?~r$zj0>0(xtUO)g)-k>kd{th#sh8DL<9V}$hfX(2G8KP)1B*=LZA6I zP$^S^q1b~5Kx*~}&bqsIr%oM+w3z{AaB$GB)9DEXPNp-TW8luX#)pE1yayI^Et>}> zb;=9_k-nNhj?p|F1h!n&Pvb!~60QMVkFv&BME~8WDr5qSN;zipkFEpbKW<4r&URrT z^x-0|_uBH-sjA3+s=Y*xCo!@hhui_GH^A=FnlA4OTy@M=z`_JE{)L`gO zKBJsKIBV565uf!>Bk_p?G#)y$W3mIi^u&a{(o4eJcaaT@$rgvBB;Wpf{RdP%*wC_G z5{MLWUIV!peR^E&ZEZ;=f+`c~^lGEegtyjS)PdsIg?$NhnLY{$>+ zAb0HxyfutLJQ|cit%O>))e-Hs4=@p5v}t!s8fCL+Xy-?fTC4(_XkH*LmXB zd0g^0y3g|0gUTv70WQk$+cmVwXmWJgcu~)g`z`c;oUz`(&5nH-Vt}y6lc$r?9k*lMfXA+)(c`2*kMhIG%v*J* zq<|V8R+opFAY`lG-@mww6(wFlzBjIe;@Llr&#sD(64r>@uU33hOSDA;GhZTWo<&~D zyA3ZGa4x|;j1;=R%MKWFjo2PHZO40+yDpb7BK_<}T+xYnCY~d`@taJdXp;;~j2Xne zD$BIM2}DlPSV-d>=tz24<5k_}v1NNrXODbZ*J%`;K;sPfbki9cIdgP>6Io?^_mLX~ zi`3>b_}M>GYdr9?r>XnpPon^nfsUV}*@vRTfn58`TY&S5( z@=@%xwM7jGS+2_{lEr~*+@j`$IIG1i&@jC%rgpj3{rkt+Fum->tA$~Y<$3?dha2k4 zoLq59uLEX$DD;FR`sQ)$x5)+N=nYJrBs#tQ<4{SsJMvVZVzmlgXcH+i82%M8q6eN< zU906@;54P=W?sB9u?zcWe1QMzeXW}j%(BR>YVj#-W=c2yT`gF3Gm5g|k59c3jx z;7Q&8VSPB&t!C(yJYO^lL*JYOW=G~y;`?Ej@QFxC2=v0YL$%?eLVy1 ztexBp^*~w&Mf=U239h4f043R(knFns9z^CyX5GPAJV~&OTS-;+UE3fZWR<<}(K!Te zU+4pSi$v?jo(o9N_}k|4ei7H+Tf*s{_jhB^{?2jn%uGP?gb>kmjl+gmH=F%5&%laT zSB&feK*!~hjRxXKCXW*N@OteXJTyv=a^aHav@ENF6TfO;+YM|C?C-it+ zf+Z8^2$WKHr6z^E2go<$D$Z@0x6K)btbBukWTyt26tx}BeSXe;XCUETVXVU}Lkum9 zT40xG4Nr(&+!Y;tIZ7-r85|*1Jm8#@!In94zFK)$Xe4_`m*A<;@4Jna5Z>_e0 zbWSO$Ns{sch11yu9!5vGSmk8Lw3-Jf4&j)_j3TCd(z4J?V( z@6Cxu%dgr9@z`Ao(nylD%Hrb3pu-aoqTt2qHO4Mvxqp zDEV&C_kGX#>imD5zv|vwx9U<>xnX**?q2<@wVt)Q_Z~%4b~fDpJU7X&gC(Q0_EVRv zhY;t4PLUW2+GX^Fv6jNpC&iOLadj47>7B6g^Mp|Y2%lMkDeT&p4OWr%^A?g(EwXi& z+K^-|k`ok+D(x_ewpAE95V0xG-jON7JoSrOZDDgcAv--vUU5lYd0i5;aqQfL#>Vrg z;NbAQ?+Ik<%SqA|>qPM;?jWo;T`!jK$H0Pa1asSxnsx;TIPSf(ha=pv?Qr)6teVWP ziuuLnqIHm0u|KV~XDbXy(0;_DDw!J3)?nN_Usg^0_5#J_7Ck3jE44NV%usbnIPWNf za(fpwo4&t^{>Iv^rRja`iprXRf04BTbhW{~hz!YgLe`lnZkM#1k=a zd$Q5X0;LyEZrp(UqG?;Ua73S>43tIDg$c_s1+wF|Rrz;PfUUY z55b&g+uGC$Fg&Mcg(6QN4)ZSHfnrvkg~v+1#fRTg8El+>`R-q)W}=n^PYOz*^v(-` zE2Kl+Fi4zWv7eOb2DjQ`QY`XbGz|HnBK z@i1HWuGQY=h=jhR9M>3;>+kQI?o#uLuYZ6fEYW>MN?S&5RHNr+rZd z9%GoLWZ%HCIF=vpdfprxH}|*5sSjaGW0dTu zWx}o3)s^QLYJ1*C0k2!gO`sM8UaP9R;^2`?+UMp5uh^+X;a1P)xFyG;yDs0)DcF%q z(xsEI7g7Cu05=UD@{;D%m?2Unecf8FXRhZp?cV~gOE|DCpTj{~8{nk$*ew^$ZeX@m zZl_=42MlmGJc4Q}Q5pMVRce_Yr@UVjftLKCCt61zo+6NtHs3-bYPP35c~;(p0mB4B zK*2G3GplFVRPnP{uY5#YYo3eYv45)kYh19G9-iCB$W1>bMLZDVXTM@429mySU7ynA z`ziumMEPFt2Yt$;&fI@D7`Au4%DH#7dHUK%$ermSi zid|eTt=rK%F`*e;qRhusuhbEIAy&6k>Ko+%mX5Y5i!=MD{z``OH`iJE@AM^sK_ek0 zertk6|Fu^%Rgl_!6#bmxOLfe1ZnZ?53>HB}qhb77%v&}$M21ds6YPFVvG zXR#AtDt0rVkKM@7fHxsKq0w0hk<_d^bN1LfhWGsqkjsm^5+cu5YhVqG-m*BiO@SkA z0dJglY;`sBm84(Ueik=0G*bdOm~G^3LbEl0cZr6ARgS4+piATYVKwq9|5!Yxt7bk> zCnn=8VOJPgLD#{>N=3`c)eKDmB<*S8{Xjw1&<-SRprR#j=wc7ptCe~I2$_@U+27 zok|N!Ia!KDeerKULb=-}1&5ALPl@nBL3%T9ieRO=@TH3Y(|;YUlKkKhx{j=hv^&V5 zJOf<^{~yIvyB^iL5j1W(*wD}dtQq!IcZ?|Exv(zM%@T3}70X9DZ9D)2jex|1CgP1? zEnrO_0h+(NPQ!`0|ERrJ*e-0|Keuwnq!L||a<8sU z@-Co17is?%9~y=%w3`&|8*m@j?bVIdE{)+WwnSYT>D{?C-#5TenS#)rQ}5jfMMuGE zD?kTSduzUJS8NcJognmsbQ#`)XT0p9FC-SLD4Tx@Kz$Gf?8qCjyg z5rA1FI!QEaL$V68b3QqE1`_JG%Rhx(JpMD~e}~(+pX;|#U2vP?WuMy4Yka92V27In zI`)(HKNm0eLCdo6|50h)ce}2C&n@LfTZC}p(bHz|ZyepI2PG_Jrv>{Ktw=P0qSGL! z-YK`<0O{A?>T9X^o#Xxh2KxKfTLXA`KVd;$Vf}CT{O<534gmGvy1nlkzdu3& z=Dvj<9#Wgs3#Qk=ke>p8`cL}#Cy4xmJ6$FDvaIHU`jW=-&OnHTl@${MLy2MIghCtZ zBPd8e+8FGyNqK8K=zvq7B!MR-#T_2D*VWa{HNIY`cUikQJ;2Au4~IBpDX6Ip_Vm$8 zjB0A#nky3E;W@yuj4G-^8r42ql5&V{wj4OzeyZX_cVTyKF8z{$pmnKX>;2n#D&w)s zZ+<>)G*O1&l5)-gweHVus!O{M7S^kV}YS;eQSeGPwUGe^sVclt~+PdY<$kn&Tz=( zEMjSgGePPb54$7NraqQ zB&psm6jXQZ4W-4-gO;(*A6R3>m{S{6Pm$r_Tux3r(nOh9`mL zG005I%+PRdgh2^7vBpWHgUF_((`%bvsNLL16n_VKXY$sG^3%N}x`M7P}?_-WWNyf?MBK7=}GnN~e#HkdyuK)*MWq5ji z&iM=;1c}(6#Dk9=zS`$`zttw}x~UC-&f3}<8wa0#$v_6dsFHFKl|v(i_{jBPvX!0% zEY-PKhf|E#&ZFsxI6Qi>`Asm4jg%!VWVC_OCk{RE=$hm zp}IcIU-;Agke?h-UQNalQl7we_qfXbJ4Mv&8ImD2guCPV0flHvhN&y}pUd7@^0W5o zc#POt{28dLIupc$--Nf*{217<+%OX0`3gFWY~bvKhB`DQ$5Jvt*7>(%LvFy- zXP~lbW`Y%$;ATDf98yp0PhOK(s#4>3RQo|pcz|DY$1zyx2}qjy(q4V zF)zcS&=xbsI!wtPLa}XAx&3a&k=0kJC`$!Nju6_y25<&bX+(^cX0R$peckdAVcCNe z99LIY5YTO~p(Nt1C+%sQkZUGw+$j>xi6d(EJJA*3RF~?!=to7GTFUc93C@;hKP5Ps zcWJcfz)J$XGZl^--Npfe%xgj^G&2o_Tc zYl5j7YZ(4bxIdN%z4`H>wY9a`0C#n*m{+odH^Rx0L72ci~`-+bX@zm38#d{a_W_v+WEIXO9_g?aV$r^{brWh%YA zyof@91-r#1MDS$RB`PyBbM_OkS@b(k-VWU8p-o|Mr$PJcFO&rh4IU%83WkP;vj7x- zrx;IUZES$t+))D9ko-+|2igzK%3r?#IM{Dqgj{zC{;RdMw5w}%W+sjE-`()}k+Wg< zH{NTbSa}2;7cu7sPM8B}xL_@4oFAC@0OzOIH{_ShCN3dC{It^zeS#S{fDIVUP|~+q z@qd0J>-(D$12H|z{VP!5;QBoPGk_N`wNBn|qX_sz$UBepZ$~C{gYn&lMMXN zX29vafMNSw3>*fGImDw^mw`BV@Mxkxt!zCIr6bSlrOLRC$SEoPArZNMNUbF9-}(^y zoBSUBwW4N2et(rv{$s;gYoh<{(EQPL2H)q^mlT)h+M!>c6e_zgN}2ua5!co6+9BRJ6cG4x~3Izs0HN z0t9!Wm=y|zfRPG;^!jGdp+Li}Tds_Xl!5wtnd7qVp=W`SCV&6R1q%_%pmwH@$jvH{ zZXx1V4kia@1UIaaS_AF?=#>j*Qrbd)z8^KitY|<&*;@(8Txed4!g*~VM|#`w$HKs7 zBzYROLH2&#$9?I0_eJj>pGJs!46Pnu@!eF|fUQhiJqiQi2Prdl6#A^N2cbvtYnT8U zv3a*qljx6S@;^NtdH1mQf~}{Fi@diIad~m~E084uEy8WrF zz3)XWx^;-*Ag3dJG2p)ei~3k-IX0h@)ze;WCm5d?783gy}x9C_sS6C;H z>`1>w@TU(P&!f4RM9{>SNlSNpiDlB# z91#`~@xA!@dC{@X`$a3J!s^W=u6>UO?zV&+Tz{uREYvK7W1RP+g>&2g$b2v}Ir)NY z*D*k9uBfpw5!3bc>(?g&Hd?RMQgdxsubJuvR5&=^6HNOolnrjV)z6BZR585X^WNK^ zg(5*wf$Gs)3mlzEt|%P(Dt_li&Ost&M0>|ZR9)91?7en*A1?P=G^Dz^nt@@M4XmT1 z!@l?090~e&>%26$LXD3%l<~4)15E+$WS_5LZGbn9iI`t~&f6I4vzL zFfg!l9n954UW?QQy(#}AC{55wy11~UM?YjOudV`TpK5Ar3knJh3=BAFKi_Rz0xC9m zH<2cRE(wA&*k|66J*=svr9iLg>+9?7?QL$Jo19D@QzE+veh8igmiVT~@$n@J#)Q{4 zCL#Re$EN@pz`f_=vn$n(X9ya}ixg{n2^YbkY4Jk7cpJv}eoU!oO(Qg7r zZ}b;pc=DbDjn#WpR!n^w6f6kt-#v|LBZdM2lkj|scnJK5#4g{(2Hv*Qg|D!&lA!1V zyy~CE=-P<4sDODxQHoEK^MEi=(a_RVkza?M8Ec#tlYw&+5_CVZf-6Z&yIO?0;f4{s zZ|@SbLsb8k{Q-)~&iuFffLRs^8kUchk{z)nOcMjr-2q+Q)D0m*H}^^2`FlmQ5rJ0q z8@4@P?|sJ9{jMGuX#HnR@E4uffMlV+Wxc~BL40@%k-YI^ z;K*=Y#3XTPms4{#;+|xVBq~-C@?UDhFNEB|!u(59=sInt#*L=U=eCqQnEz4W8+6zH zL3SY8ypi%IZ^P!5_6CC9wa4vH#n<3JWvsED?QG!%E`P$TlePPfs7S+Yg9v z2U*l6bSqOy<~kWC(yMq@Naw9)gt~{8_TFz@No_ z*ko=zMH!v@$1H;iTc(NvEr5vpJuA(vSVb#%Cqf~jd`mDVfc$@m4E;jPPQnd5Ub^NU z$}QO8`tfICWosvbW@5a{sE-!xqMO|%%v-a|{*{>&{U04dG`LeCDy5Ppq9G6!;!Hhd z+mR*rR05?3|2im40Vw~D=}av8gtmrR>60ync9sP@;Ds+QdWa?Q;463jAWXLNK&Oo& zJu5bDO>f@u7#JAw2!-ArQ6kAIlC*k7ep#GAw-Wkhyjj{V{z-f#T@Ho1|U zSCM~5t&pulmo$E<;vh6;wBiQjZ$vGL*(a2owOYRkcIcu`H3ftWVK4kNdn~c?W&2)l zLnSCTsn{N*Ey{m-<^GFgQ4=un+rsSsV;Fk_(B%xhALBT6um;WNt|lyx*=m_tZVysH z?)(d`6CMq&O7hR5{ew*KtY|cwm{!|TcK^G7JX{z60D*r6NMc9;6(&h_f_q`ob^4OxK#CMSgaDT+FayC`B(YMrMT`HrrtvblcI(mU8H7g zkok`NV=&g_%k9pj2P#cT)=;Yvx>Tm*xkG_0Xf@?uex)uMWSr!L=otuqkVOopw^3BE zHBAp$BDG@)t^aB70EKYK|7F*X`Rl#8b}yCNQaqe@Sy1f&((+OJ?++~}J`AIlK+4OK zRBQ-Q?1cVuiO#$mF;rnn+`9swSFKvmwlGlbruo~4lXWs#!Gl*uW0%cA%Scz z=+CtOKXm!9&DP7ZP50|}n5~ktfBQ%%e6~Gz45%0ZV}xw{<@At{?j!$>F~FbytINeA zxD6l}f0K>XPKgyAm4P_9J|N^KRw8}W1mg9rsURUy-uwT@rDX#|wv!$x%?+}Ef5A2< zf0^UK8E*LyP<7`TMJ+C@;}aI(=HrRlOTVerd%OoF*B)hdWQ-)T9Iqu!N zJFM}-Po6x>84k3(i(-dyLw$gsC)mIJ;eUOJq0_;O6{)VPp{J#(0aDk@NH%z%lme_b zAc0-%k6GRH{=d8w{k0irl@JPR*zZYM+J{{%?T4p9{fT}f2jh?Z0n+{}xcuJs{C~SG zuOUKFvDa)VR+{enB$M#tB1&BhXf?_{Jro3%`@Qe>b}H#HSff0|6(>T5w!F5s77f^5 zX>4pPE^e%>EY8WPY;Mk}E-9(ctA;h)6Cy#MAm}#3BB@{#n|Ytxm)*3ac&iVjq=8p$ zm=WQ0$4C7vV3mFjYZYMq-+v~7kEQX@gTVwNSTstNH(>|hu}J77{^^j2Fm0}s&1~{i zh+$Vu^e}#0*5V>d3djhAd%H$R55ez+P_Qo9$R)|i$@8tM^0U%ez~DgnE;6&ZWi5d!255&N6NP?Ic$rd_2prC%-_BAH}+0U;p zEHKH;qHh@>{2%YWyAK9feROnmOG}IDpy2-g{_Ji$A=IaKT`(;sW@LVTVAUzwz<^xm zeKeGYw2 zbtYTg9mD4Ca@d3EAuktCK@U^eO?JrxxdmR?vG2^zVap$b*L zQtoZMyJSeBOnnE8Cr+<3{8MKWZ6~pGxZ(wX$Dg;GW4YRBW4c%TYInPzvvr}8tHnXu; z%GJ#qpf8i7r8hlAB)G5E$vn%tUzt$W9ry0|@mahiZHUwZihLYgy0Xm;x}4;|2}eHT znK5oY(dUK>UfVU>I2dyUr0nP@ci%lFaRnq;-AJG&v)x!Xn+@j|6%{pIQz<4cl9D{w%A@BLP{JnkO z3-I|9DRzvMq3bH;-`6OVt}*)gt3jvd8(wKXsyj$p=>*B4uGZMeh16gX*Sr zc;e-(*PkAMj&`0krOa%&tq>BOe)~peeYTQ z$~1C(LX8~eu)A3>l)}}B8J<+$77Cl*@7Y}1@3~m22v584k2~Ce23&z8AL5~-RK!VT zsnY_-OU46y z_T}?WpYNO-`+uGWo+10@j!gCW*+<$%i~%hzlwvC$Da|PLe87QBm=4Bbktj;p3hcvZ zpChL1YEf69pT96ce3q=u>(a09^2P-qo{Tx1c4A0 zXLdYzXGQ@5d)zPJVe#ql3)Et?bIh$}N^!P86uYyR$_UF@l)LU{hes!aPXeAkdC(<9 z2BuV&*m!v3Sl{i?B&I{Sw*ZH!#G=Lqj*36UoZuUn*L*+-w+Y7}L1IT@%qIbgDWe6m zyQByd${6@zR*Wz1eZ8C@ZQ6iI1EUF^w(gZ z?DKJR@uWLrzIy?jItmL0v_KEOLBU>~RAlv5Azs3UTL4Canz;gm@;(NMummuz22sy8 zmz6pf64{mX!n%N@>=*LD-YQ$g%di;DN-!!LPc#0dXK098mo7++6j9?#b3rUQ46PD`r3v_7p`o1!1aW>3R3b7xm#q%;6!a(vW%a^Zwszifa zWqpCLH0JFU$G`#0%D-FSKVti{CsESzkWJa3gvISzTSKS#=?ug51nJyur6fv#DjA6V zp#;xWC2jO|fR~m1bf;R+RDEU1dv-E5KZOI>GhVl5Pb`KGMAT>d{z;$Zs|1-fMLf7H zQN~=*pwvSp{>J-h`qwKp<4$Gnm;JSw51rs$`}ccNowAzFS7-K0^R4~jHGf85Xib3vNvT-oklyoAkp5Rgov#sg>ynbZ>kRtA^HOZ0D~}(( zXBrUG(DSrXn;!bH9?Ju@@aL!NfCIEHn4#*CWneirk8~=!>&vrlZNb(T4x{pY8EkeG ze6J6q?^!Q@#;_5+B(l7BuHNk@Bj=&OtR0u(d?ntjIe%mf=OJaINFyQIxGP6HYF%5AiIc4`b^dxVf= z*A8J?^H=dqn~R^PaP(Lj^{mdbl8D2Y6IyI_d^RWUP~al-JNQ-E`&I8qyHW>i2+OPs z$*qgXlGlAAYv`h=?@WC|pX9-q>|Ox=m8<_NU+>fcjAkx|-B*LrQ;zec5{meDA#@n;!7|Z@wesj6GG#i z>kDT?Hxv*4zCRrH+1yt&8->H4FYj3`s^j%{NIZlA7y_y67f{9 zath=2G}507b^G*#O!J^_D7+U3V`ezlyE)(p!qR99B=dFd1gIU1Yt^QdV%i zr>@{Yde-_OdvoCKT`_kMRed~_(63{AFHe$4ALcL2PD-3kDr`AS)m036{u#?L3Asy_ z>>;v;HUgz$)rA>TF6idm_o#jho6t~Q46`7mqYrH$r4tPm-AR8qWOFy2eTW4){mIY< za=Oe=5lXuHkPT}3^C6ad>DEIV_tIYt6``fS9kM}B4D#A=h4B23% zHx031r*{u+V5g5|tfrwaDh|w%M1`tV1H00duKe!n_K?L7R+eREw#63qIVRR6*>+jE z4*4G}mNu!p4wvTKF%m}1KTFjt6C^P^F4ym5Z!$3vt1%00ofiZxYcs_0EBj&*#%_L4PY$|}l%=NBOvXKERx8tOzqEr*n<5U}zbcvgo*iD{<)QJ0IN zwe;C)f;iYN$uMifiw_G0Yaa!OmO9a!nUcBcn@Tg7+nC?q(A~X$buwwXRz5Yb!plaq zrdE2+yXv-=JXOkF>;83im1;D|DOIo4!nS&PY*R%m zOi)=7HdOGzE>xF00v4(PZqM(2ch>D^+&1J!~~<$GKy}?_%)PufVBK7jd}b zXYXZNYphAgeZa=Z$GD=4l1KE5e%a&Ga-E!oki4a^tof6`0$x9(?0|KSs=N9)CE)^+ zw;FM4aW0gEuecjAcy&QnQj?66tY$Ks6A7BRIe+N9DfH8Qm16L`US8R;E>kjphto&{ zZRiPT*cKM z=TT_`s~fvNnFqrCkVJst)9}|^tO+~Dc}b0W3x4z z3#i3|w1PFF68jBKr)3tW<&r(3=!_MG9^qM{LyHeVPy_Sh!zk0Z%$Jb|?*gtqewGtj z2~b!I2nPPG+zVSl39u|y3uzZYzNhGq2&qu?Af?-x4bIl3H*-pk7$hhCPKiN=qH zC0F?vHQ66`Hl@P6FScGX%&%xLA$z^hT1=%z^vHOh z$Z1(YPvjDlsHnfT>`Q*_FypY&5Swu9mVDtFSEkeaJ0A@Pu~-?HgKanScmN0f+T9tR zl=p@prt#^^+}*>l{9eu!>rL0bG`TF3efm~IRnuPNE0vhq7QEJrdS=@u));kS$->a= zpr?aLHd(dG*LK^~c+na%AD>$d9LC%$!(ss(EDF{EeIS=gH8*81QFAYPGycpFY>vSF zoEWAjHCgcVW4?<_hET~gldlHUfrmC`$CP*V!hy{jpZ2ZK0}km7tFiN# zQG1`}3r|w2UOF{x9lB{MF{;M&5tCv4EYi{u7~D?Ji`F~X3uwH`;x=PoBab+bfq5vKWJwJaLM+Uh~r_U8QXX=7AjQc+7P4uYEGg^zj!xWGVb(6G~t`f3E;m=zkh)j`< z7(6Igf}3#SIHT%2|I9tFyE}2rS=dyQ>Jgj(7iYlHpH(@2(4`(QsVOP@O(3;z1nzUJR06 zOrm~lye~gURSyUie-bMnlPDh&1iQ4-tau6bExN7e%se`-g*rIho1R5SG_H}aUt`|~ zh3*#JbsLt7icv?|iAC8aJl!GKo$5N6`g9;3zE~aE0{G(&+I^V&C*p%2N{l9?%2J$3 z{%QP};$~#8x*o1qIHR{Zz)Ovfa|&M;B!w1ieCcvn_^<%H?0=F>)#1CJPK; z??J#|2-dDRb>e~dCC?h{xlww%I|XU^TVYi-p5hw}8cxB?ya0xZb8E2j;xvL;(|66k z^RuqXK8^F^sb>n8Lr@A*3OjPWQFi)P`1rI?^}2QX6P>?Y0KQ18gyZ(qk*wzq#@S@yz zUhIYTuZexEV6)zht*CkmZxe&HxFc?2Pf_4y5RqR+m^Gj3Qx%PiVZ^6O?E{=5z5Wpo zLr(*VC-8n3X~a4*8~Ah&U*LXl@0c69CSpID=YW9sC&7>l2{8!$Vq>ejpQg+Ez##1~(xW>?Ud6x=Dk52odOrQDtHU*6Vz;ltZ@OWS!4N*~WYF05>_8Sl`n|6`VrMzVH9h_jXD2X<) zwWg@ufPkP_EB%oeEUWdU!*g<+Z~S zkys{-r`G-R$@=S&ZZghLpL2fnMTQR8Cs9(MyIKzm6V)rFrF?OUhFA*FiBZLm@@TN+a|_v0~cN7KG)7>u;o{i zh)8K$hahc(`!K%Y9YvcIL$MDt;|npU)&^VwV4 z3Qx>39-H*h>TIS#J9*SHS=vrir)A$b&r{$Yt<(m^`9b_r#5vycGSKI%5bPB?|>vENYDtVd7s9vT48|LiCz@EF{tKlz{jWSR2wVu6QKPn&(_;P@O z!ZuH=lI#=Mgk!M(2I~6b5Qk~-qe1(=Aj-<)x%d7;7RLK3IE80=zId@^f^0k#9-ASbf&=G$ZA8f?`Ms%9I02eF;C0!I=LZ=7z#% z5Ri70inCuGeX!>0 zO@i+p@>Zs-{hmRgWnwQ*{5+M)+;m30*3iW9d*?%Pv$8No$2FWZwUjgJ$_Vl0q>2-b zO$l5x38Cw61!jZU!1ug2!E(Bao2T=er#%#>(MdSo%;jr#b}gYrbdH2S<0nxPAW>XK z2|47OG*qU(d}My;TFc_f!J5~ZG;6ONTR`#anRV8=?@VjAKn))p@kOloSc9UCsn%$I zq;ug}m6Weq&_tqnJb$`=>We^6vywblO0S3xtV=~<|PT!FA`rm~nT)Q{yhl*giW2w#F+afs>!00fGVZ2sMrAerH;-MtGF_bVHo0<62u{3U0Kc}HYb+7ndY#D`eZ8e#f_gO}6 ze!+W(rPe@v&-kYMn0grvFz?l>vtcpT##WuPo%cVl{7(D_6gP8jeUr7k%ayhoCz#Pl z!N7O=VE5!4s1+K&25Ge)>$vk@t8*_r+ds~)qVa8YwfpRvo|7%yxLf4=LT~Z9s>!!T z_1C<>jzQ|!j*dcVxamaEo0h}g(?&(N!EZT6)J$K&=IZH;YR|F?SoKPIFl0$GnZuOc z7X|yOQ!^Qouz@J(K$IMML@XKB~5{(~zd*m`QxJYtM5Jsi8a z58Jx|Lf&kC*v-Z#jwbj>+P;GoT)4I)76+7I+0O_i07h3`dGGxGH!ztr$A{!t#Z%=* z;e2@^m=b0mwfPlq@Z(%c%~VU>{UpQqJ$Lo9s8>H1Gy{BI$oF_=-$ljmcMo}7_9vtCajq>!^w{*0kKf=YZ-6I~FqO-4)pWRk_ zBz+a8TcPHq;o+hce>kCEQbxg1Q~Si;$#rIawVGoAa+1-AJ3G=T%3CJFm?;n?_JN{K ze#l}_cFXrZ`OcM|%U6x+(N@C*QRWXJ0C(SOC<}7)cdH$Oy2?+y7*82AK0TQCEXdPN zS4#jb(2}A@esbFCx!Ub%?oF64%5b(Y!gx~lCQVCb{yxT5lnUX59=*3^#;F17jZc2)&LV2D3v-Zy%N0|6G4+X7{zZRj2 zHnj|0%0n$O)u|0fmeTbcigKRuO)K-BrrBRJeKBY2e1qJ%Z4f&3l_>T)hivzuo$plf znF8D|!33P)>#n9w+)1M*ZYjbD&H@4ZWee9eR&RhWcN%yr6sL`|6`cOv%(bD^0$oSc zEVnqxw!DwpeQkTxMWm7Z`2p2l7ZCjfr%LI$8++zCq6?PBsZ&6LnLx3U z#4{lfVqzNr72lrh8?__ZXB`Vpk3~a^aCqEi8c&^&80C!%>^+4~w14K?0?r=W#1@sX zRTR6})3FzK?i%=aMLv36mGt*keda{23|tNf0jcuD%jnMi<2>)JSP1Ad{LidcsgPIHa-S)VD z*+or`)0FjO-_xipbiU(lRh1)DMj)OCN%mlHICsSzly7bImH9Yy`9gfx)VTZ?WO*X1 zVo%lhT7Ks7%buq>n{Ls?oz%@X?++7H$7Y11or;25SoY4lyu;!RaA$SP4cDWD#U(SQ z7r1zm_<8VzwWP=}sIyRTVvz$Cd^(VmbVNYUPX^g^}8MFjuVkd>wRL%v%2IJADZ*}VDOe`B7ebm4q8g}@e%Ka3)g&sAPTs?k8bEk^Lz>nJ^8Q&^BA-cjIj)B+tegqJ8<%@s3_= zJA~I-QHhV%#z$4>ED2s808tZr1-!XuCV?2<&8nmd$_{KBQk?i13MO%F(EV za@%Tu_qPG@N8$KIq7t&(Q)+n0}{m|(}C*^O>i(yUBfFPd*r6Qt|CGyI;)`wUTv#m4P9Kc>)rPxEsIa&Ufl zc*NZvviq-=7(!#rzTlVaD!Apz*0R~F2d+ay&dU!krpX0sz8`n1*06u-~r6BG^*~vZ*ES^&CB<*!j*B*!E{S@M8qQr`zM?$X}Ph678%<4#CptI ziJfb4FDK}2Tj5{l4cxBNhD2&k(|#Rq*%nHUvD;Gam>w|13!#1TP;6^p$vasp-eQQu z{UuLR%Bn#Y{L7us&Y@~ySJ$oA@$7&#Gp66cqHzWm*_86QX?pi0`~pS!ZVL&Y2Z+-l zUa>Ds^NQ^>U3U%vcGizwYwX=>owA}zs{BB6G=<6*SN(#2Y>#(rO_t7M$iooLGYzeg zs?gAFtUS!mGQ4qmL-(FSIGdk$b&oec=CUQ4?-pvS%$v`xpG5q=ecKf+YMPu$TEB3W4iV`F zQF_3U66uic?isoT1f)wkq`O;c28QmCl6{}BOO$cLg<;a&=4YSrd|OaG$EkVo<$)_6|_Hk+e0#ut;m^_ z$8Z$OO@Lc&q~pH>+b1;q0!9F`uYHitw3p_q-@WyELt0pR(adrd6K_&L$M}kZ7GuN- zVE)l90=^W4B2H|QRBA}Y>$A{1Z{abAO>m+5ezKexTtO@qV^~6_oxPiAO5=x4ey=q0 zR7{!tyPI3e3|6v0Ni_X<9CfFqr7>fm`uI0*N6%u)m*3^SyYgrDGvKn{s_`m$Y6j7d z?WDSML9o%((ro(+ZrkuKR>LG+%QiELGZdhSIYQ*1)clpqReIhRn7x^`N)LQEO_{or zWBFfUARS{IM?ETCCHOmEhEJvbTy3~{PS5zj9diR@gFhD5P^H8A&b)xXFS@o#6c1YI zNN;)r_UZ}oS2Pve^*A5dj;Cg?I1FY+y;Zm!rUPOUuQR|6mP1bBvAfLx`||to-?m0l ze82w;s`MYLS!;C#G`>$5Gm>5VIG=5#b=gT2xhHw(Om^f@;4-hqZ;QqUMOzqRE@Rjt z{^A%+QL5c z^Rfz>PdgP<9stL)d2*_4c3YDmp8UQ{b4O5*0xalN#TPA@Nk9DOxka71yrGlAY84;H z9SV&3KWhQ!@=8O$` z25KHB>pO@W^%%|bD3z4sebcvzaqosme$10$gv{oJGOcIVA4fe=kZ@fy%OrArPUPV; z*Gywbunyb_^EB1>B=r@4?-pHr?}wvhUp@y6OB2J%`yUXqbUpGdEh1#aL*;dXfT(F( zmU*vBL{Fo3e7)o}e(d71Wzm$kd|Rb;HA)n+ zOZ0#bptA#RmJB{ybUTUo?WO}@UKOB4T4Pb+gNDy^+GT1d4dmqAoFA}_g8-+ESXxa5 zXk|})&d_fXbbWA5nX8SS`-2EC;wSv|kJI_0tITh%Ai5+&3x&X zmukS(WFvS>T^Z1N!-4Bk>d8orb;$kF&Qdc|fz&4($q4>Zf}-N3#QTf86v3LmBUxXy zbbQ0jADA=hp$W}sUp45$0R;49lTZ_3n5gX8La%sZoHig z^6V!3j*ysU8v*5x+igSfm`QDEiHQlCzwdBT+n=KlB6~T-DCbDqrq&jh0&peKoJN$a z3?Zym5J3f11%$ynx9j+$BYFTA>~g*v!)f;IR|s&W3AnZ{=t8wN{w{h9t1KU>=z+D=88gPmAS`(fk9aRnJO|`LOIaUm|k~l>>@( zJT`^FlKi^L9aWbKQ3`ProXct%w4dHwZd)!K3fXyTQn~KjD(nSykpp_-4OE!DK#&`p z%qpI%!4(duh>orHQ!scRLhcUhqkoY4{H=%EGi#Gip!t_uUB;ICReZ?PKK7joEHv>~ zoF2nbIKF^9W&w`o$J2&9yk#`zo(rJQ`ZQF$i&w6!xbF5K>Y5YHq9L_ehQ^KT!~dIYrb zKVsR&?V#JThOOu7-#%d`$Y|$a*xfvH+}OVq$uzaN-%rjZZ#B z%usm?Yx=k)vqqLa=Y97=f9H(> zWbVzMUK2;}=^(?375Yd727+|WNF~$2SR4nF-Y>FG`JVl2H_aZXfti1TH}I8j@_*Exri@Z8R1?)skA z?3GTYK}IZd|1FHE&kLvvzvC zKG4*Yz_usVDj*pwYn^qeAzDRaTj5i@`g7(FlfFv0Ig38okY>X{N7(mdY?tI^MonhV zG!S_23lTo+th#>4Mtwe3cFa^=tT>nqL&bRWoj084?q>BQem(+akheZ4!2jy>mS4K! zychiojT0;JuvmX@eNkd9pO-WVRQ+P;<5kM{#UZCd@evq_P`gi@m^($V3D`MHq9XM8 zk@HD;)(2nI{}+SJAz2o2w$|Xo1Wo#7T=A|ueTpC>7eQC`d}KCt0{AdnGhjPQh89$2 zUI`F&LjrpdXA3uNY7D6Y`cQDH?V4(+YYLzO(tc&8E-Qi(S|ZE3z^X=gW)e43X-c8SlXuj$N6 zr>)8&nS9b;5nb%Rl3QIrx4iW1#_T|{V8%yg8G8vaOZ=~)gX`7;0{5@BF z6`Mk$@`zr^rnFz*ra>dC8UwQ$or?jSdxKEU9&=gnR8`n1F8)0_*D--l z%l05&&Rz-#GkSjd^ufW^HWV5;N@n{1YJfJGrMx(my2k4XzXJY~ZLh0s^M19^b2=Vd z)6Q85{X3-#TE^mI^JNYFYZ6W&D>1VFxv* z#YN=dr!77y*;=wPRri{QC(HiI@dtY;d>)qu-v%}L;~qdEDsa(Tf`f+}jF#6xxbn!ESDWbj?)t7p@z?YCK*)B>K|280Uklci?ERz!k-_7H6N zAGTUpAZ7honq8B1%~QgF1-iJ<;_&_WOnY=z<36EA6#{_76{Al*H5obn9uwiS${8>F zmzV$N-bIX;16h2;*sl>k0E{1_8CspJk5d zra~GQMgFZ6#}V!WqXc)kv&WH*#Y_pF=E0sN`4Qk{?m?9BBf~WcV=ZlL{e5EH)v9MI zyjt^JQv&a@nfyy6w@5>qm_{;p&!0z z6qlyM$!en=I^r#_WTdMmZX_h$DbBj=)Ud9lky|3zi*D`+ZZV!CdhB&E)w(Xz)orpG z0w-$JXU1JVd||R0h|w}}q31H!l5R|BX?|ol_4kf3WdGOvC&1|61IYFLTeC)U4?BI> zM!`D65=edKZ92HYxR{7aOAE1=MDRLB9j;sqIj&7{vr9Yz z9vqL}mzjHQQ34UJ@KftX=x0Q$SG^wK8BgDRucOo?+&){wU5dlB)^x6Uz`=h17p!(@ zaZ}rI86w6}qsyA(lljBP`9M&%jQf|Y>x&|3HiE)LeM*)G6DE^I!#NTMP2o1z#XaGA zTZ5LbO$P~l|B@n7%_97|IewC$u1+l3$n(L-g@Id7{T0&4^~MjwgH8;Iy9N|l1h1bp zK6uQQZ-XK)mq~sdb%krA$|*0eBk%v7p0S0MZa%%&sPG4-?zRYzW?KuK`uN5gSmgq= zdtxW1+7683fVLGYG|75%QYF+mR};aWR_{^8t=$-zYXS%-6tVW-2D!#`l42eo;U4bc zp6*4HDw$gN%DehX8f4om6v@<>Sd2$T`9nbT59PhPh`Wh%h)+EKht^_8WmDViAre8? zCpoy5p$0(L0+f9_pT%!afGYGZ+-Lm#-S*p6y_f4M9v$;-M30n&(CYdgX=JXMq8lS; z=vV#w1d5^KXDJfsx5yOz-Ii1q+x)JY^|q*|nE`lOiw9Sx1673RgyHYjiitif5cOk- zUB|kW*+pX)Rgg5bfP==5`HS9uFZR^n?QjoPnIBJ^a-97J3_P5ZIYxWjvfjf1<-x7Fpr-GySSeu}U0UP0BK~O5$1MAs)#bm{W`=Mv->ILSznHu=zC3~h zkbjjsKh`4GSWmpMbxYtAkfkQ4osi^1mrP^ssx6=ua40SgNt)(OnNy7Xnv24%+H%-m z&P|7o)L&P++ElsTHE0^%7=C;8S2KC4=c+gfx5kwAar?{0@Uu3pUmnV{Kv5p1uA+o^A}F@&uRG>=6l{9z z*(CO}zp0gTZBitxJgJ7bK`|&rL!}u_^A9D}<=DyEGJR0na!abcB1MB-hQ^V5>$gH_ zInQyl_|U$~^Zn*@cJHkYljM_s9N@E52`c<}9_!X<&0?{2fG^;l8p!0rF!T!;?;gDz zfd0@)@)Z4@9fr5UUfM%jHBVc$uOhyg9~vyRekE2Y;fulWG(r=NIU_pF5M8kF2cLCH z!~9=@c2~UOGroMq+$;8Q_G^)RkEp(yUP=IWt=%MK5{Q=pG9D&Hz|m`DK^e*z4dgpy7@{BQkf14=x8ilX3og!k(s+pqR~f4m@Hji6#PtNb z>e%$CBoaMiKm}Nv4|29NtzaZKUSND|4-m`uTKz2wYfO7G^Ril&n)ix>u~@IeLcxv~BH=G0eK3(yfM-T`dBopD`x=6cJa*daK= z3t7o;_=!MVw{7ymX5WU&N&OQeZL%F=loq*7k}+(s$Uz0ju_i_|^@x_&Cp%R=4Jmuh zSa(D6f>)o2d6LC73Iqn^9r~csP-DTb%%G|G8x49*&i!@Z>uByVOB%DktJtn7g20kv zV=9|)ADxe8JNnbiMpWnj3`=Rx&Yay=8h`awWPJX0Eu-ni$7Zoc!zsbVD04X-5YKc=ebv^C5<^P(=`)@0b!5w7j)4_j#YewYQV8xqlg37~VpDhy0DfYWSuLy-QA; z+OBKt{arPN1*Mz!jDoq_(1-~af3;y4GZ~mb-IRt1IX$1`7UAWoR4Y!2-5uZz2EfD0 z`QA0(MQm8a2?0U%9=A_J<%HkIP=S;-taKw)8 zWBNA0Qf0;3XkhYa9I#66bRD`HC3F6@wb!Zdi9T$p{?(ZcRl3isecmL+rVyGqE3!l@ zDxy-FR%PjXK%ITH)ME2a;Ow+4IH^y^fjz-u^4DWBJ;>Mrp`T}wu!!!nQ5`v)9DkeX zw+@BwN5D?Yj&j$Un0eW}?{sj&WolK=Q2HBK&a@L`&q!Ouzy1;HZrf9>|pzi zaI`bHr?_9FxF0a=!2loilk0`g((t{Ju^6;Ut4dBYzygeIfqGb)BSzyMGhJTAvS1!D z+-$!0LY-J&s0V=*CGVQ0%MymJ!a$erg+uw5r+t5%b*F+0& z@GcB=J6K;G``kv#8r zcv4{a84ey1e4NYu`_Y7;i~ldx zuD-e}T|BctSQB>odPm=KZOWk25LY_RS1`+7pe^i{A6mtj)zAZZ1YI0Fvk=1?hxe!i~I{(UMI_PyETBfI`R?{v!|xk^3vyM2{tk;Y%tKAH?`MT;*}40D~o zBQ4TsRSX<9r~W54#2GFV&tim)&bw|5ObYC{hns+iltt9MxdCMt^%1k>5~7?+oFZTP zw`^R7AkI6ftpr*pDNo`+2X-$erz2hJLq4ak8+`G-!};@MPB%-oR?gLn8wW@E7WXzs3D5^E5SRB^&7GVD*OTYufivy(G=~vnn!sRo)MJv)x7K|;-Td~Mdsw$< zbH@a(WU$uTu?9qY_!J{3eCnz*Yw@fDn=WE2q*D{`&>kb4ksH?$#msHif?4v*KR6 z$|t7Dj|F1_tf~t9@%(>H+)mMPDZohx>fTUpitbQ|`E8->z~S zhq(?=oE%PnqgjJ3zUDj$A+TOF%NtZ|vY>Vy{Ctq%)5pC1JHJ2>j_JGD`+W7$CE0n1 zE&Kbh_!8T5-r|$QLxisO;y{C(iHT}ubje$kM>O-1eYgDQ% zU04V2HV7QIaJjMU7*_Qs2kI-e9hF@4Q}i5TRowh+%rm7#(`^(91W}KrXnN5%o1bgL zdr%IIL>P!e{^tET?pMGgqSL;O(1XPoR)$?Ie(xwX)2ETYNV^qsWpS+ikw+LZM7Mb9 zA2Ts>cd~zI*k#avG7&K?J>lCwHu9e96U6_qJe3w_44&OGaKwAS=`u;=*{JfplS;YN z;CyWXn3Z;g*UcQJDQE#H=mrEZlp3+K}D_x3aN=p{X zTCHttsQ%b&E32TKA&LJ*fvFHm^oaV2U`V}jG**;;L5ofoC?bZm{st*9CQ#%BYOJ2h z`1~ez$B(FBhNj*rmswcx$l-OXSMEiV&%V#&LZ8(hz{*#-FdILm5VdzdFDh1S_-~oV zMcGcrL!oH6#}6oV&eYZT$*cQq>7eCkFN{Q_&3ut6fmHn@4dwWen600+a>H~--P0xe z7G;Ghho4>fx4e>!o~Ncd&6HhN3qGwLlY0DWF>vD9;*k=qa8s$jr#N8%cE$1fmr6e7 zo!TLK%9oRt>xOboRhp4TVy`HG*N8CaE0T!Akwb$y4SvUFBqUXaBx|i|e{Q2eO4J@A zQPe|klSXr%W>UT#N#$Xd5rEbq)~n}K3jV7&TDH&oL0nt(9l9;^JZlsPH?ABXVY+cP zD~tVNo5@dXO(Z`#K$5rfTjy=&a!=BZ3(`q|VqhcqFw6!%Vt4)aU}=wm2i(r@+XwnK zTGp@_Ie#FN@XR6i7n^bw2hc;OqrqbgPRAl^K>n^+c5Si# zV{6gI(r(rjsGN>u!7eKy>mJ*fMPEJ9e8SOO_vh8a#6yBa-v;ek_A2&AF<9gKukif<4xt9FhS@tnM7K2v?5x$cAP+grW%BHIZWY(SmGPcUe(NW; zN@+!@1#WSHL?I z$M{Z8e-+z?xeGq|!{e%sPA~85Zg%u?TqF=OLEVswvOb=AMMm$#9@BV>)i8%J$0Ij` zDX5;N%J9&iT^l3K`@K)|JA%J8`3|4&w>V^x;WP&N6+bB4d))Q+cWHS9Hc?lKumwl3 z1>+LHH~cF+wWMj|nX}P&%YXWMmuwi9661-tOi1^UBi6F`>v35`xQk!WzQI_}N)E~n zp%V=lV?Fm?`(YSsC&#;Sjucj0dlJ5WZ$=boFC(Gxslyo4Ox2++sj{=hNAecDA>tBgeR8@P%^ke_R{YGa=!un{j_bcTmGeNE*`BZ$y zW}p)3218RD#i;;$q2Q+lYVK0ZXF;&EM*6$?G^RR+xL>rT5x$+Hm@_d8*%_C@B&@+h zZgQnXq0ZgmBk4p7Use8GZ4^S5dIy*88jz-Mb*`JbZKMe;RnouVL^56+fCh^J(OP(!fSG;$Y@Zp;f$|g}mi9Z$# zAwzg}xRH`{+AosyJ*q{j_}&d4vaAvm{cXs-9I-rO*#CG0jxfYf6AUiNcC`1|n&H6t zjnz^^*va4mR1X+m*?ltKF=KjK!i@L4bI*Dt)=^XbMLuo)2w~4OLufPZw*@Dg z=2?R6jHSMf^n3Tf)}VP$EDqOdPBHEMX3_>j58ZD*)?@Yrx8f46Zfy(^GvupKakfI~e--SLYkB*X3H&J) z9E1w~(ab97hsUj28{Q+5v3~87fe6j?>{Z;^%9L;H{66KH(P6%bw#b5=&07uXFLNJ# zcM|)c{eKtA3MY`EBdtB)bi4B4qFO^Id0wHev++V+4kI<5MSOVSB?=(1PcSvcB-<;? zP0I3>YDajsV6+5VoGoACwoAr_!t6sv!DGZy&^p{*n%Yo3{)BB3P^+Z%eHfAO1k4?) z3R^^<&fy4bKqOR9lIu(~LfoWKvD?m2$|Ka@(I?B2y^Dozgcll?C0&}CJT08($;r3< zb_`Q4zI}l@h^AqEY2nP`84`BzQu0FZ2J#UHMq4{k0MGLYQ5%T-A|Ax6w2}-sRU-{< zQ`CwDWIj9iyn-yq^8ALLjKDZz==gS;???4eA;Wh+ESi_tUzIF)Y!3&!V%K1@y_d-u zX3)E4fkTCk^o?3$gjp@Q9TST3DJ&ba05>u~ITcmhZ#`@P+0YJ)+2pUrY$P-d^x6dl#h>uxK+qGv@dQ z`3IJHM&pzESId~*SKM;@turPwXlqVbdEFhSrEtMZk*xrE{Qw!+h@qnyp~j_$qsk+SDJw_e=_QIDh_b@*oo^9Xtw0ZVcn`(zWd95F*!+$7o zftJn{k1rEn12!WNg+&}L7l1{KgUXusw7HOdXDDXg1xU&J?Ji{IXQkjIKIt;Uy6(Av(s-#&g4s%CnV%I{OFY zKU|-=Z}<9pe|Xmo^$dH*@gwH|6}6c76K2vyQMS{3^mdq-$}te{D2*{kqreu|yG{yc zCS;}}VWrTj#&%E_^3zdV*LXR^focc}5u=R6C0gk(5<|uAd1+k9rJ8%Q(l@nr83D?A zsisLidNI~ZfE~>$P|yB&Nl&epT6rFj$QVkNp3x=)3=>E<9el7RE>bT^uE+K7cp%l- z^#(ZeVP7>FiP(A{MET5da6kOKm9xcdvfHm)g3MW`QK+qc@&rwIoXgu#*N^e6U|vQa z?xgIIYol1^R#o0#a{IG65fW;lAU>P!D{f@rYPosB%*Vkk);9n5P_{SBqD2kcrCJ!_ zpC9Y|uU16`W7TSDGez)N){%Ad{KY2N0 zJKs7YyruUHvU&7pyFv5aBtU^8HVq@Ew~k%`uc?~@Ltxnui~rV1kUz@j2P8k>kFImE kH4#B<%F0oZ+GnI(6R!scYnv*>K)V+*5{lyGVut?z2Q&b?fB*mh literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native/a/a-main.json b/corpus/conflict-page/native/a/a-main.json new file mode 100644 index 0000000000000000000000000000000000000000..668f002c22e396d7530675ec44f659f85b6f6a86 --- /dev/null +++ b/corpus/conflict-page/native/a/a-main.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Client B conflicting edit. - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native/a/a-main.png b/corpus/conflict-page/native/a/a-main.png new file mode 100644 index 0000000000000000000000000000000000000000..160e31b2ff70f2b56ede40769422db0f0e81083d GIT binary patch literal 48904 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native/a/a-shown.json b/corpus/conflict-page/native/a/a-shown.json new file mode 100644 index 0000000000000000000000000000000000000000..2fd05a4a7938577ce0347213e7f3ed53dab2c0a4 --- /dev/null +++ b/corpus/conflict-page/native/a/a-shown.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Client A conflicting edit. - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native/a/a-shown.png b/corpus/conflict-page/native/a/a-shown.png new file mode 100644 index 0000000000000000000000000000000000000000..324161780c3b6bc1d4a76ead6a5e5c567c003dda GIT binary patch literal 47596 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 opJqOME_pdCc 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 literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native/b/b-main.json b/corpus/conflict-page/native/b/b-main.json new file mode 100644 index 0000000000000000000000000000000000000000..668f002c22e396d7530675ec44f659f85b6f6a86 --- /dev/null +++ b/corpus/conflict-page/native/b/b-main.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Client B conflicting edit. - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native/b/b-main.png b/corpus/conflict-page/native/b/b-main.png new file mode 100644 index 0000000000000000000000000000000000000000..6fc48cfee0af3e8e1a5eb28b9554a742fd124c56 GIT binary patch literal 49003 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 P>xoyj;a|c5q4ocN!r7Hq literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native/linux-teardown.json b/corpus/conflict-page/native/linux-teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/conflict-page/native/linux-teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/conflict-page/native/linux.json b/corpus/conflict-page/native/linux.json new file mode 100644 index 0000000000000000000000000000000000000000..51c769af931eb724e1a4f34d25afa486513f27ae --- /dev/null +++ b/corpus/conflict-page/native/linux.json @@ -0,0 +1,11 @@ +{ + "cpus": 2, + "disk_gb": 16, + "hostname": "one-conflict", + "lab_address": "192.168.77.1", + "lab_mac": "52:54:04:69:39:00", + "memory_mb": 2048, + "samba_port": 14450, + "ssh_port": 22000, + "wan_mac": "52:54:03:76:a2:ae" +} \ No newline at end of file diff --git a/corpus/conflict-page/native/native_conflict.py b/corpus/conflict-page/native/native_conflict.py new file mode 100644 index 0000000000000000000000000000000000000000..ba5057280e4efb096969c86293a53973fdb8a19d --- /dev/null +++ b/corpus/conflict-page/native/native_conflict.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +"""OneNote 2010 conflict pages: how they arise, what they store and how OneNote shows them. + +`sync LINUX OUTPUT` runs two disposable OneNote clients on one Samba notebook: client A edits +two paragraphs offline while client B edits one of them and a table cell online; A then +reconnects, which makes OneNote write a conflict page. The server file is kept before and +after, A and B are screenshotted with the information bar, the page list shown, the conflict +page open and its menu open, and A then deletes the conflict page through that menu. + +`cold NOTEBOOK OUTPUT [--delete]` cold-opens a notebook holding a conflict page in a fresh +clone and screenshots the same views; `--delete` deletes the conflict page through the menu +and captures the notebook OneNote leaves. +""" +import argparse +from contextlib import ExitStack +from concurrent.futures import ThreadPoolExecutor +import base64 +import json +from pathlib import Path +import shutil +import subprocess +import sys +import time +import xml.etree.ElementTree as ET + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +sys.path.insert(0, str(Path(__file__).resolve().parent / 'w7')) +import native_runner as runner +from native_runner import ROOT, clone, command, windows, vm +import linux_vm + +FIXTURE = ROOT / 'corpus/native-ink/cold-ui-ink/notebook' +SHARE = 'm6-conflict' +ORIGINAL = 'Fictitious: café, 東京, مرحبا' +OTHER_OUTLINE = 'Fictitious positioned outline.' +CELL = 'Left cell' + +# Coordinates on OneNote 2010 maximized at 800x600, from the stage-5 and m8 captures: the +# information bar's text, and the first conflict page listed under its page. +BAR = (332, 100) +FIRST_CONFLICT = (725, 142) + + +def shot(name, output, label, script): + """Runs an AutoHotkey step on the clone, keeping its screenshot as `label.png`.""" + (output / f'{label}.ahk').write_text(script) + result = windows.do_exec(script, target=name, timeout_ms=120000, shot_delay_ms=1500) + screenshot = result.pop('png_b64', None) + if screenshot: + (output / f'{label}.png').write_bytes(base64.b64decode(screenshot)) + (output / f'{label}.json').write_text(json.dumps(result, indent=2)) + if result.get('error') or result.get('exit') != 0: + raise RuntimeError(f'{label}: {result}') + return result + + +def click(x, y): + return ('WinActivate("ahk_exe ONENOTE.EXE")\nCoordMode("Mouse", "Screen")\n' + f'Click({x}, {y})\nSleep(1200)\n') + + +# Reports any dialog OneNote raised, so a confirmation shows in the step's record. +DIALOG = ('if WinExist("ahk_class #32770 ahk_exe ONENOTE.EXE") {\n' + ' FileAppend("dialog: " WinGetTitle() " | " WinGetText() "`n", "*", "UTF-8")\n' + '}\n') + + +def conflict_views(name, output, page, prefix, delete=False): + """Screenshots a page with a conflict page: the bar, the list shown, the conflict page and + its menu; `delete` then chooses Delete Conflict Page.""" + shot(name, output, f'{prefix}-main', runner.navigation_script(page) + 'Sleep(2000)\n') + shot(name, output, f'{prefix}-shown', click(*BAR)) + shot(name, output, f'{prefix}-conflict', click(*FIRST_CONFLICT)) + shot(name, output, f'{prefix}-menu', click(*BAR)) + if delete: + shot(name, output, f'{prefix}-delete', 'WinActivate("ahk_exe ONENOTE.EXE")\nSend("d")\nSleep(1500)\n' + DIALOG) + else: + shot(name, output, f'{prefix}-closed', 'WinActivate("ahk_exe ONENOTE.EXE")\nSend("{Esc}")\nSleep(800)\n') + + +def cold(notebook, output, delete): + def interaction(name, output): + page = ET.parse(sorted((output / 'read').glob('page-*.xml'))[0]).getroot().attrib['ID'] + conflict_views(name, output, page, 'cold', delete) + if delete: + # OneNote writes the deletion in the background; give it time before the re-read. + time.sleep(20) + runner.capture(notebook, output, collect_notebook=True, interaction=interaction) + + +def sync(server, output): + from native_collaboration import reachable_page_text + from document_model import EXPORTER + + if linux_vm.instance_path(server).exists(): + raise ValueError('Choose a new Linux VM name; existing machines are not owned by this run.') + output = output.resolve() + output.mkdir(parents=True, exist_ok=False) + scripts = output / 'scripts' + scripts.mkdir() + for name in ('cold.ps1', 'collaborate.ps1', 'network.ps1', 'text.ps1', 'stress.ps1'): + shutil.copyfile(ROOT / 'tools/native' / name, scripts / name) + shutil.copyfile(Path(__file__), output / 'native_conflict.py') + + def ssh(text, timeout=90): + result = linux_vm.run_ssh(server, text, timeout=timeout) + with (output / 'server.jsonl').open('a') as log: + log.write(json.dumps({'command': text, 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr}) + '\n') + result.check_returncode() + return result.stdout + + def server_state(label): + """Copies the server's notebook until it parses, keeping it as `label/notebook`.""" + deadline = time.monotonic() + 120 + while True: + target = output / label + shutil.rmtree(target, ignore_errors=True) + (target / 'notebook').mkdir(parents=True) + for name in ('synthetic.one', 'Open Notebook.onetoc2'): + with (target / 'notebook' / name).open('wb') as stream: + subprocess.run(linux_vm.ssh_argv(server, f"cat '/srv/agent/{SHARE}/{name}'"), stdout=stream, check=True, timeout=60) + result = subprocess.run([EXPORTER, target / 'notebook/synthetic.one', target / 'model'], capture_output=True, text=True) + if result.returncode == 0: + model = json.loads((target / 'model/document.json').read_text()) + return reachable_page_text(model) + if time.monotonic() > deadline: + raise RuntimeError(f'{label}: {result.stderr}') + time.sleep(1) + + linux_vm.create_instance(server) + try: + linux_vm.launch(server) + linux_vm.wait_instance(server, 600) + (output / 'linux.json').write_text(json.dumps(linux_vm.load_instance(server), indent=2)) + ssh(f'mkdir /srv/agent/{SHARE}') + for name in ('synthetic.one', 'Open Notebook.onetoc2'): + with (FIXTURE / name).open('rb') as stream: + subprocess.run(linux_vm.ssh_argv(server, f"cat > '/srv/agent/{SHARE}/{name}'"), stdin=stream, check=True) + ssh(f'chmod u+w /srv/agent/{SHARE}/*') + with ExitStack() as stack: + labels = ['a', 'b'] + for label in labels: + (output / label).mkdir() + + def start(label): + manager = clone(output / label) + name = manager.__enter__() + return label, name, manager + with ThreadPoolExecutor(max_workers=2) as pool: + started = list(pool.map(start, labels)) + for _, _, manager in started: + stack.push(manager) + clients = {} + for label, name, _ in started: + folder = output / label + for local in scripts.iterdir(): + remote = 'cold-current.ps1' if local.name == 'cold.ps1' else local.name + result = windows.do_put(local, 'C:\\one-tests\\' + remote, name) + if result.get('error'): raise RuntimeError(result['error']) + command(name, 'mkdir C:\\one-tests\\runs\\capture', folder) + command(name, 'powershell -NoProfile -ExecutionPolicy Bypass -File C:\\one-tests\\network.ps1 -LabMac ' + vm.lab_mac(name), folder) + result = windows.do_spawn('cmd /c powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\collaborate.ps1 -Root C:\\one-tests\\runs\\capture -SharedPath \\\\192.168.77.1\\agent\\' + SHARE + ' -CloneHost ONE-' + name.upper() + ' > C:\\one-tests\\runs\\capture\\controller.log 2>&1', name) + if result.get('error'): raise RuntimeError(result['error']) + deadline = time.monotonic() + 180 + while windows.do_cmd('if exist C:\\one-tests\\runs\\capture\\ready (echo ready)', target=name).get('stdout', '').find('ready') < 0: + if time.monotonic() > deadline: raise RuntimeError('The collaboration controller did not open the notebook') + time.sleep(1) + clients[label] = {'name': name, 'folder': folder, 'sequence': 0} + (output / 'clients.json').write_text(json.dumps({label: client['name'] for label, client in clients.items()}, indent=2)) + + def action(client, kind, **parameters): + client['sequence'] += 1 + sequence = client['sequence'] + local = client['folder'] / f'command-{sequence:04}.json' + local.write_text(json.dumps({'action': kind, **parameters}, ensure_ascii=False)) + inbox = f'C:\\one-tests\\runs\\capture\\inbox\\{sequence:04}' + result = windows.do_put(local, inbox + '.tmp', client['name']) + if result.get('error'): raise RuntimeError(result['error']) + command(client['name'], f'move {inbox}.tmp {inbox}.json', client['folder']) + remote = f'C:\\one-tests\\runs\\capture\\outbox\\{sequence}' + deadline = time.monotonic() + 600 + while True: + result = windows.do_cmd(f'if exist {remote}\\error (type {remote}\\error & exit /b 1) else (if exist {remote}\\done (echo complete))', target=client['name']) + if result.get('exit') != 0: raise RuntimeError(str(result)) + if 'complete' in result.get('stdout', ''): break + if time.monotonic() > deadline: raise TimeoutError(f'{kind} did not complete') + time.sleep(.5) + if kind != 'snapshot': + return None + hierarchy = client['folder'] / f'hierarchy-{sequence:04}.xml' + windows.do_get(remote + '\\hierarchy.xml', hierarchy, client['name']) + pages = [node for node in ET.fromstring(hierarchy.read_text(encoding='utf-8-sig').strip()).iter() if node.tag.endswith('}Page')] + for i in range(len(pages)): + windows.do_get(remote + f'\\page-{i}.xml', client['folder'] / f'snapshot-{sequence:04}-{i}.xml', client['name']) + return pages + + def settle(client, check, label): + deadline = time.monotonic() + 300 + while True: + action(client, 'sync') + state = server_state(label) + if check(state): return state + if time.monotonic() > deadline: raise TimeoutError(f'{label}: {state}') + time.sleep(2) + + a, b = clients['a'], clients['b'] + texts = lambda state: {text for values in state.values() for text in values} + server_state('initial') + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': False}) + try: + action(a, 'edit', expected=ORIGINAL, text='Client A conflicting edit.') + action(a, 'edit', expected=OTHER_OUTLINE, text='Client A disjoint edit.') + action(a, 'snapshot') + action(b, 'edit', expected=ORIGINAL, text='Client B conflicting edit.') + action(b, 'edit', expected=CELL, text='Client B disjoint cell.') + settle(b, lambda state: {'Client B conflicting edit.', 'Client B disjoint cell.'} <= texts(state), 'b-published') + finally: + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': True}) + state = settle(a, lambda state: len(state) > 1 and 'Client A disjoint edit.' in texts(state), 'conflict') + (output / 'conflict.json').write_text(json.dumps(state, indent=2, ensure_ascii=False)) + page = action(a, 'snapshot')[0].attrib['ID'] + conflict_views(a['name'], output / 'a', page, 'a') + action(b, 'sync') + time.sleep(5) + page_b = action(b, 'snapshot')[0].attrib['ID'] + conflict_views(b['name'], output / 'b', page_b, 'b') + conflict_views(a['name'], output / 'a', page, 'a-again', delete=True) + state = settle(a, lambda state: len(state) == 1, 'deleted') + (output / 'deleted.json').write_text(json.dumps(state, indent=2, ensure_ascii=False)) + action(b, 'sync') + time.sleep(5) + shot(b['name'], output / 'b', 'b-after-delete', runner.navigation_script(page_b) + 'Sleep(2000)\n') + for client in clients.values(): + try: + action(client, 'close') + except Exception as error: + (client['folder'] / 'close-failure.txt').write_text(str(error)) + server_state('final') + finally: + if linux_vm.running(server): + linux_vm.shutdown(server, 120) + linux_vm.delete_instance(server) + (output / 'linux-teardown.json').write_text(json.dumps({'absent': not linux_vm.instance_path(server).exists()}) + '\n') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + commands = parser.add_subparsers(dest='mode', required=True) + lab = commands.add_parser('sync') + lab.add_argument('linux') + lab.add_argument('output', type=Path) + reopen = commands.add_parser('cold') + reopen.add_argument('notebook', type=Path) + reopen.add_argument('output', type=Path) + reopen.add_argument('--delete', action='store_true') + args = parser.parse_args() + if args.mode == 'sync': + sync(args.linux, args.output) + else: + cold(args.notebook, args.output, args.delete) + print('Captured', args.output) diff --git a/corpus/conflict-page/native/scripts/cold.ps1 b/corpus/conflict-page/native/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/conflict-page/native/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/conflict-page/native/scripts/collaborate.ps1 b/corpus/conflict-page/native/scripts/collaborate.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..4a13a4320675250d49eac958bd3a020ca3b882f6 --- /dev/null +++ b/corpus/conflict-page/native/scripts/collaborate.ps1 @@ -0,0 +1,105 @@ +param([Parameter(Mandatory=$true)][string]$Root, + [Parameter(Mandatory=$true)][string]$SharedPath, + [Parameter(Mandatory=$true)][string]$CloneHost, + [switch]$ResumeCache, + [int]$StartSequence = 1) +function Keep-Container($page, $target) { + $container = $target + while ($container.ParentNode -ne $page.DocumentElement) { $container = $container.ParentNode } + foreach ($sibling in @($page.DocumentElement.ChildNodes)) { + if ($sibling -ne $container -and $sibling.LocalName -in @('Outline', 'Title', 'Image', 'InkDrawing')) { + [void]$page.DocumentElement.RemoveChild($sibling) + } + } +} +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\text.ps1" +if ($SharedPath -notmatch '^\\\\192\.168\.77\.1\\agent\\m6-[a-z0-9-]+$') { throw 'Choose a disposable Linux lab notebook.' } +if ($env:COMPUTERNAME -ne $CloneHost) { throw 'The collaboration controller requires its owned clone.' } +if (-not $ResumeCache) { & "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost } +$shared = $SharedPath +if (-not (Test-Path "$shared\synthetic.one")) { throw 'Create the shared fixture first.' } +New-Item -ItemType Directory "$Root\inbox", "$Root\outbox" -Force | Out-Null +$user = 'HKCU:\Software\Microsoft\Office\Common\UserInfo' +New-Item $user -Force | Out-Null +New-ItemProperty $user -Name UserName -Value $CloneHost -PropertyType String -Force | Out-Null +New-ItemProperty $user -Name UserInitials -Value $CloneHost.Substring($CloneHost.Length - 3) -PropertyType String -Force | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy($shared, '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0) + [IO.File]::WriteAllText("$Root\ready", 'ready') + $deadline = [DateTime]::UtcNow.AddMinutes(30) + $sequence = $StartSequence + while ([DateTime]::UtcNow -lt $deadline) { + $name = '{0:d4}.json' -f $sequence + $input = "$Root\inbox\$name" + if (-not (Test-Path $input)) { Start-Sleep -Milliseconds 100; continue } + $command = Get-Content -Raw -Encoding UTF8 $input | ConvertFrom-Json + $output = "$Root\outbox\$sequence" + New-Item -ItemType Directory $output | Out-Null + try { + if ($command.action -in @('prepare-stress', 'stress')) { + . "$PSScriptRoot\stress.ps1" + Invoke-Stress $app $section $command $output $shared + } + elseif ($command.action -eq 'sync') { $app.SyncHierarchy($notebook) } + elseif ($command.action -eq 'kill-process') { + Get-Process ONENOTE | Stop-Process -Force + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = New-Object -ComObject OneNote.Application + $app.OpenHierarchy($shared, '', [ref]$notebook, 0) + $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0) + } + elseif ($command.action -eq 'edit' -or $command.action -eq 'snapshot') { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText("$output\hierarchy.xml", $hierarchy, [Text.Encoding]::UTF8) + [xml]$tree = $hierarchy + $matches = 0 + $editedPage = $null + $i = 0 + foreach ($node in $tree.SelectNodes('//*[local-name()="Page"]')) { + $content = '' + $app.GetPageContent($node.GetAttribute('ID'), [ref]$content, 1, 1) + [IO.File]::WriteAllText("$output\page-$i.xml", $content, [Text.Encoding]::UTF8) + [xml]$page = $content + if ($command.action -eq 'edit') { + $targets = @($page.SelectNodes('//*[local-name()="T"]') | Where-Object { + (Get-ParagraphText $_.InnerText) -eq $command.expected + }) + if ($targets.Count -gt 1) { throw 'Expected one matching text object.' } + if ($targets.Count -eq 1) { + $matches++ + if ($matches -gt 1) { throw 'The text occurs on multiple pages.' } + $targets[0].InnerText = [Security.SecurityElement]::Escape($command.text) + Keep-Container $page $targets[0] + $editedPage = $page + } + } + $i++ + } + if ($command.action -eq 'edit' -and $matches -ne 1) { throw 'Expected one matching text object.' } + if ($editedPage) { $app.UpdatePageContent($editedPage.OuterXml, [DateTime]::MinValue, 1, $false) } + } elseif ($command.action -ne 'close') { throw 'Unknown collaboration command.' } + [IO.File]::WriteAllText("$output\done", 'ok') + } catch { + [IO.File]::WriteAllText("$output\error", $_.Exception.ToString()) + } + if ($command.action -eq 'close') { break } + $sequence++ + } +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + [IO.File]::WriteAllText("$Root\closed", 'closed') + } +} diff --git a/corpus/conflict-page/native/scripts/network.ps1 b/corpus/conflict-page/native/scripts/network.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..95fa83d5243ee6a2a653cc075128b063e2f09cf2 --- /dev/null +++ b/corpus/conflict-page/native/scripts/network.ps1 @@ -0,0 +1,20 @@ +param([Parameter(Mandatory=$true)][string]$LabMac) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$adapter = @(Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.MACAddress -eq $LabMac }) +if ($adapter.Count -ne 1) { throw 'The clone lab adapter is ambiguous.' } +$enabled = $adapter[0].EnableDHCP() +if ($enabled.ReturnValue -notin @(0, 1)) { + throw ('Unable to enable DHCP on the clone lab adapter; result ' + $enabled.ReturnValue) +} +$release = $adapter[0].ReleaseDHCPLease() +$result = $adapter[0].RenewDHCPLease() +$deadline = [DateTime]::UtcNow.AddSeconds(60) +do { + $adapter = @(Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.MACAddress -eq $LabMac }) + $addresses = @($adapter[0].IPAddress | Where-Object { $_ -match '^192\.168\.77\.\d+$' -and $_ -ne '192.168.77.1' }) + if ($addresses.Count -eq 1) { break } + Start-Sleep -Milliseconds 250 +} while ([DateTime]::UtcNow -lt $deadline) +if ($addresses.Count -ne 1) { throw ('The clone did not receive a lab IPv4 address; renewal result ' + $result.ReturnValue + '; adapter ' + ($adapter[0] | Select-Object MACAddress,DHCPEnabled,IPEnabled,IPAddress | ConvertTo-Json -Compress)) } +@{mac=$LabMac;address=$addresses[0];releaseResult=$release.ReturnValue;renewalResult=$result.ReturnValue} | ConvertTo-Json -Compress diff --git a/corpus/conflict-page/native/scripts/stress.ps1 b/corpus/conflict-page/native/scripts/stress.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0ab5bc69b4dabe970373e481873dfd951a1a69a --- /dev/null +++ b/corpus/conflict-page/native/scripts/stress.ps1 @@ -0,0 +1,83 @@ +function Invoke-Stress($app, $section, $command, $output, $shared) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText("$output\hierarchy.xml", $hierarchy, [Text.Encoding]::UTF8) + [xml]$tree = $hierarchy + $pages = @($tree.SelectNodes('//*[local-name()="Page"]')) + if ($pages.Count -ne 1) { throw 'Expected one stress-test page.' } + $pageId = $pages[0].GetAttribute('ID') + if ($command.action -eq 'prepare-stress') { + for ($i = 0; $i -lt $command.clients; $i++) { + $xml = '' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + } + return + } + $random = New-Object Random([int]$command.seed) + $events = New-Object IO.StreamWriter("$output\events.jsonl", $false, [Text.Encoding]::UTF8) + $events.AutoFlush = $true + try { + [IO.File]::WriteAllText("$output\ready", 'ready') + $deadline = [DateTime]::UtcNow.AddMinutes(10) + while (-not (Test-Path "$shared\stress-start")) { + if ([DateTime]::UtcNow -gt $deadline) { throw 'Stress start timed out.' } + Start-Sleep -Milliseconds 20 + } + for ($i = 0; $i -lt $command.operations; $i++) { + if (($command.PSObject.Properties.Name -contains 'maintenance') -and $command.maintenance -and $i -eq [Math]::Floor($command.operations / 2)) { + [IO.File]::WriteAllText("$shared\maintenance-paused-n$($command.actor)", 'paused') + while (-not (Test-Path "$shared\maintenance-resume")) { + if ([DateTime]::UtcNow -gt $deadline) { throw 'Maintenance pause timed out.' } + Start-Sleep -Milliseconds 100 + } + } + Start-Sleep -Milliseconds $random.Next(10, 100) + $started = [DateTime]::UtcNow.Ticks + $content = '' + $app.GetPageContent($pageId, [ref]$content, 1, 1) + [xml]$page = $content + $matches = @($page.SelectNodes('//*[local-name()="T"]') | Where-Object { + (Get-ParagraphText $_.InnerText).StartsWith($command.prefix) + }) + if ($matches.Count -ne 1) { throw 'Expected one stress-test paragraph.' } + $before = Get-ParagraphText $matches[0].InnerText + $token = ' [n' + $command.actor + ':' + $i + ']' + $start = $end = $before.Length + $replacement = $token + $bold = $italic = $false + if ($command.edit) { + $boundaries = New-Object 'Collections.Generic.List[int]' + for ($at = $command.prefix.Length; $at -le $before.Length; $at++) { + $boundaries.Add($at) + if ($at -lt $before.Length -and [char]::IsHighSurrogate($before[$at])) { $at++ } + } + $first = $boundaries[$random.Next($boundaries.Count)] + $second = $boundaries[$random.Next($boundaries.Count)] + $start = [Math]::Min($first, $second) + $end = [Math]::Max($first, $second) + $replacement = ' caf' + [char]0xe9 + ' ' + [char]::ConvertFromUtf32(0x1f980) + $token + $bold = $random.Next(2) -eq 1 + $italic = $random.Next(2) -eq 1 + } + $after = $before.Substring(0, $start) + $replacement + $before.Substring($end) + $html = [Security.SecurityElement]::Escape($after) + if ($command.edit) { + $weight = if ($bold) { 'bold' } else { 'normal' } + $style = if ($italic) { 'italic' } else { 'normal' } + $html = '' + $html + '' + } + $matches[0].InnerText = $html + Keep-Container $page $matches[0] + $updateStarted = [DateTime]::UtcNow.Ticks + $app.UpdatePageContent($page.OuterXml, [DateTime]::MinValue, 1, $false) + $updated = [DateTime]::UtcNow.Ticks + $events.WriteLine((@{ operation=$i; before=$before; token=$token; started_ticks=$started; + range=@($start, $end); replacement=$replacement; bold=$bold; italic=$italic; + update_started_ticks=$updateStarted; updated_ticks=$updated } | ConvertTo-Json -Compress)) + if ($i -eq 0) { [IO.File]::WriteAllText("$output\editing", 'editing') } + if ($command.sync_every -gt 0 -and (($i + 1) % $command.sync_every) -eq 0) { + $app.SyncHierarchy($section) + } + } + } finally { $events.Dispose() } +} diff --git a/corpus/conflict-page/native/scripts/text.ps1 b/corpus/conflict-page/native/scripts/text.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..75e9e71207b599d16eaaf68824ca30831860610a --- /dev/null +++ b/corpus/conflict-page/native/scripts/text.ps1 @@ -0,0 +1,13 @@ +function Get-ParagraphText([string]$html) { + $plain = [regex]::Replace($html, '<[^>]*>', '') + $plain = [regex]::Replace($plain, '&#(?:x([0-9a-fA-F]+)|([0-9]+));', { + param($match) + $value = if ($match.Groups[1].Success) { + [Convert]::ToInt32($match.Groups[1].Value, 16) + } else { [int]$match.Groups[2].Value } + # The Win7 framework leaves supplementary numeric entities undecoded. + if ($value -gt 0xffff -and $value -le 0x10ffff) { [char]::ConvertFromUtf32($value) } + else { $match.Value } + }) + [Net.WebUtility]::HtmlDecode($plain) +} diff --git a/corpus/conflict-page/native/server.jsonl b/corpus/conflict-page/native/server.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..d5df993564a30ddaea1b427699648ff9b1a1417d --- /dev/null +++ b/corpus/conflict-page/native/server.jsonl @@ -0,0 +1,2 @@ +{"command": "mkdir /srv/agent/m6-conflict", "exit": 0, "stdout": "", "stderr": ""} +{"command": "chmod u+w /srv/agent/m6-conflict/*", "exit": 0, "stdout": "", "stderr": ""} diff --git a/corpus/extended-ascii/README.md b/corpus/extended-ascii/README.md new file mode 100644 index 0000000000000000000000000000000000000000..873405ca5918c84f20bd12c78c2bef1eb7adb4cb --- /dev/null +++ b/corpus/extended-ascii/README.md @@ -0,0 +1,9 @@ +# Extended ASCII text + +`TextExtendedAscii` (`0x1c003498`, MS-ONE 2.2.89) names no code page. Each `candidate` +is `m6/ansi/notebook` with the paragraph's `RichEditTextLangID` (`0x10001cfe`) set to +0x0409 (`en`), 0x0419 (`ru`, ANSI code page 1251) or 0x0411 (`ja`, code page 932); its +text holds the bytes 0x80–0xFF. `cold` is each one's fresh OneNote 2010 read: all three +export U+0080–U+00FF, so OneNote reads the bytes as Latin-1 whatever the language, and +`read/page-000.png` shows 0x80–0x9F as nothing and 0xC0 as À rather than Cyrillic А. +OneNote left the section files unchanged. diff --git a/corpus/extended-ascii/en/candidate/Open Notebook.onetoc2 b/corpus/extended-ascii/en/candidate/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..a33d3bfd210f844e3db618e648ac898adeee09c6 GIT binary patch literal 4816 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypWOaMy5#LF~y)9Oxb$#>r}|Kyh?&t}bosmDf_Y+J~WjIpU16&nqK zp&kN9zI^|>u_|nd0MsKKISf-97*&4foBm+C_tSH2N_*D3xv@JKfzm7q4Gq}Dhk6i< zx_>kTMnni8*O}hCjGGRN9_ohWdr$_yJe4T`#AgCxK_CW+fpB`~EME{GBo3--Vd6H) z_ZEZrTu?F7Y>{LTAEsuiuGdl!TLvnx-CJk`WR8N-5Eu=CK@tM6`X7|PL4XHGB`+Yu zFm(x|3PjN`C9#!{ufV&YS{i30vEq40iLJuR~o}CUeKQ!^!fyzp3;DeQC z>=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jgw6K&`@AZW{ zcSu}5J`to<*Mp(=1S2D?1O}GPEh!8=$Q}Wi2mSTaM{uf|q0Lu_fMDRhC zvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf@C)Q}pzcvH z8UlkT1dxjxSh)a9#^BP130x+8lVMN+l?$Mx4+jk_3{zwoWOf3}jBFqcDmy@}PaB8! z;05iU+}4;?89zwhcNL@u)PgK#sAR}vC}GH8NClFa49N_74EaEjRG=Kl1waZ20OIF{ A&Hw-a literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/en/candidate/synthetic.one b/corpus/extended-ascii/en/candidate/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..d2ed44f1e9f620a51ba6e44d52867ebcb5d9ee16 GIT binary patch literal 12456 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 literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/en/cold/commands.jsonl b/corpus/extended-ascii/en/cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..1b14b83ce031e1dc4b11a4d3f159a39c13615d7a --- /dev/null +++ b/corpus/extended-ascii/en/cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-A375E105 -ExpectedPages 1 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/extended-ascii/en/cold/machine.json b/corpus/extended-ascii/en/cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..57863a786fdc66a1bf4d1f75151759a2f20903fc --- /dev/null +++ b/corpus/extended-ascii/en/cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-a375e105", "hostname": "ONE-M6-A375E105"} diff --git a/corpus/extended-ascii/en/cold/notebook/Open Notebook.onetoc2 b/corpus/extended-ascii/en/cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..a33d3bfd210f844e3db618e648ac898adeee09c6 GIT binary patch literal 4816 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypWOaMy5#LF~y)9Oxb$#>r}|Kyh?&t}bosmDf_Y+J~WjIpU16&nqK zp&kN9zI^|>u_|nd0MsKKISf-97*&4foBm+C_tSH2N_*D3xv@JKfzm7q4Gq}Dhk6i< zx_>kTMnni8*O}hCjGGRN9_ohWdr$_yJe4T`#AgCxK_CW+fpB`~EME{GBo3--Vd6H) z_ZEZrTu?F7Y>{LTAEsuiuGdl!TLvnx-CJk`WR8N-5Eu=CK@tM6`X7|PL4XHGB`+Yu zFm(x|3PjN`C9#!{ufV&YS{i30vEq40iLJuR~o}CUeKQ!^!fyzp3;DeQC z>=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jgw6K&`@AZW{ zcSu}5J`to<*Mp(=1S2D?1O}GPEh!8=$Q}Wi2mSTaM{uf|q0Lu_fMDRhC zvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf@C)Q}pzcvH z8UlkT1dxjxSh)a9#^BP130x+8lVMN+l?$Mx4+jk_3{zwoWOf3}jBFqcDmy@}PaB8! z;05iU+}4;?89zwhcNL@u)PgK#sAR}vC}GH8NClFa49N_74EaEjRG=Kl1waZ20OIF{ A&Hw-a literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/en/cold/notebook/synthetic.one b/corpus/extended-ascii/en/cold/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..d2ed44f1e9f620a51ba6e44d52867ebcb5d9ee16 GIT binary patch literal 12456 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 literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/en/cold/read/environment.json b/corpus/extended-ascii/en/cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..fcb5a8538909915c2c9f4a7c9e1a70717a4d7647 --- /dev/null +++ b/corpus/extended-ascii/en/cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-A375E105", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/extended-ascii/en/cold/read/hierarchy.xml b/corpus/extended-ascii/en/cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..fa4d29784012bad3829a8ecd7052cd044813a56f --- /dev/null +++ b/corpus/extended-ascii/en/cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/extended-ascii/en/cold/read/page-000.navigation.json b/corpus/extended-ascii/en/cold/read/page-000.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..f55849761016bf753db6dc26da07ab49553ce0a6 --- /dev/null +++ b/corpus/extended-ascii/en/cold/read/page-000.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Extended bytes: \u0080 \u0081 \u0082 \u0083 \u0084 \u0085 \u0086 \u0087 \u0088 \u0089 \u008a \u008b \u008c \u008d \u008e \u008f \u0090 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/extended-ascii/en/cold/read/page-000.png b/corpus/extended-ascii/en/cold/read/page-000.png new file mode 100644 index 0000000000000000000000000000000000000000..aa6b6e3797f63f2489c730ddf74e2ee8fc18f279 GIT binary patch literal 39119 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 uEC^}<9S4Bv1)xKj3+Q4bLv=0000 + diff --git a/corpus/extended-ascii/en/cold/read/payloads.json b/corpus/extended-ascii/en/cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/extended-ascii/en/cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/extended-ascii/en/cold/run.json b/corpus/extended-ascii/en/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..7d96357217a412779632df1272e8d93361ea1931 --- /dev/null +++ b/corpus/extended-ascii/en/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/ansi-lang/en/notebook", + "expected_pages": 1, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/extended-ascii/en/cold/scripts/cold.ps1 b/corpus/extended-ascii/en/cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/extended-ascii/en/cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/extended-ascii/en/cold/scripts/read.ps1 b/corpus/extended-ascii/en/cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/extended-ascii/en/cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/extended-ascii/en/cold/source.json b/corpus/extended-ascii/en/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..d3e5ffe17dfc9c590b42235a151cbd45cbf9aab2 --- /dev/null +++ b/corpus/extended-ascii/en/cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 4816, + "sha256": "b2f857961b76258a1582b402c048b340e89962257681426b3bfc58df8e0adbfa", + "mtime_ns": 1790487072902156985 + }, + { + "path": "synthetic.one", + "bytes": 12456, + "sha256": "aa6f1188773f1cc41cc5c619f213ce04b30592320c6b2e32b2b4c91639423ab4", + "mtime_ns": 1790487072902335569 + } +] diff --git a/corpus/extended-ascii/en/cold/teardown.json b/corpus/extended-ascii/en/cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/extended-ascii/en/cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/extended-ascii/ja/candidate/Open Notebook.onetoc2 b/corpus/extended-ascii/ja/candidate/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..a33d3bfd210f844e3db618e648ac898adeee09c6 GIT binary patch literal 4816 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypWOaMy5#LF~y)9Oxb$#>r}|Kyh?&t}bosmDf_Y+J~WjIpU16&nqK zp&kN9zI^|>u_|nd0MsKKISf-97*&4foBm+C_tSH2N_*D3xv@JKfzm7q4Gq}Dhk6i< zx_>kTMnni8*O}hCjGGRN9_ohWdr$_yJe4T`#AgCxK_CW+fpB`~EME{GBo3--Vd6H) z_ZEZrTu?F7Y>{LTAEsuiuGdl!TLvnx-CJk`WR8N-5Eu=CK@tM6`X7|PL4XHGB`+Yu zFm(x|3PjN`C9#!{ufV&YS{i30vEq40iLJuR~o}CUeKQ!^!fyzp3;DeQC z>=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jgw6K&`@AZW{ zcSu}5J`to<*Mp(=1S2D?1O}GPEh!8=$Q}Wi2mSTaM{uf|q0Lu_fMDRhC zvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf@C)Q}pzcvH z8UlkT1dxjxSh)a9#^BP130x+8lVMN+l?$Mx4+jk_3{zwoWOf3}jBFqcDmy@}PaB8! z;05iU+}4;?89zwhcNL@u)PgK#sAR}vC}GH8NClFa49N_74EaEjRG=Kl1waZ20OIF{ A&Hw-a literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ja/candidate/synthetic.one b/corpus/extended-ascii/ja/candidate/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..c15b622081fc5df7733206820860626a18c0521f GIT binary patch literal 12456 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 literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ja/cold/commands.jsonl b/corpus/extended-ascii/ja/cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..09a67abb1857d332e99ed1eadf88fdf023df14c0 --- /dev/null +++ b/corpus/extended-ascii/ja/cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-8DC7DCC7 -ExpectedPages 1 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/extended-ascii/ja/cold/machine.json b/corpus/extended-ascii/ja/cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..7a96f7cfbb857707e13a4e759dd5cb9664973b3f --- /dev/null +++ b/corpus/extended-ascii/ja/cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-8dc7dcc7", "hostname": "ONE-M6-8DC7DCC7"} diff --git a/corpus/extended-ascii/ja/cold/notebook/Open Notebook.onetoc2 b/corpus/extended-ascii/ja/cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..a33d3bfd210f844e3db618e648ac898adeee09c6 GIT binary patch literal 4816 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypWOaMy5#LF~y)9Oxb$#>r}|Kyh?&t}bosmDf_Y+J~WjIpU16&nqK zp&kN9zI^|>u_|nd0MsKKISf-97*&4foBm+C_tSH2N_*D3xv@JKfzm7q4Gq}Dhk6i< zx_>kTMnni8*O}hCjGGRN9_ohWdr$_yJe4T`#AgCxK_CW+fpB`~EME{GBo3--Vd6H) z_ZEZrTu?F7Y>{LTAEsuiuGdl!TLvnx-CJk`WR8N-5Eu=CK@tM6`X7|PL4XHGB`+Yu zFm(x|3PjN`C9#!{ufV&YS{i30vEq40iLJuR~o}CUeKQ!^!fyzp3;DeQC z>=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jgw6K&`@AZW{ zcSu}5J`to<*Mp(=1S2D?1O}GPEh!8=$Q}Wi2mSTaM{uf|q0Lu_fMDRhC zvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf@C)Q}pzcvH z8UlkT1dxjxSh)a9#^BP130x+8lVMN+l?$Mx4+jk_3{zwoWOf3}jBFqcDmy@}PaB8! z;05iU+}4;?89zwhcNL@u)PgK#sAR}vC}GH8NClFa49N_74EaEjRG=Kl1waZ20OIF{ A&Hw-a literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ja/cold/notebook/synthetic.one b/corpus/extended-ascii/ja/cold/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..c15b622081fc5df7733206820860626a18c0521f GIT binary patch literal 12456 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 literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ja/cold/read/environment.json b/corpus/extended-ascii/ja/cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..5d25d66b05678d093c31c8611399998b97e017a9 --- /dev/null +++ b/corpus/extended-ascii/ja/cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-8DC7DCC7", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/extended-ascii/ja/cold/read/hierarchy.xml b/corpus/extended-ascii/ja/cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..f007d2a89ff7d4305b16385dd20d179cbe89c671 --- /dev/null +++ b/corpus/extended-ascii/ja/cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/extended-ascii/ja/cold/read/page-000.navigation.json b/corpus/extended-ascii/ja/cold/read/page-000.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..f55849761016bf753db6dc26da07ab49553ce0a6 --- /dev/null +++ b/corpus/extended-ascii/ja/cold/read/page-000.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Extended bytes: \u0080 \u0081 \u0082 \u0083 \u0084 \u0085 \u0086 \u0087 \u0088 \u0089 \u008a \u008b \u008c \u008d \u008e \u008f \u0090 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/extended-ascii/ja/cold/read/page-000.png b/corpus/extended-ascii/ja/cold/read/page-000.png new file mode 100644 index 0000000000000000000000000000000000000000..32aeda3c9e3dea9b2d0a652414d228d486b0f4f2 GIT binary patch literal 39134 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℞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 S52keh0000 + diff --git a/corpus/extended-ascii/ja/cold/read/payloads.json b/corpus/extended-ascii/ja/cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/extended-ascii/ja/cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/extended-ascii/ja/cold/run.json b/corpus/extended-ascii/ja/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..5e2d4b5264e879413dc43a7e9f6a66cbfdf9dd19 --- /dev/null +++ b/corpus/extended-ascii/ja/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/ansi-lang/ja/notebook", + "expected_pages": 1, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/extended-ascii/ja/cold/scripts/cold.ps1 b/corpus/extended-ascii/ja/cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/extended-ascii/ja/cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/extended-ascii/ja/cold/scripts/read.ps1 b/corpus/extended-ascii/ja/cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/extended-ascii/ja/cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/extended-ascii/ja/cold/source.json b/corpus/extended-ascii/ja/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..741eec391e4355839f257cefd01dd18b8a7df53b --- /dev/null +++ b/corpus/extended-ascii/ja/cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 4816, + "sha256": "b2f857961b76258a1582b402c048b340e89962257681426b3bfc58df8e0adbfa", + "mtime_ns": 1790487072903313695 + }, + { + "path": "synthetic.one", + "bytes": 12456, + "sha256": "b792b045a793ef97ded2a9d6ab456070fb5d9d3f48763067ed5e24bde482358a", + "mtime_ns": 1790487072903438737 + } +] diff --git a/corpus/extended-ascii/ja/cold/teardown.json b/corpus/extended-ascii/ja/cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/extended-ascii/ja/cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/extended-ascii/ru/candidate/Open Notebook.onetoc2 b/corpus/extended-ascii/ru/candidate/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..a33d3bfd210f844e3db618e648ac898adeee09c6 GIT binary patch literal 4816 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypWOaMy5#LF~y)9Oxb$#>r}|Kyh?&t}bosmDf_Y+J~WjIpU16&nqK zp&kN9zI^|>u_|nd0MsKKISf-97*&4foBm+C_tSH2N_*D3xv@JKfzm7q4Gq}Dhk6i< zx_>kTMnni8*O}hCjGGRN9_ohWdr$_yJe4T`#AgCxK_CW+fpB`~EME{GBo3--Vd6H) z_ZEZrTu?F7Y>{LTAEsuiuGdl!TLvnx-CJk`WR8N-5Eu=CK@tM6`X7|PL4XHGB`+Yu zFm(x|3PjN`C9#!{ufV&YS{i30vEq40iLJuR~o}CUeKQ!^!fyzp3;DeQC z>=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jgw6K&`@AZW{ zcSu}5J`to<*Mp(=1S2D?1O}GPEh!8=$Q}Wi2mSTaM{uf|q0Lu_fMDRhC zvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf@C)Q}pzcvH z8UlkT1dxjxSh)a9#^BP130x+8lVMN+l?$Mx4+jk_3{zwoWOf3}jBFqcDmy@}PaB8! z;05iU+}4;?89zwhcNL@u)PgK#sAR}vC}GH8NClFa49N_74EaEjRG=Kl1waZ20OIF{ A&Hw-a literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ru/candidate/synthetic.one b/corpus/extended-ascii/ru/candidate/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..45c40a62a8fa360a8ffe1cf02ed36a71ddbf91c0 GIT binary patch literal 12456 zcmeHN2~bp57Ja=yvx5pM+UO0o6VXI*#AOr_Q3!5`T49C$SG9U&;x;*a+mosTZ&%N?0%L{*vi7TWdmPpF{DxLkdEyPmR2p_9jwZz zgZPelK&USF$5n_D^G)oV&@*Yr@{tiMhR!{hk+H|~W4t4_yL9X@xuVYLOg)C!$fg0C23{5om_4T1XZ9#=HGYrR z?o9c;$J-imE6*EsH3NPdJitDuV=dIduW8|dfBxdo)~M?kwHR>K;7_>Lxj*>M3{Qp& z1N;X&=6HBn%2-EZDYt9wn*=`QuWjL>e@u^kfvCGN>M$^WT?Xbwdvf4wE70z4skdL4 z8ihLMM!RQ5>E9s-+PxW04Dbz(@hj@_9fbP5PtP7k9cAE7(XkPzLmt$J9V|VEx-4(j z*FN@G2hUcYA+LWt-tK)p*P#!zLspF6->RTH-)v;lz)PnA+5gLNy313XyoX-oELO>Z z`Ahd0zl%Uh_EFXk>vQv0Z94gR*C|J@4v1`<`Jh1R69-aH-Vku+kwkMPV45T4uh z!m|mpfGNvM`6}xh{sdIS=TS*LzWg)>ZbP4{+GxUUQ@L$%-@#qVx-AWrW<*Up-?tzd zA}sDIvW}^=P*_W-Oa(@X${t$M343O2;n7@q3CMc;WqtEANKP!brc&pm7lvo)-K=Q@jqcI(%pHw<0 zo*4&8_524(X}@Lt!2V!ySPT!fK}kjFN*3eioA1t&-~6q4Z@#l=^Z;k?zs>bqy&
  • W&PA=PLwsmOz zC237nSX-enbU1M;{x92(sVOS2RZ-c#&Cj%fh{48ntMS46^&LZIWgpDc9`892UI>Gf z+Te-ME?ep3mEZdI?EF~OC^{|8>-yWeXG>@upM@hC1+nmQyudXHEGG7avagms@e}`x z{2B!RtMs_m{ao`^y z-eNH&4>5-0oO60Hc^ac9$E>{=5scmp6+_LK&sf07XDnnS`3U`e#!S6%NO*K)08iA( z3=zz;21jNs>ny}thG0G*1p$aeM~eh8QY3Q;=dd4xbs3EBC``f7E5b3gXvneO*Z>h= z;+PuwB3TO65~tyt1_@Ux@L#u{TEY)K?>`0vUgL**Zmiz+P7OUZ*$~@ znWc!k(C2CAC#`Ukm(T7wp;Dv7%Llvl4@X~|pAH%)#OqJuuJ#(0D>J&X2Fw9`YmFV$ z*0wSbGiYi+Th7&T!hXvRL2g)%P>n%JMT*f=WZ954untvqCTDL-ZPr;|)p5`d&JL`~ zAf8vrszA=@oys;*X`jG@4NYj;bpFL9t=xk2sW(1e8ay8Sr_FEH1lbw(&-!lbXW#U8 zZ6BI5YHMD@L<~~Y#IDYlCPHd!%f@|eWYfS)q5%hgvmp6=ll?4?b0xS?J^6KWUL{P# ziQ&S?S74+_VsAkrQN)X(Tue^;ud z7cLq`eU78_3{Qp^LzWp|i{Rau5IZpi^PRynMRuN>=WTi-$i;!D+{+Y^OEtJulc6(>c_Tp3W4-Xm4Pf~i2tyFdlWvj8QH%T|EN-|l z&KNf`X*4Cq4~vjx#hNHzuo75q>iGgf8wVV&zG3*&GJjRLb zy))a@=KOn53x+>K$8E0M=E!#k2Bt|g>y*<1A-Zs^6VK7kFfod+=3m>*&6aBg-zBv` z{*62w)GGPg!w*^bGdP7wDfr7G5OPt{yY&TGSGf2oPLb8!FjL9O9Rw}dn~zNx=@ z;thRDa>V3zp)SVZb%($k>`gaWK)59p{#2o^Y75bUEhl_XFLY2kO~w#(qy_`h{YURtIV-sRxV`x~aF?Mu& zlISBEPZKDeCQ=4XqRI3zO`%MhN?A0Grqc|{rkV5)nngJ@o90k1<1Z^t)l`er1i9cifAKkqRq60KBHpVO55mj+Df(kc3azNFLi6@5)-=o|W$&eAzLPyeFt z=mK4&f72x@rSIuKbeXQuRk}vk=?2}TTXdU#pfb8cKTAJD*X-qCk84SBBUN?JtiF}?%pMP#i0+yCpiz$*_Z!!Vc<6O`L(|h68ikw-w0`m zQA{QzV@#3U>527c#uoFRIh6KUw)4ni@NK?9iI4`~eao)rnlU{#&Wl^;wIi}N)>QiT s2R?u2n;~W-wlEb%F5i@%Dm_t5wqD)bTZq&x_w)3X|1`1gA^V~K0oj-z6951J literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ru/cold/commands.jsonl b/corpus/extended-ascii/ru/cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..fd5f5a24943070a800781a23db1508f1cceaf637 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-2B8202A3 -ExpectedPages 1 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/extended-ascii/ru/cold/machine.json b/corpus/extended-ascii/ru/cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..c0c1f5fced309e8a81ce576ebbe21e6841635728 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-2b8202a3", "hostname": "ONE-M6-2B8202A3"} diff --git a/corpus/extended-ascii/ru/cold/notebook/Open Notebook.onetoc2 b/corpus/extended-ascii/ru/cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..a33d3bfd210f844e3db618e648ac898adeee09c6 GIT binary patch literal 4816 zcmZ3e|KIuM`!b(-j|5(YE0x7J#4gqT(JSk=YNPqbC`J7Q1~g!Qcb0(k#IK%9FNjZk zdt%)UX`nnZhAICK1RypWOaMy5#LF~y)9Oxb$#>r}|Kyh?&t}bosmDf_Y+J~WjIpU16&nqK zp&kN9zI^|>u_|nd0MsKKISf-97*&4foBm+C_tSH2N_*D3xv@JKfzm7q4Gq}Dhk6i< zx_>kTMnni8*O}hCjGGRN9_ohWdr$_yJe4T`#AgCxK_CW+fpB`~EME{GBo3--Vd6H) z_ZEZrTu?F7Y>{LTAEsuiuGdl!TLvnx-CJk`WR8N-5Eu=CK@tM6`X7|PL4XHGB`+Yu zFm(x|3PjN`C9#!{ufV&YS{i30vEq40iLJuR~o}CUeKQ!^!fyzp3;DeQC z>=&E6j1YN6k%k5a6=2YVlqMGh%ncLkbZefpL}|54*a8iKhK3%XT&?Jgw6K&`@AZW{ zcSu}5J`to<*Mp(=1S2D?1O}GPEh!8=$Q}Wi2mSTaM{uf|q0Lu_fMDRhC zvM?~f>JOku15~tukzt<;xMA@Dj5mMuU0$Qf@C)Q}pzcvH z8UlkT1dxjxSh)a9#^BP130x+8lVMN+l?$Mx4+jk_3{zwoWOf3}jBFqcDmy@}PaB8! z;05iU+}4;?89zwhcNL@u)PgK#sAR}vC}GH8NClFa49N_74EaEjRG=Kl1waZ20OIF{ A&Hw-a literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ru/cold/notebook/synthetic.one b/corpus/extended-ascii/ru/cold/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..45c40a62a8fa360a8ffe1cf02ed36a71ddbf91c0 GIT binary patch literal 12456 zcmeHN2~bp57Ja=yvx5pM+UO0o6VXI*#AOr_Q3!5`T49C$SG9U&;x;*a+mosTZ&%N?0%L{*vi7TWdmPpF{DxLkdEyPmR2p_9jwZz zgZPelK&USF$5n_D^G)oV&@*Yr@{tiMhR!{hk+H|~W4t4_yL9X@xuVYLOg)C!$fg0C23{5om_4T1XZ9#=HGYrR z?o9c;$J-imE6*EsH3NPdJitDuV=dIduW8|dfBxdo)~M?kwHR>K;7_>Lxj*>M3{Qp& z1N;X&=6HBn%2-EZDYt9wn*=`QuWjL>e@u^kfvCGN>M$^WT?Xbwdvf4wE70z4skdL4 z8ihLMM!RQ5>E9s-+PxW04Dbz(@hj@_9fbP5PtP7k9cAE7(XkPzLmt$J9V|VEx-4(j z*FN@G2hUcYA+LWt-tK)p*P#!zLspF6->RTH-)v;lz)PnA+5gLNy313XyoX-oELO>Z z`Ahd0zl%Uh_EFXk>vQv0Z94gR*C|J@4v1`<`Jh1R69-aH-Vku+kwkMPV45T4uh z!m|mpfGNvM`6}xh{sdIS=TS*LzWg)>ZbP4{+GxUUQ@L$%-@#qVx-AWrW<*Up-?tzd zA}sDIvW}^=P*_W-Oa(@X${t$M343O2;n7@q3CMc;WqtEANKP!brc&pm7lvo)-K=Q@jqcI(%pHw<0 zo*4&8_524(X}@Lt!2V!ySPT!fK}kjFN*3eioA1t&-~6q4Z@#l=^Z;k?zs>bqy&
  • W&PA=PLwsmOz zC237nSX-enbU1M;{x92(sVOS2RZ-c#&Cj%fh{48ntMS46^&LZIWgpDc9`892UI>Gf z+Te-ME?ep3mEZdI?EF~OC^{|8>-yWeXG>@upM@hC1+nmQyudXHEGG7avagms@e}`x z{2B!RtMs_m{ao`^y z-eNH&4>5-0oO60Hc^ac9$E>{=5scmp6+_LK&sf07XDnnS`3U`e#!S6%NO*K)08iA( z3=zz;21jNs>ny}thG0G*1p$aeM~eh8QY3Q;=dd4xbs3EBC``f7E5b3gXvneO*Z>h= z;+PuwB3TO65~tyt1_@Ux@L#u{TEY)K?>`0vUgL**Zmiz+P7OUZ*$~@ znWc!k(C2CAC#`Ukm(T7wp;Dv7%Llvl4@X~|pAH%)#OqJuuJ#(0D>J&X2Fw9`YmFV$ z*0wSbGiYi+Th7&T!hXvRL2g)%P>n%JMT*f=WZ954untvqCTDL-ZPr;|)p5`d&JL`~ zAf8vrszA=@oys;*X`jG@4NYj;bpFL9t=xk2sW(1e8ay8Sr_FEH1lbw(&-!lbXW#U8 zZ6BI5YHMD@L<~~Y#IDYlCPHd!%f@|eWYfS)q5%hgvmp6=ll?4?b0xS?J^6KWUL{P# ziQ&S?S74+_VsAkrQN)X(Tue^;ud z7cLq`eU78_3{Qp^LzWp|i{Rau5IZpi^PRynMRuN>=WTi-$i;!D+{+Y^OEtJulc6(>c_Tp3W4-Xm4Pf~i2tyFdlWvj8QH%T|EN-|l z&KNf`X*4Cq4~vjx#hNHzuo75q>iGgf8wVV&zG3*&GJjRLb zy))a@=KOn53x+>K$8E0M=E!#k2Bt|g>y*<1A-Zs^6VK7kFfod+=3m>*&6aBg-zBv` z{*62w)GGPg!w*^bGdP7wDfr7G5OPt{yY&TGSGf2oPLb8!FjL9O9Rw}dn~zNx=@ z;thRDa>V3zp)SVZb%($k>`gaWK)59p{#2o^Y75bUEhl_XFLY2kO~w#(qy_`h{YURtIV-sRxV`x~aF?Mu& zlISBEPZKDeCQ=4XqRI3zO`%MhN?A0Grqc|{rkV5)nngJ@o90k1<1Z^t)l`er1i9cifAKkqRq60KBHpVO55mj+Df(kc3azNFLi6@5)-=o|W$&eAzLPyeFt z=mK4&f72x@rSIuKbeXQuRk}vk=?2}TTXdU#pfb8cKTAJD*X-qCk84SBBUN?JtiF}?%pMP#i0+yCpiz$*_Z!!Vc<6O`L(|h68ikw-w0`m zQA{QzV@#3U>527c#uoFRIh6KUw)4ni@NK?9iI4`~eao)rnlU{#&Wl^;wIi}N)>QiT s2R?u2n;~W-wlEb%F5i@%Dm_t5wqD)bTZq&x_w)3X|1`1gA^V~K0oj-z6951J literal 0 HcmV?d00001 diff --git a/corpus/extended-ascii/ru/cold/read/environment.json b/corpus/extended-ascii/ru/cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..7bebbff19e046fca4248fbe1866abe0aded53b6e --- /dev/null +++ b/corpus/extended-ascii/ru/cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-2B8202A3", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/extended-ascii/ru/cold/read/hierarchy.xml b/corpus/extended-ascii/ru/cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..4bacc530ba83fcb77bd446d98f5f7235a277ca21 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/extended-ascii/ru/cold/read/page-000.navigation.json b/corpus/extended-ascii/ru/cold/read/page-000.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..f55849761016bf753db6dc26da07ab49553ce0a6 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/read/page-000.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Extended bytes: \u0080 \u0081 \u0082 \u0083 \u0084 \u0085 \u0086 \u0087 \u0088 \u0089 \u008a \u008b \u008c \u008d \u008e \u008f \u0090 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/extended-ascii/ru/cold/read/page-000.png b/corpus/extended-ascii/ru/cold/read/page-000.png new file mode 100644 index 0000000000000000000000000000000000000000..aa6b6e3797f63f2489c730ddf74e2ee8fc18f279 GIT binary patch literal 39119 zcmZ7dby!s2_XZ53g2d1%Ev+zsv`8tE(mj9!14x(TNX|zDX%LX11Oy2shZt&LkP>9* zZlt?oXx`E9?|rW4dj6U-=Q?NaweMB;T6>>JU2Rn|5+)KnJUlXWH6?vKyn7Gv@a}?$ z?&9vy(bDnZ;ibaWm0lRW`?B3c{ElTbXRTPqeHGI9 zi%?4Hwuhkq-P3-KIILY`_dj~RNikkp?>t$QqhU;L+PDZ;#<*KlZ)ejBUCsmKuPl#J zuHw(5Z!QS|x6_Sou3sApn@xl~k5WpF)7!M{SidCQxVb6%p>_v?$!?Xmia#dtCt zBjq-;?rC>(D`is7IW*$A>$2tN;apO|EhB^d^ZtXavTdwH?3#IBbOgb{+Co1$16|6q zQO{S(`7%;`!oqyzb%?yilFEWI5lt%&{W8uJ*U$&~+q{1iB2u$$`h+Q6i$c`qmp{ebRW5GO4Soq=kmR}fOpt#F6W9>%nw|%rp zgIN=mvx_-`sz>0x*=lGFW$@Zxxbp1%_#7{ibgnaatZg3%M8HBJNS0tP*ok}!qPTCU zELq_l>>Qh9a^>KZv9*2PwzG75UMjWcw-tLizfm{hx6x(9@!;qN^IA{2zkaO7aoE1P z2FdIk^TvK<-ItCx_qm%}umhM2HSB9**wE3M(58yro~m+QD?4!>k1hc>`KoS`FMv3; zeYgN{qj(oH#Jl8DGifBH-Tz5Vc-RmP@E4>JEFeyw=akrw0ItQq^%5G;_=u`vrtg6;CS<2yE-CN zy1g-I_B+ry*O5C1U18h1pN})%0lWKNDEUTM>mrP?_D@!Y*Phnl7msYYsf)P?;{N%A zS8FGIAHlt?;;T1nletk@Ri&-~dLbuQXQ{FE-`(Ahg&s+wKJRlR=x$MWczPWPj>c|J zBJFRpuEXW!sxw>N*NMW2hOn!SsQWZ`tf#MO$X%Ql(i?uIy13bOQ#WbJ!EHU< z#bb$DoNmr)cv`{|SNa2g_ADwon2ulPVe-LI?!D`>*$@Bier4DlNJk zv4#5^d`;Prq1*E4c=5oMFNW>9xV=8@eX^p1N_^yapNImWreAH9r8id7S-W3@MZ``6 zDwXg|Sj5BPW3NgZG?KDj-(Vb4!&0@o4h0_zcREFj8PJEj4LX}de{y1lL4GdnJe_$cF=@CE$KSVr)Dr9q*ShEnHQ5dH z>8bsNemm|AJ6jYOFMr){uJ@gI3`QL?nzyzs?MlD>+Bu^_CH^fhv>w$^Y1U2&)Hn`M z7Ac2FOxhj&>g8Q9$4Bp)KI+8TYho!)SZ! zBQ0PTv^bMweLgaybZt>V9Ar+WwfKCil({CL;1JEWNk(3M##RT*euqsxRf}b{20pmU zTAh9Ee78ShM;;1XItD&CIO%NEA79~JOVM2~?x++zQXByvBFFZ07!#x{0QK9n9`1Wm zIoT6swRY+BjoU`W82@(9)4kfJtMDAy^+_r0d^W|h-uK*pHGDF6qUZK0=6hh-=p08JyR@FMVS&Ks z1rQ+xi}=_Bs*a)f>=@5ACX96b`Hu_(Y+xmy=J{?sWU_jom#XCJW{pgzsKSHd@jH?3 z+vMR>9(}NOQHFsy3*PDvxwBTlbq6Oi#~;q-JvlbKnTPEE1^Kl3_M_|`vgqEU(1M+j z>*T^xK28*#ng}# zh|l0i`axlWhx%|&(Le@hyStJl--c&oOe)Abi5E0J?Ed=dYO{I%rdviq6FuZF;q17( zHUG;0bgWr6=qS4RCYQ3rnK4=vm%k~hl<4FH*UZ9RevuouD;Z2mN=iw|G%+`FZhs3r zdX*r?Xs=|YaS}0EFXsxK+KovnZ2L?s3@YW|0@VmUn5Xw&@q8fjA4KmRpBY~egaoX_#xn@_y^XcFA-IxM7=iFXhr7MGW;RwUcJ}5cBG`DxPgH43KiXu3~FSqs5gD z5xi_wbj25F+FD!?Xs$mIE)SO4aGD-%9px#$sX1qmYCc=m66T(}sogRuD-?bz7}a}5 zm;}jm&1{A&)Q=s(Rlr7UL5R=|!cB*T<;{NeJC*Vw z5=-oMQT+bt{aG2!Z}l+s#x}7D+g}=S#<3}PV#P@kVV&dCmWQbG#+gVjvlO70#8NaD zV%qn$(zRtZJ-76f*p;KFkIHwnR8@;R`kbYNN~d34OP#j1umij?=RQ<8F6G9wlK~p+ zw(kj=5+7YG%}PzQ5?`^L6P=p>GxPJ6GdSG(eH;&3eoCtL=mP!|oS|}A%nWnSTOA~O;&N!v2K_+d~e zhMJze3ducAD&#MgrPAnr2(t1e=Q!vo7<_?N#NWmSH^!4_k5TxgYZ&E?YIXt?5 z*X!{0Y3*U%C>6UPQIsVgm2ZP?T5l!YTVR<|x3_7REwD5(>}|T(nnHipb$peEB_yX~_@KUs9;Q*cd-!1j;Wu<)o zu0=F!35vT7oZG>yLaT|_H&2QGKD$V-g`jUluGU(zTp#y-<6JICIk_78T3<89!3Hn|rN^|!R(?&g-2**@vjDKS53%g3ObAsg6L*jW>WV%0Lr zDfrhmk9QZR!ww46&Eet`Adwj$R$fhl(bx~SguB{O>RJL(lxs9-$HXWTZg6M z&9Hx>D4NA!H?{imbx>o6)gZ55jqZITo_nNwhcWdkXe*#jHW$2E1RP&;)w`kc-fv!< zzi)(o_e5$P?1<8n`~M6pnp(B??mREqv|mTzyepNJvtQ@dd#HOUAdX?0ehhzWDFH)2 zBLh8^6LU%O#8!Vs6&B8j^`f3=0ec#zv&*$I{A#5vhefw`u_p>hf?IGE+KwMqJJ{u2 zswFs;t(`$n#Y!{#@}H`PETzG^JR_S4BH`H?i6^gLjd=l@)PMVPKHf@5fohuhUU{Q2 z`pPsVbn~Xq_Od3$!gT<>F{pyRx~(9=w)t^*xK3v9_{hdZY0VScbgb_M*xIVQdLgMW zQlhO10Cf~Vo|r8>&g~|3PyTcNL=!n%&4yE_9Dej)Wn5ERnb@SfAwe=Bb=!Fo6N_v= zk5@FN`|OXNtBQuOYS5I#@0yEV3RD%$N=u0E&5N90%M>)i3bChgj}7){(||W${5zL_ zK(*67*OnhIo<{$kDy{hg9$Pa1Ca5_o>Q___@1ku&VuJv=2fu5~8UA0(7EY@c#W(ZJxeGMb0fjME!+%i(PEmN0E*GuKqoja$$Iv6|Oj=h;K zjgE+m+~xS!j);VvvIxW8^jOVLjCy8+S%FSk>OROCC(Prm#yAO{t(z2R9xK_G_q9}BdDO{lp1I_z4}pW``9sI9k5^?^ zWG|C`QA~asAAPs4s=h}S1YP_@OyhpSb3PVHqikT-=(-8WUCH3x%sArx&ivP!<#0K> zJXY;X^6qir>&deVqS$unPMZOnblQrtUeI##iR3;4~h&ayVTM*70+o75?6 z4VmJmo8963GJp{?B&SF;D7N*XLx)?QkCR^U*8A1^elkKJXoMdJtW<=AvVhoO@7EV4 zkMJ>TFK^xv2yDQi^Nl`1z9wyG!cMfro0<7=f3FPph2H706>o{{+rhfL9CezHjfK)< zl+6&oV!ruAmbXBBfz#QoCDnBKHAm;mpl75R7yxKO2@%|u5-?e1rL zFbgOk%alCGP83wX38~9`asZ$Ss(9wNx7sT*MAyk#sh-wMZnpxG~9yKFIrN)n*_{P*KF(3BzX^FQmU}VtR}hb_?wK&5@F2nUxiseNXFr_@&w7oShXZQU%zt)_3E&n zPH29oRzM3;xeE&p*ArPVQAD6?-d+qi{%-ZWaZHp>LO*olF`e-dASV*|e#NNF8P z%BVYAP9G~_#+n3@lyRs8k9xIK{t1wxXzaL*GeWmcz3OFaR25B9IaBx?{o(a|>~L!Q zb?_Qdj6I0&wPM5a9!%FDh;H^&^B!GIkmS6xY}9|J9nkVk5pkaHeDGkW_`X|E7JaJ| zvP^zEug39Kyu$YOLDM0YTNm@=P2+_-6O!azqS7o^=qIP!FJBjoRtHO-Ag@B`#C`L# zgKU{lLtanR5Lxvnqdb=8Pi@wZ)o;Ze4;hX%vpkc8lbS!tuB@v2^Umkmc9$jWkjfZ1l-> zuqu)=a2pf*U7Pb$g>Ora3QUa)%`}aSfe!|6{AbLkcTEdw6;W@@ zi*=TEUOv~^sbSkSEPc9XBxlzmT3UNNyf?;dsq+Qt*EfvTE0ae)SFp-j^w!Y%Vwrug z3;S8DdGtZlC!SKVvEr$*5~^sdfJos4e8$h?;JFVmc1D05*B}I1RPq5^#Tt7SpFO9K ze@JNsTlV)F6!?iKkg|K)4!_NQ_GY5|)^po4?tC$qzgtWqSIFk(T*TCrT0ppIr1@n;Vf za!#%-PI#a2GrGJV3*PL!6f8fFyZ$D1v0P9t0|fX=l{0vLa;COgzA^k_aAbd`Z*NX2PY)%2M0qop@Ou-dJ9_h1)G zDRRRM(^e2o)=SSk(Pa84U;T7%)W6fzFeBJW$hpTNa%J3teKrLw;lum&Kgml~W_FTe?wKty^mVP9 zUh6KEVg%J}MQ%m0(~c-vM|@*ro3+UWG3eJsGNPDoZe*2kSY8TTupsr)($Z&dC=A-0 z7I1m&2kl9PuEkL7emQSpukUqnLc^_240uxGhUr1 z?AE>GBzu;GwIheY=1lB+>YSa1QY51!2-#Q_PBmJqnG}M&uVgGWnJ(AuW6_MQxz(tG zp#9`D08&N2dK#rq?<830ka6Z21@)?GFHj~_l2Wo)O8FZ@gJrzE<9)4bbv&Ugb%$B0 zEBWQ<1=|MA55suo`?QHjjTaQg?xn&yp0%2eVS7gomn|{q`QkpBgqlJ~Jb7gqRl!lz z#ars(2J^dxR$;sBNq1G=1^q(as<5-%%)VJ?;|M(#PlB^Dkl3=JJV$GzQg^yV19=i3 z&e+aItYFZmEx9=9$aG9)15XpndrS{UrP*(eX7X=kv2Cy{F1M69Irqs~uasFa_enW+ z;AM&*=Ein61Z7YVXxrVs@O#nsMougxFzB=qWv_X&xlC-So`u6Lx6XOUgtzx>%FrX@ zDtY;_Wv1v!v4P#q(IsFn?PzdZ8E_=upo-BSTpeE(yq@}!zi8~TTRipuLS}5X`3hQl zqWM22?w>q-US_h!KFES`v(v3DHnC^_-37S$!LlHf(R_Q*Isf{2aF!j|Y=3hIOKV=- z0vHDeEp*O<9vKH;^@bJif~z;pIipRY0|S0dyV0G0OY1SamypOB&$3773r$B#bb21KwP6xiLDQglYeB4# zg-`iJx3#MmZYoK^XNW~h`iQ;=p~&RifM%4dX7ipUjvt1S_Ptp~{SuT3zQxF>p#5}{ z_^%FSkPui`wStv$;ctM*DV ztT0$%@f+E^Df$4#?s_rjnb12wUg@}es72vW%gheS5sbfb_*MgSL%KYu?@8Gc9v*i& zhEIxncC04kHUt+-)E%HOQh{q+6*;li0$5=hX1WY|R?7d!Hv`-rgy71$&`&p-Q-jty zrv1D@{CTgGfJ8{qY^ZGQ7JP0&exP!c+u8?bV<~}u zaeag#ZBUcGu0DY*)GcDnqIY#5{B>-q7`~c_vc&WfZwX;W=LCH*= z@jp#I-Jg&gWE|ufta#Zx5euQh0i=}UFwZG_(DR@#xUu)^%>evE|+{Q=m$9qotCInv3>7J^kqt#{P2xT&Zv%sQ6D&pF^R1 z{ZXRozs0W77!w)$uv2B&(wN|_f5mW79&?B-H>j}nyU_#Um!*FzQc+0Kz`&5o-%Ca7 z2mdRxxPR$sB3?_7$zX%f!P9eeThQ-`NORw4H|%BE-%V4sncPH3e)eM!%BMdeCM2=@ z;~0Q-CuJl8wL4S5O8EA#$-LQDVr=2}8ZXnC-70c*MokP3`8F-gAgAmkhTfj0qfTd3 zSa3C6o=&rlKjo-RBm6T}i7SeN9RqZ18qgc|>>c(tDjh+-_8HCUsK5by7;*0n!*d-y zeZ-jCBzQr3`t7cn&yo?H!E}rbE;DVvK-?G31cO=UyiXg+cnCDSFJ{8x(hG`r?;CMF zvo!VcUGn`qA2(Tu#Dk6PhPS28<}2r>=cAn2V_)_f=3e&d8zH8Ied;#LAF=8z(fyMR z_w4%mHovPQ%&z%z*T+=FVq&k%*?sT5&0`=a`NYIf2U%sa!H_dCFdVI0vgA)0N;!lA z5eRrqS_T0zF}(+`t5X|`g*iiO6^B4JAA8bXE zbpA?KO^wD{KUBsj>z9_y=@PD999qIBhM%8V);gQGkP46xP)%!D)Bq=60~;Ycd>P6- zt-StT?n(^?;DFSdN{f0?6ddX!51w5VQD1mzo1FqiQy;BBqp4~03j1upOUrZ+tQZctM$@#Xf zSGdQp9jVM%94jJT$)#LAwD@r$?k(NTFBelR^anHzW!F~7oFDV8_^}aaV`CoARrG&2 z=}wNvBXPSJI3t%RoFtgt^9#vQv}$Ilm^6`b<~jLruH#g1^U0q% zGxoTb*pi*6rQ%ovOXkXEAVn4CcR3I&v1f$QraTcG-NDEBl4^MUxJpnbG+`|rL@PLy z%#@Xq(v?v8rUKesqI4%l0pI4{Ep((T9_2EG80iPqQ{+w#SyF@wshZ}m{U|w+9-9)( zHf-23Gn-;&G7DhLG2Pw#s5R|@^Q#SCMQu;)`HP6>4$&>0TMuiWpkAs~G^dk%j8Oi0 zuSbQ_6v0A&#Z2VgKBKtaCgLOfJ<3%^J2oTcx9?1sXL#E&Sj}XIbjU=(5OKrA8R`q( z*nVbUCUw~RpO_OOu!X#gc=j0?l;Fn?!lL+r;Y1j z5IPe}@{FG5xwNlG`*&T>7YJ@p^s;Gh*CwhrM=1az{wL~9Jv-%g^v=##S8seOEFdit z)AtA(M=8$6xVdGer)#?350HoM6$#T>bef@j+^gQDl9isEtmXDkFU?hfg{N9@KA%Te zaZ8`$@$r*m53j=j-};OT22Ve=g)ZYg{Z^!UfnJ4Li0z|LnAZN-b+8pK&9FTZjn6n| z_feDSi{;D696}Mx1d-4K0{~uACaZ*J;{$x{R^)?yOV1i!&H8Yu=5P8zjnEP3#?9f} zpQ*-Aw0%eEUbYO4@{{|H{$VJ|Ez(%v1a1x*)xZj5`D0XpB||f6mXCL}-ihYFvOHgD z-X3@M1I(PCY8T!GWHnwV4|5nDuMETL#aaT~C$vB2HLg~^Eny9aH#CwO8_SAK^;qIt z2e>y%Xj?uw>3*Pr(`svlX@T>n4Ll&L zhP7r?K!~l}3K}D<*s|5Lq(1oN1D##7vXt_JXCbx|qs@i#V!?%V5|vI+y|Z%4sVO6q z5_}Dl;%uH@ZDmYXAx3{1XoW84&~h9Lr+S6g&cDMF3Kh7QO+P+zh(o$!t)NCPwX`(aWBis}J!CeKB8#W_&1h@$g0kI`;v%+A z&d^WmG=YhiLRC%=#2j9PyDjOuDG=JrD6a!SV{{+=lpceAeT~}zW_64P^OY7b>XM69NuFGQvD-?%L*k|czpJTQ zKdX-XyKvs;b0}lERLTYy)R6wemT`z}s&%1dW1D_Sz~fUHQ9vVuRdS%k{Jd!aI{spAXthhlLFSZ(z z`xInzjb0_&nGPwIbcrqn`-Bl07gG-s2%xU4p<+9o#1wryzNNvM377h&$W>6jtweG% zSNe#Njy&B(|gALX`m+LWAi5e(xe=1`h?2>K9_T9h1Tf(R_< z>A>I8ob1?LNvaK>Jvc4c9oyWaeaGs&#TTyeN{){11kNpg#2mB`nSHHj`EI#0K8(Jq zeaDS94r#?4ha=x{FA!g)9sxmV`3$+*zmA~X+}wfZ=iot;gh%IP(djlj&tAV?d@NjE z+ZzwNGAKm3EO|;8*ZpNS9;6`zO&lSRQHjhg?1*F*Qi-8tn1|uM&LO`r+$qlI`}YY0 zV`GM-2XRIgVCIG-VvaYT?9Ur^GV$b;8-q!c)QOgif)1v9oN5YkIwA_u(&7z}On&r} z?};WJ71gi2N?LO_hlZw7^C~M}Kuh#%JAF-P6uZ9mR!aN`J?8z`!yP)6TzcFt>wkFx zXlN)?*KoBla{a&t)oS9w4Fq&zclrIe`5}w5##GK#NYUbAdTL%{Bf8ZHCGVr7r);!d z#`~ASQCK^~=%(XWflV8W_T)K7>68IHnH&fTxG2G&E8EhglN zlEA?^?OPzf^W9ud9Zh{>V?Gw=*)ci4j+GjjFMnI}v#|pYIK&hksTFZ$Z_%6f{Yp2U zIs!rY@*-F7aq&7?pIn+;sr=+o9y+5A+p^rD?$*Spkj2BRMNuw4<@^Q+tFQz6m*4z= zr^Z$}e7pzJWANER&%+KjoU}vDBJ5`oo>ZHh_$m%DpBQt*iS1C-)We0QXg5^E^tV`( z`l2wiQ6bZM$H7Cgqmepd0%^yEX@QHP4ta)jlqU`2KuJvt7YZi-QJFh8;?o`Y$B^lHZ#nb z&8O1)k=d@OfvMn%((A;ha1+B(!v^PLJ%Nf2u6kF0`F=+_=`TTj8CCw|w#aoVf(x+6 zg(<>wI;QCbj!1v~_4Mv0q0;pq2Nr=ye)T8Y{B9vDa+soq>f9nkwLSwd+H9j7@xeY_ z0poJG;?4D!)c;&k*o@@*s$Mey<{tGZM0MrY zI-pFmn#-V*i3M^fZ}ZJ8`gp2|&*_u}8?h~Kp@l0ZNJrKQWjsb`I^{j^N3=9pXgq+* zNf1)Y4lP6|c!eQljUN8x_irhDjoRX5XGv<@yh=*rz$6fVdcQX`|n1Tg%ZnxO=XRK0YnZlx1=A3D-@&?I4?EJDTtIn zkXg--@CC}h18cu7Yq~A`?g4@kp{X$VmeM82`V zL`d@SM8w{$N3jrS>sH*ey=zTMnFCZGmOpu}Tv&B9tdo5}0{1;l9`NGWAfm^^d!Ax~ z7$P^>mN!OXm15;1^0OnuAU_i4w5j44;!X-jpzO>dixt1A3W>rXg62t^ne0H?5TC0z z1wtUgesO>tLt-4{4j!JjAA$2_%TRT{0F=FajIiR^R1sez86Us6s3ymL4Z`E-EAcqp z3*p_;{rJ%!zJNeKB`_%Xt);(U2mv16zzSW7Yc~C!>}&KTDEX=UW>qK=Tyf()MC;-= z5g%LpJ90P&sIYIDO+!HtUqj)}*3TaoI7KZ2LA|}b9UUFg($c1Qcn{eZuj{{m|6Wrg znH7FtoszJ2M}`u) zPB{;nlDN3IsP4+K(*}SE6Kq|}aNxrS}T7PzY{~bl@_B$RJbjVJ2^7gYu>P}UU z&$4S4Gd?R2=b)lGAG-WJHt&44o>#M(NLdNzEql7_U7B`kY5XnkrJI;K5@Tg$)9EmO zoQ+iSH0OQKZJ25NT3C|%PFEWK^r72a2JVRH&e5wKR4C&~na@jc)TR?%ZP^Hi%I}pe z^117MucvUAjeJO20YLS}&t?zb8GEpKMr0fm6l9>Gg{e1F{2YD5w0kYe;mKS3{cZok zaqAFu#g~L6)UBF|qitHS$MalUPyM90kVD5g`nM}`s z3-1|4Xn~d|@s?Nmt%zFrUH71k`9^8q)(jioVD-6rVe*C!5(FH&39^&E?t2#RBIII{p<*V}KpI!OYYrbXM2@N;^rv}cQ=c4nco);Q zNk@K%?MnVC0oUSezyM(Eg|l=yxPEhoKww*OI)PEg;X-1GIGZg}Av{Q(p%RC1;(Sku zzO9t9gWF7@vzbVt;AyQ%m`91*D4p*~^gQ}Fx_$m64AOCE9*T3Cn3yEoVfg0~?IeI)z&!QG|g`C+(pRbx2$XvY#6Sb9m7L>+SBlvK9yUt{4@EXM0x{&jH|xDUOuv z9~KdGcz6+lxFHdMu|q?zv@zrPeIw%pj42{UB=09M%Qj0pgHl|JTz((2u9TGt>3!EK zW%3=#9vn5$pghVovbn$qV|y`mf5DcC5{UZ_;02;5WQ7?$v|V%pF$T4e3}yb2mI-PV zf~d7%76>phk_95$k%S-9?6})9mxY#St=Mg!SQOl8xr?Vug)i_B^lSMmWeIS+g@(Q* z@gaW?=_#(oz|=>=h)Y$zK|;>etJMpv4HkrgLMjsqWhQW-@I96k@FzIiTmH97YIsV z7%oYXpfz7gX?v<7av^O7#BE83{uc7Wj_+Cxn62l9aXIa7iz0&dl{o1{Pw;IoNFU4`v*!)8HDo2NZl(p zBV~7EeNV#lU9>n{B%ERUF?sQa@oD6e;ar(;0qUN+!z#7jiR@QLJiOT!Q$)kb9amjq z`mXrGoNG;wrF5Mr1#N-IQphy z@Vj&i_cv0r=fcXqW%yu>M=7W6{*8#9lr(N_q)byFF6ZwkPeBBj%f3&vp4v%GRZpcJ zj1s}YGIA^}hx0UnFmE}j+F!c#cuLVr>btAhL^2%@a{BL>)qA!X^x;L(OA2cPaFZe{Wax^UjBHeLAe3Kw=}mE$crZbzGIRnkLau(4mr3hr`H{I)YX%gUG|(#n>m0^&vBjkko{=}&fMJ(99J5>%us zTb?U?(m{yl!M*tX-RFm(mTd3K#UR;N(`15UvoxFcA9{`3cAT-4ybtEV-XRJmz~c{r zC@+XK(PpCQOWto=1@VEB*qKuqm{{NaF~R%FjWbssu?nh^yv}W*W%(yMo`iUKkDntH z@jJvXi)O`UE|^*T;9?%Jd(DKFZ~5LbxeXaet}UkbOOrpg+nu<7w9%Nu$D+Dzu|Hq* z8#Esa!9j|Lcg#TqpRJ8OF0Nx5_mnV?bex3Jkq)0Q-%Da_VR%IvSo*ubO|9ZjoM~CS zYs1eHn1~A&$dJd{9@3${i}#hSzqn=c|g+5lyD3)__I~g%(dnx6FiY$B16ZA{>#{y7j_-iiYL>oUu zD1)|TSoXM`4^{Omvye!u^x2nXH+fTmJkVF0boDSa4`@!a%}03uk7D1r z{`-kIh@(RC5=}{DeEfr-T%ZDm7qxTRg6xN1ECm!oTdyf+hZF9d+R0aR^V54H*1-0&CxpSLpv7Ns|_Qk!Q1=zlQcEE)%eyhwk z>NBF@Ok%hNk@18oOL4I#bdrTT;C9S9fH(An%egipkUX?G^&B->(^2x_sTJ1iUCSZt zimVou$B#RIj0R&3+ZP7@+-ds+Yw6;evC7!9Jwf9-Q4)7^{dl-Qn<5S$t5Ik4Rwfkq z0z-Phvu~-h8{tz87LA>g+U-i?9=9*#pNO%oVhjohtrZ@uzTLE`#kFgdQ$KrnGZyB& z-;LL}&-&QKg5ibDogiLN%hCloU;0PC^q5x*^XD;Ts{S9uB}X$30-M~<7*cQ$Ikw+2 zMT*a}99-qS7XoRADLzMh&b7BJ(M^s{DHondjfR{&M{ov>AhQM?OUuEQf3g;Vh20ifROD_gqOVRTX{``7P*~IxP`TvO)4^3BW9I?e z(T^YwOZ)?Lbu0TH!3k}M#$Ui2P6ngA5Kjp)A8P<6mnZ;bTEl8ID!x7x%qs6C^fAP! z>3^i-L1T!mABs$xUAm}t^1HU{<{W;G9?vmwoh>#yRp;3T1uXH-u(w(0u+*g?g40{1 zb(nTN@qE+~iYHzQP-wEBa4`O&R37_ma7dex8AE-_>E`3f*jja-4; zPlf~`I0AMXfSi8vGwIE~?)A#0)}qQ!tp_qc*p}H(O^U>!@v91wo9$>g}UuGUt#eKd9!E&wz}Rwh7JJ>Du_d1v$hSksHQBbNOM_5 z@n*hMSPzvFy5px_1N}I|eqh;{GJ5$6;lE!d{vBU~g;Hwg_w=cw$J7FeMQfH;I>Y;%4;o#Fpp4-Ce_V_JM=|jvp8utoA-HzJe@qWSxDLPzL~gr` z|6a52r#EZA@h^T!%i=xx@BcVfYwW{$@ny zxdW_^*HgMprj`Cg!zd5U7~9?_%mS0{sO(USRYI#-hYBQN^HTls%RHyr_6{$#^Ew4> zB1xAzMUOeO{|Oxl1RbrpPt^$8Yz*ZKn3Og8pKMmxblUZ2l>){%g{Ny>r|WGB>t-wc zj-Jy25nLgbWJ;PTZ(@!w(S4!U#LS%)veyF}M#Wid%2KjClFm(i_9c-+kj!Z1I;CKW zDbmRWJxeLs0ro<`VA`+vwyQ;5aed~El$0-{Pow0II;mw%jGVbY@4B&4tBt(bkD-L~qI)1V-Ew~+E177iGoguZrubL00&BO@IN`0$ z7D^yi$*VN&tqf;t+sHrdQYS-r(ED?vre0wX;shs0M>i)oY9XZiJQ`zCw(i8kNgwJ? zX4t|cFS~x`a;su}k@*b9fs!Igiw@&k|Fnv-i*I8_yM)lq_z@CEa)Vx&FSOcz0E897 zzwtIC5ne#)ssB|Jh9M_a3NcLwtr%QagpPjnk8<7T9Amqdi|aj0`t#}eIY!)9t<^1u z&Q3K7b9P6QlV;kB)>OfUBI|C~7U5EF9ur`W@xI8nsa|-9+X$eo$im~E#zT)+=g?(t z{`+VT#Qu_G98{I*C{p(;bo8a=MvXP?b@$KUiohmS!rz(4UInaJwoZ2P?!fmJ#_>ok!M|H-Rj-roaSp-35Ef055k-lUn)xPhiGXZnolYO5WFh z?lzUU8GMC?*iPz^+q5lI|7<9Bm6_=z$bCd3>&q;vN%y@hy6&j?qYVRPN!EXmA6X7M zp0^#@Nuh=tAuGP9mjoI!NY9m5FA>2vfkQuOfG;cVZxR`sJZKSR5>a@ER6%T$q!Tl? zc*>`{5uGo-{0d*veC58ov$Y9FeITX+7J9vPVImHC{ET7U_M{91F#hi3Yl0{-+P3%} z1b)~pYPE0DFP5nl59zpruW_Qp12V78dL$$?Mu6GrFh;}M7 zJ@3(68_U@5*T8MsoaT^GMHcVQ#;LNuK4k%{goOFfYOH*U1r?Tun6LLX1%c((b3z$& zqP$@W@l7W?MDStEJG>Z3S`_ZHJcEKgu!{c|BIB6n2tysi5Tq@}g9B7$-%qcS4;YJK z6#Ihjd|Bg~4#_+_=)Za5i$`L7#*y_Y$;rC1<+lAy3)$ z8pr}?OjlEFP+Q4;OtWNNL0R(iql?6^@%MQHfE%3e)ESLKUxc>E4x{6qj6blWpF%(1 zE9wLxLTXmUS7{t)I;w?tEG_;Dk@iGso^Y?0zilU+jbY%Q&)@=w>s(+DWr?RSF1(Y` zm4pEp9)uX3FDTp`$KO*z{8W|dpZV{O7B_3y9i=Ss=AR$_B{Q>@?p_RyKAi>Pn5L@6 zWe$kH#Q@XxcRyvqcYa8!X_ zbJl6`gB$i+;+Fp(b#ECKXP2c5+?T_b3m1tEE+H8AsA{?~+qP+dpwn__xfx(gbx0TVe$2P4#J#OLB z`n|E4DTZFkLW3wMXE6~%rILmGSv0JypxwJr@0ZT^5eIZNDInHx5-UdYOoa6h5f>?N zIIGR7&&7kSnR9XbS7db6t*=!}8k)M~)k;k-@T;}!0z*X0=ew!$$qBL)A*zcw)f zWeCztMXrGx{jp+gH!CL(ry(&1cMxn!@3l{|QNPcV{mJFpWl&&XxB~tpUlu`{K!b~l zr~4PU#yWTT)1LI2h-T9=1ibW#wPwfn;rmEhO3;$vg%Lj6FLXNRGbWgTxxb!d1kt}& z-Ntp}wS@X?#6kxxkW&-B;RnchMCwm$O=u>P==n4W5K1bHpVLTue}fV02VhBWgL`jWav@ij${%imav~@iNy%#KfFQQdVGmr9qT%~Jq818K$77UE&pOd+a zG0Gg6MmX3FQ-_xTzYG7sPeI%=`ZlLi3z`U^`a|Si3Va?BWMuYX8v`GTSLe{;%<@M( z&y7pqVFI8jEoh?cff^`)NU!Y~1TPQvkvT+U-arA|?!&;W!9RPXaCxvb=?3k;RcoFL z9?)9|Y|b7zJ4Iyfa|Rz?7Dmr#+!X(voFrJM_#mysI(>;!34G5ZSQr3C(cvG1VO!QDS)(!^#PMOG1dg6W z@@B7TNpY13!oMr9UW5-iqbVp`Y*8qRm4zC~eEP{N_bvSn-5pgOTDtj5@1WHj9K|;P znZ#|NSvjdx^`&Li^aJs%p|{y8iTO$Nd2I{)F>EyB<~&|qXjX(+T@Z&^tdy|Yq} z7rIfz9s&!e2<41H$>*f1!k_QbP4Q%4y0Wb@Yd2Z-uqRG0Y?@l-7ktg*Vyz&4PJr(I z`Tbnfr1?sW5w3JV>Bcz^3bZwB8b7J2>LOxN#o~`H(*KbJACQ?NTZB~)%L=OmPeQXz z#Ng6%vCRejj}*W{&`fnI!=c4CbbzwNz`!v1b2Tp);g1a9lqNOgHgus0 zl-zz`_(S?HN&R2aw$CIBVgDTCGl40XX99yp_@(iTDsFCWy~a|1Hx3wYA%jChHnz5{ zeh*&cbx@P(+f5l?TGG?dphGy=pDuY= zPWE_qv=AmB1qFr0Xa??6iOEZ>bf_R2Ow7-xW@au=Owb_Md>Iy_1{Pazpe3yx&R2I< zF4^5s-^n7vsN&}UHYkMn`Rj9XB<7~qQhlKQYmAzpl#Jgl)PHJYVIBe70a)>2E2SB3N1|Q*}8Fx#2dn#eKX=sfy8^Q_{+*rMWj_Bzh zp%%`BhrAd0tPf=<3>^aWpU;OA-~wF^@^ftQ1quhe}+aI zFmlMCe6Qzoh1r9y3;Wq5Q&T{3j>I#uyl1-q5wR=;6<@`r_nM2F=YM1M{9Vsp!Lt~` zko{|Z;-6eEE@+y({!f^5wRN19%LOg%Ry}VkK=uFVGXA+^`zQDG4~Q}5G?fe3InMGx zM@S?$TB9lIYkT%ED!+W@F4Wrpw=?`dTzL#m>Gy^nsPby-Yi*z7Cjp+T|8HE8o`nzv z@qZ&q{~_i7tX%#}iDd;AFF^OTWmQ;M%vyGdpP-R7@JcWXS`LAMf%@fttw#MTRt_Cn z6RE40bst{5Ffq6fiVNp%4C>?Nde+MSm(@YuVV0-&RpK;M!58Af_dy%s!x^2XjQ<11 z<{v2LKWS+Ljd;7ETX2QisJQTAX#3P~2Hdkj?jZbcv(f)`iDHCoS%vDd6z>n}@j~&@ zoy=D1JYwI(|5FG44_q5ANRz5w1KJ!CYIK_~UJ@;Y2KvUEIX`J|vn1naDT>)`N$fgnsdxjo?p#K8&K!pl< zLw;UvQ^Rnkp`Qe5fRRP|s6lOWRXsub>*6LgrbQ{;#y?UNk^~Ac8+;coiBjX4#=9 zflEjfCq>0ly%{Pnp3(d+Iy9=9w1o(|HL0Q`Wl1qQHRWsnd4dOe9E=%8cQV!m4VmnO zqDunH&D(EhF=L}gl_Sgv61DyuS}QU%8o)(R6z=1fhR#Htn%Fw$ApzwVTm-9ByHUPj zw&QkI`~z2^?Mg*iD4=}pgxNenA^QxQW~LdvRzLkxVC5EAPJdEBzgqa})kb;JjGLqP zh70ANmfGjY^MyRv&ZTVLPjalUsLy30u{6G!Ce&j%2S1jTEC z1f|&S1}81Xlx;n!$(l6Cw6?}ze9&cCE~~vU=jeWMQtMAR z`G*4Tca+zLM?Bbu`b|Fhd!>Bb9*-Ma2Napm-wo2Ltuw?!&kA-nnb%)j`Z2Gqt$$N3 z7W42k<*)8)?B{b0WbRbd65LN;Y-Kccb#q$**2$AK3(orO?O%KIdLg)B}ksTy|HfT2_9%#J4{0ENi4cR@4$=H#`D1 zof%nac5T^E(3g9yYi8X$Yn8j#)fzT>l~tEFYR*m1)I9tV%)eGM#8YF~wBlDuTit=6;8p!_q`Mviks zc|wa&vcN3)%nHB9&Z`;pS=QooZl|Fj(}(NC59=y2iM5@r`?@ZD)-4W=UHb0Vom&UB zP5QR0ri(_<-uuE*E@)RfYX!%H%S_HU#yjIn0O( z>NzzB4%?#bFWgRaHs882KKzBpbng(MHr*AfN;O^>=XzGjY&W<3$_j z3Lp+bgnsk+>r)7Hz-*x}MiS4Dio^CW5LgJ!J^LWVEvuw*#P1* z0agZJBO?93lgT}M51t@{j(j86hJ~d1k5Nvg~#^SuD_;Wj=v0M0Kh@Q2P_dDXm{x)3{jyEbPu8c z`aHtDEcDofM2n54r+C}(OLNbMmn5ly zR`d^R$w;xQi7AU%f8XKo7X(%~wHKKHqU33ed)aYCP#fN5$`^fK_R;_&&8ewmjjzz8 zboE7eKPZT8AF*9fz7R_e{D(PT15ASHfU$DJhZyb}$p5xTgpsIU?{ILQG|N{#y*4)+ zkA!YM&evT~%r#{Wvv<@{1S~JQ9j_>V7`xW@b~{{9{BXlB1nYeb>vr_uHoJ1-<_~}C zHzwNg*t$vy<8!f$z3TJS)pmk?yy(BH+wrMu^}zw*^Z{G8UCiU-)60%Cs^~01@4Nc; zW3JNM5F}XWr;>@t|1Q3pO4{%7CtR#ez=asBpV-sSlO4YnuVXG;)hHy*$p_KR9PjXh zUxQ@7SksY<(~!r@KEr@vvp|xkc!j4~%>e-(7J>PiOD*t>3gMLiZ-W|1~DX>Nx$GG0myQw$3bA@k(puHtxFhjWGJJJOe3=&gzG@^ zKtqEleuXPUV)@K4b$H-vIgeyDk7%6m`^n!NIlKKvCQ{oQj~5;lxLBXZI+p4On_^Zz8D7SwNn(cW5g@h z7}kr>*EjD^^=MBJub9wQ#8b%*! z_l@`stRU`qVgMGR3+q>BSbdCLOL2F2L5lIK04x+2sju8{`gpr3;x`C_%;TD$u+Uv} zzh1!U6YutmyCVtK-t4`22?(_CsS4|7L+1C_O{u7@u5WA&DJn~^#2g+Ulv(YRU+gxR z><285FYv!fih-jBn^tUo_&bqQtV0yz->3z*;r^C5IEK_vgP{ulB8CS#tXEdW z+1vdqAUJ9Cep|W89BYBWO}tFwsw~0v6@Kl=(nBXD$Avm8@PiA5gcQaIUKlaR)wWf$taW{!w|+B2S$GlwGi-!HvVXqc z4Uw|w2b&z2dIh%1s1=a+mJpX&T+z%u%*G1M-)GbC5!ONvUb*?6+y%koX>HfH17g$u zZRh5ia&_L@$L6GNY~_edXtuQuEaJxAe5B_#|K_B|b;0;&!*Z`;on@*ky*>oeoQzWU-hGShBCp zasXtqOdYgJ=`SQ{^gvv;*ziS%a5BDqXz6J(e!c`Bx>yc5LJ4r(k0ULKcQ!-9BB0!&c5qy4Mce#;PI2`w7sxx2{ zZnazZ1R+7or`$ph3-J^mACxSPsEVn9q$DmiPEO~DsRWBUkXqEDa2t!r%2U=*gTfc* zAOFQAmnn{)y_c4WdGU3=Aax@L`!G3TGigFf(}o2?D8D3Osf37vKB3tHf{ydpiSDK zHL+p|i9B5G`w1e=aonUy!h|0+E|)DA3GpltE*C6hH`m*uV(*X2cAmC!3T^H_(X7Ig z%v@cf!XxZF(|q_=FSw{wQ4HV+#CcUocwjLloWE@Lt80Gw!WAupIm8GA@6xDO(7g(A z;2jd@9WvssxXox0*IRkGE{u=l;?c#oR&B-Pd;L@9rl!gD*B8ZeYxWK)a7>XDECK=u{%a6RAWj25$IBH z8FsyUEt>+17VcSfyoPi=n6-ZuLT}wQ$Q*e0ka+aLW59}@y%k0M?iU6%YA9)+>8Ph= z+$g>zaZw#av4t2}2?b6%53G1HP=C*}Sl>7xN`8@yr$%_;fpq5U+nxQJ1}^U2ue@?A zf-;J}JUQvgGDazQc83JBuFFzKc_JbE>ws|0DH?vckW0g|S>Xu%D>-2%)Y=V;LX|Jy z3SNV-i@i??V)wJKDaTb8NS@fLgz72gG^M=a4+ummN{~dgFgr|fT9*-wL2N44L>gP8 zOkr7ZJ0k2uZW^R?6^exw5fv4RbnjI@%+z!!@{yJOrrmHJ+Aq-!jYw&UeYx}|IdJ>$ zXIe(=_==lbO3qM0p1dk%sizj<)q7_?mJK=jHiG4Z+%~jkt$hp4mcv)fck)3grJg4c zEZ)m~Z;w~l+33N2bYocB%m)$u2Pgy9j$zE)XA0a7itop{-J7mL(I8IPN{#f!R%;YQ zpN6Zk8?(Jc?-wstc(uOk-qZY!BQ@vRUSWwgwq{p* z9jf6THsKHj_bku|d0dxa@KzE|EdEGT0C1%6#?$woP3Jf86l6fSVsgz@t(Bm@Ylg9e z)l~Uat@x5m)#aaw(Xkwk&F69nhFv|}mDa~O#AzA&>lY_glt=T946RquT@-d7nsdWM z+@UtRzgBA8Eo?K$Vm}oX0*6 z{418kDtYMcfh7(`A8uveXjg^^_JWCzwSfu47%%B03wV^McOiPR+1)s__Tu1> z(Qg)8{BdGcSWq6nDdB6vM6YbGP=pYfdU{LwKC&J+Mw|KOQ!}mun~RH;w>+{S;WtMK zrS;OdIcCJRpH+u+&~#*3Nkkc~$jod{><89QOuBTCs!PP^#95WOD3*1FAgop+($n;HFo~AU~ zU_Y(GGN&|zPp(%`Zg@&pM@Qs*Fh6i|HD7Nf`>YRp{)v3J(UU3tIz;*&-TXZW?!Fu^ z=~LVcF4~iu)l->4pli-h~M&uLi?04evzlNNZJe&lzYYis$>f34WS*W1o z2MWdWh^-k+H*vNLd{V#9zcvC`u>? z21J(%fMm$q(zpd{1BF|Ij7H`e>@wVrpUw&J>X;GzYT+Qyu5JEz& zgZVSOhK7p(K90WSh4jOh&c}YkAQ!);*J~KBUs8pWwB`CSpYT>y@ifX4PBRy44k#qb zD-eW*Z-s@Mp`zuVozpv>_f$IVe*DQEX*KeV>XZJKo{)lC$x71i7Pj2B}c9c%7Hv_Uv5MAn(c&g)GGU5=uV|4<54*?7{O7*3U_(`J(>5xPhB%->eyqvOJ z=fk!6Yfgb^P66dG8H0J($HHDF40du6a=>TIlHTB14EVF$ke8-*YYP9 z%A*F4QE$eVr1Xxe%ZOz8${K50f%rTdO;2;uYJoKmk3@w1wr16HE~OcC{Hqm2Zf zijxl#1eH@PUt7OMFQJY$%KTy`_LdydDLrkH%L(Jc!%?PFWj@_I^C`J>M%N3e?>m8s z0@-~bEEJE1Cc@#BCYq|fNUO#~*qt8oe~nykU_hf+C|g0mmUn_KMhlk^ttXjiv~0F^ zjTX6?sPU6f%Zxz7Bh!d^Kc!s)Tq?Xiu??Fccx-MEYZ6?(q@!HAA3esKk!8~`E>D6i z+>ms2lheuA$MQxcM`mnlFtbccK&!&5P7Qh~C+}-uQH%EB3r_9UcZzh<-POY>NdXDZKOdBs&l%ld*29RDuMvCk|I+L*SK|mZ%5fg;R=?W zQY1ym2*w`c4Lv;Gbq*PY4$e)uJ+(009NvHFB;-ZTc2L|S!dD!FpdRui#_!Rt^@^i2U04M7SrXc$bzismt#KnwtcJ zvgQ+Kppl5Oic0-feY{0&{Em_5lQOjb)=g%0ort3O;O*z5&zmvofx^fKq4;y7&dkH^ zOl(ff%-~l{U{-1E&79$j4q7$vjJ5|t-*-YIh5cWJ#?JvrD8V0wXQ8T1S>zbCrexLL z(iMA3X;-vLqo94}aPA!leXYg{tj~66x({>;XQ@VG<~WmU#}dGt#j+n*UODldl#KB)@`@hd2OX?}%QxZTIqr zRADKIc?Y@OjMZaT?@?@XeEEC^;15+!LRYfTvmAT8QZZPG(Cc-X=$#F^YBN)R66;!9 z&3Nx0Z1%Jp=L+F`NH9A$KI(AQN=4+=7wgP1$jn^;n6G>(17yqRu}jtRQG5l-t4kEH z%u**7tK*m8pd}8VZONaTp*kg^%vVY81xrqbz08)v<>s;b#?wS!aXWoU+}1@tYtFUw zk$A7Ob?GGg4JfEIob>>O-LguF#U0FN#o5edTxCDCkFj#inDOxXO?tM2`3}P+HV@e& zf;&HdmyJ5o;UKhCtBv09Z`lM1-w$13$})LdZl_kacDGBVyugA z_-FAGZQkOu4Y?*AOK|qXxv1&C=7pyJU@PDMPDVIr4CuTPQi1#oQ7HVl-NRfCODPs^ z^s4EBB|!if!I+bvYVxUO#EYP(rjVdiH)ftf1Wr2$KlScIs)w0DNKM+0_aL)1{B4|+ z-(tqsf&j8hd*Rl*aJzJ>|qmYJgFXUN|rG_>xPeg{u+DY^ZCe3a?D?OH{z(QStArU8{ z5L~ma9RyFc5?H;n8{8gv!^O1gAy#C)){`8nLOXV~^VH2gyp5AVkn)}&AskCGw{;_e zeNf=2US8y5w%<6rZ99QUh2wAbveS{$h1CaXWN*hDhdXiFX|M6NpAFZXJ}1QGA<{R* z<>(yY2jo4jkGt6 zlCBzmvghxrC5fpajpR9a`^Ndz%`fO>?PE?CsU7_UqsI!lnh?W3#{3yXXNfKB#Sw4j$2KD$|arL|n* zI|ivtSZ7O5?x*>mkgiJ6K_ue_Tn|ikz#lD`4tt-kjl<^%{lAMU%Fz1c%g1{|& zfWJU=yZR_kr-_k#PneU)sMj&BI5m~7IC5WJ_~;6|EEb4S5tgPOUvrnb+L3lk zv&S8Ezq3FdYCTQn+4lPQxA?iX5W%)=6&mUm=_$>g5q^a%(r4ER+pF<(NlJc_kM`qf zPfDy^XRIM;awt%TU}C;vBx?`N0c3&9UL^P(yX&N#JT2Xb4|hKKai*Cy=(!26AAXot z!O(jmZDbhMwbU5nHExdl#dmQ@(HkZd^}=np=c79MGK)$3{UHUe?VJ%?>?9TAAvR8DcL%K4d<4mb1$7EJT}tN5 zl+3R5EPCQ>$X+J#xX8ANsd+jOuA&DP;&pfqK{yyRbpQVS`;W5j`~nXA5iesM=QDv_ zloexN1yhRLN*2O0fBEIzs^Bv-OjY7C!g60VrK^_k-`ezKZWgMYzVCvM3NNd>;x5{Z z$JHiM4^pVu5wKr`vtw6OJ@k(9QZootFi=fOVM;^dCJfH9kl;4ZH``wVKh#c)kAD@W z6!Z)JsDqyKHigMehOcgmsI&(iFD5_g32(fy^Vc46ueE?X42YYI>8M(pOnmG8alE_>^g=|q+;3lR&w|?+W zX2LB~?OrEp&b2r;T|=^t<<^$aMyTakjjhPg40;_hEry} z-A%dEO@}o)ECF5}P5~WWd8X`cB9%2XhPO_AA>6%{MW&zSP2nBC`KoxWZ7< z*YqgwcUKnc>T!?F2%LTNeIvlDKh34OEU7mT-_p-GU$ZZ0exf54%m& zVK1~cP=ybIKZ1u2y4oCNQpf_}h%(!Env6MW+}{}w1c79t9e;bMyD7GL$1Xm#7(JfS zX(&-PrIQ92Im$~O7)gki!aQk0-fL+YUh>F9ag5ZKnBkQYDMC@)iZp9D$H>0MQpVtB zY?%Q4#ZfpKgoe-1VebnXgsaNYY&%((R86LJQRR&REATjnxd2#WX6thjL1yrC@-bHN z7flSr)2~|H$6STPvp6+o`iwk`=(b1m%Z%o3HvZky!IdbFo?9;F;}EAg;)%=8--6AT zEM0-`6T!kSE8a02bT6_<@O$b%`F>RJzfV0xWY_riG#P5;h>|`nzFW!31Cx>26=vK=NXbnGsSG8m^u@1sgsk=(&T+jP7Tkzy*kYJG zfdVAGfdHtMvj_Fj+NGd^bX@L8rv%d z+avO-IeNr^yoP?nn$duyJGLAHi{A>9zxbz+aA#)H1741}hm3<=Ye@S|!)PVvoGa+A z{u|2Y#`?>2(vKvr`CY6hJvQXs2U(qa86;YZKh14Sh|>r@^-c=Vnp%fhUp;~lM^RqH zs}m?aB<3Xwm*^t(+kAh)TVjjCy#okXLGB01w1|dA_w;9}C5V+^P`4Y4tYI~0igJxuH{i@*P2b+}EbhYCD2HF<7GP>LKwtk)t%SXINJ z#?Jfx&^i_2pc)<}sl4$8cl+;&Q0Bc=zw>+Dty}5uM>*>2Q;s?De0NgfC&D4uxPZ(P zAch;q{)5EWO-k0*u2dL2MzJi=H3oce=$Y$tSy_tugqhM=W_sx483oQoDj=`%RA_W_ z$w~0%OP7LPZ9h$y%&PGp6nhFbSVt-0`=i1}M9&(m_*Ai?4&I$3Al7F86(}~`uSTro z{pQo$Y?kjCPczHNmK#c})lG3)6sWFJ#mG?YTKNl}He-gAl zc#iIU^@1OE6`Q@j8TEwr2>pV*aI$tPX)c0AA#^Drhl2Ta^%rcMuDZ&OBB!^2Wic6W zKWyDoZfk+r2KY;UkX3o11c)~|J!Y<>02#r>syh)gsfj4ZUgLhj{{!}fe5UF9Id3f! zk+SZz#q$!%!iuJfs-vP&O3pEWlRajpOZ=IY8h zr7C_58I~oqDD*#-SS0bdPg${7+uvS;Vr73xc%=MI_P34ji2jI0J_M{D5 zkXq7co`YEAa_|+4lq;GP@%*~^(3h=tX(nQ|_3W^sAMHZ64j$e6L@mA7l?V?q1Rty$ zyr&}2D_k8lytG0dF_*{L+T*fhsg%JT5Dq?083VK8$eI*Jwdyn^77N~kd@E01d+Zez zGdb5JCA&NT&55J7k;d;nGiLi~nbGW{(8~+jIQ(WE9dMJbcaSpcTvK3=nWnjx>$Zk_ zYb+#|lMi>-d<+MNwS$o!*ikYUSPQX25N=B}jqS0t(`!{`{bnaAp)dke1*Gds-z_5y zy_@}^2I1J~=bXy+2VtJN^hE8@Iv?OR7k|Qx__{F>%1X9kwr21iY)N40XZGzbC~=$g zq?l~O=9r}{XI4k0ph=70H^Os82FR=^Ui<#zHz{QcX)}KXc*1$kv&=6elY^69-V)VB4br+b%kk!6AK9hsCeiO^{YV+{BgNAf2aaBr&JD99 za~gf+RxG)zFsQGCoCeGP#XX-gm3c;2x!q^wV_ufe9cvf^7MQdX;KhUvR$Em#(@}ndtRytWgOYd@5u2 zQ`Ft%_iXzqEE$q2bHgQ9Vax&SvP60RDUkY=9F`j_l%>fird6OSXT%4%6o;aEhTPe! z{9_^7T{-Bhm4gAUiAKZ5UY>wgS^!&Y0L5!Qk=IzhRLZ{g%Dz-~@KluW_Us}mSg-iq zcrEzokpTj%gs4&segy5C#bzQ?CGm-U4iptWnmAN^oGV-ccs!i%NE>JpUhz4YJvMzR zk*Jvxue}vdM5$9|9Z#}tRM8X%`&5s&l8TKMQg!TQb;;Wj2idT%({$M_b>#|$B&xte z&e;!t2Y{}=Xv3fY)b#B+1M;+``_jH?2l%{GannrhbC-q;R!Q~j)9-Ysj$tWsQpJ7{ zJr9>e)a{OK4fl*TGWLE;_@Lrw5DN#&FM&gk9C}O0`$_+~ctXtGz7&4L^IW3N4mFBB zDlYT3WVaWJuJzraqz0}Yi}lkMjlRn1WiFOHV?ncj1+=y8C^uIvOzrHvjBkehQ+_s_ zdP9iAhG?q@Fy6|K1@O1_bvIAZ-JForXz3GaUkT0bw@U+7 zL&(FAJ+(puXZK&~ETJ`h`_ZLRmV3-H_|6r1#08OoQF{QmyQHbnOkdLRjpd_Nx9XOU z{Jg5ih;udX@b^FJ`J5*+k{#k7SVpJR(lgC6{2o4P)PsDSHgKyj9~iMMk2fN=Z&-O> zK3UW>OkKZQj{-T{P6ciuM)PomNr%#hO%FZ1>MW+)s*L43@0i}SJnjZwbpxkwrNt;gjbR1VhM+GwG zyd*CV-h#iQK625X%R1(I3)WN|1ey8IDF(*ONu!v?2i3M#M{Dw1FqPzBGiljfl`b{- zE8>@GL=1~-HpG~0c5)W)?d^SvmN<@3xOfZZNWBWeH7o3(fpbfE-^f#fHb>CWOHcO( zHW4r(nQz2-Hu!H~Ck%zqq3ArVHz>eqpMGupjC+mEyp;=Q^QtnvM_OkB2 zyfaN!$7W9hi#hN_HF~uuyRA>~iNBs}CV*7H+>$m1AlAn*H z%Vyry_P;}Xcy|^vX~pRE(B91OtvPAg95Z4otxu5p<)4)IZ-x(*?}m8h5NOmdmWGO> zYz{DG@yeBhCN+Yo=0*u+E|>zgze9BPhHaxWy@_j-MB)k2W}ZA;#}qxXe%1^07<+CO zT|2YL8NAj#6s(;sMUt@F1RK| zTqa&zC7xku4BcvTzlL(2I^)fN$h4ySd+VLh?MB@7t6P-XAUob9Dv+k25F6N3b-O?1 zv|RE9b25V8M-USbzt79D5ezdam2r2uah|a#DzqWc-B{R>14f^$Rs}Oa z=Os%a3{uauXdy3#Y@;O=cu|8{%i-~&S7J{ut4hPMtl4o*9Diag#j&nTii`Vv@}U9s zytZ&lmiX;o-&p=I^cLykXFS!%I*{vQ!D{kt+M+IcQkDV#PqFS#tZ_QHU9&{cc98+> ztg>hB9}90K{NOMp($puF(Jw(ByG_=y)EFPPn1(OY^gk_s)bhC!$>+T!6-upfALyzd z>N}oMm^V&y?#~IGsRpqYz7_p0!H~NJPSKW1;>Z~YQ?=jWb|kMSyfa&g((&W-o35oKDT^Hw+?|vuN}sfn1g;T~?11`STCl z5rx}MF-D1uOwpM9@75(5dv!a11m$-H2LA}l2`kD8+rv8q>QS+OCqh1fK)f9tTN{9Q zoW8V`t+bAty8s`aW{Dqw-#pmTRo);?;O z$}!&h`5BJvY<=H(+B&zwb1vFS)rb*r=v{hwCb1X)Q&Na#15fGOg_O-((XUV~9awuC z2D%D2oxa-s=Dn@N;r`-=0`(FWf(4eVJb96tXsl+a{JU0>OYv?%w zpJ)o=E0O8qQ5=tMCtW}&YQ6HVl%qAB;VjucFW%nba>e0(va;+-k8*rYMwY-iIN116vq2(H5R9ohlsKnbiGW|h8%}B?A%ah zE|%@{TP2srzbzw_EAXULbk=scsaz$exk&z{`Z_|L8e>)|8X+nMaL*UR6Q#_v#=!X9 zy!X5LY5e3|*k0R@J*-ZR7j}4`Gug;8`VYud4oDPt>7;9MLAs}HQEy_r$V=_Ii|o4V z+zL_nX+la5*td6?&5u}rUep2oAmOE_qlFRxR}B(w6;yjF{JBVM!1voercPry52_@0 zM!*IW;G7PyvB~kYBAD6CU|htY*7!1!$_bit{@RJMP5^=1%xQKmY2JoiRK#Z!+uDV3 z^(ZDPF8_Mx`ZzVa6r)^HCd=zOGh#92 zF_xAM5ln<`qJBmiAQeKD(W}aCCV`cf@xmfTjxz1O!9i=$TB~(>Nod>fjAy}(e52MH zh(PqGj+{+HyPmAeb?-1=^-x8;ayR;nn_&xTeqh!2-Niip4t*p$D@<-I&Z~Siez&it zxISGYS=iMi<++mvR~2*nzgTRJ2eUOX?iH9mWWJjs3`#jFM%8sycL#1#II-Q$w{GW# zH|I63x$us8ApY{Ws}8IB6;D6QaKRo~kU=Azap?T(IyUW^AuIqhx6RO5pwx^<=RuqR zn%jW>h-cu-)!Qx)WQY@%;voC^;A{87K1;V>Csb&VI*_|~Q`WV>Itn^2Z4Q-<50*SK>&4IPnY zE#Mm{)k+7*3XoATdUy-AI#rgW@XMtT6X4?&5fqqKN_l5ZiB>^{B7K82eT_JK9W-?< z!;5uqa^IOAESW9~N^{)i1xApTnvLLe6q_^I8UaPVRdQxm7o2LVa5g6k{B&yf{}hTg zHmoCUW??7eTYp~B1t(M)zVqGOyq65Ul10pPzO6U>x3~uD*$d!VJiMgry|iDboSfQ? z%Gsz5D$~hR0H}u{S6GrbyOuT1XzQCdq5kHJI*V&OV^jI`7!x`Nw)1s95fKrtfP?Y_ ziv5oF%I|&#p4FX4hj*vFbJzHeT+C2rcWk8?-=lHtZW}1kbfFnjuVd+w!tr?8*zq@9 z_e2kAI-RUd)v}M1;w-Lj)5Pkj?2lhsTrf!8RA%w1mp_(BnO~g3u@AclpJ{2JFxKnI z@Lu?G8S3HQDKo3b`qqnXKGb5qQ46aOu~cquM?9DbxcPRme|Qx zcz&(QRQvhWr(cl0?@I4F{WgOiz0nTm>{e&VzV+>dX!;;6T6E~?qWYGyq&&fzA0xR-Z4T$c&Mx8pj!G$7!*a zsh$RcXZ$9&F5)+K&G)aG2oAQ;0F~Y2878eG3@4w({h^Y=^fsD9LqK#_Wa_{+V>WGk zF61LgF$LXMWE8~AE_2T^(mf@#ey_U(L2ZPu06r4hkitYMqfK>=;-ahGyD~docuH5G z_azRsvjdR6wGFmsM!#cRz5mdv7rb;rg^~oF)Kr`W?%Yl&EhViL1H*a$y1T}3vgIu*62+!;e&*~1(q+DAS>v+=FVUc8I>SC zuHXWO-6K29y~mpr+8XiYDb7e_9lxU7Ug*KUz3pOdIyXB1GLYMIwQXa$*-==(2< zj{yZI!dE|50C|O=Ft6;}YJwZ*F%*ni=|ZpZy^i`+ zDTOSJhS)h)8ig`oN_tr?9h%eunf92_>}mY(;7H9c3>#GHPm&VNAn5rDQ_ zM((K7Wz4X1_+e%*Y?%9}RY6~z3ho-CEGWjbH5x;}=fu{{CM03iEk9)h?-Q9vW*ZX7 zRRuRsq|+GgFVP?f4jo}XA1iS<==Y4Q%Nf9EL5@aC|60n5-D5fGRJJZ<$K6YQ`LUgp z{}Lf6xs+n^B#q}NbIOurhn;qurqTlB<+jNCaf->XCsoc_{*ocR_(rypRou9S%@6ES zy6OvF)Msm3Xm9GO1HY>E9H*S^w?`ll#0_Vi@U{tZeV-=IiQV|`j zfu=S!&I=jVNX|$hiRuTWPFe0-Vz3@5?CnM}c!;cxP5gyl=TX&S4S;I7p3R28IiT-F z{xZG}yKV{(3JLaTqXpYreQ5}k8yZI+IUlt`VLv*1y?^=ofOaJa>=rO85aiR9apTFT zhnEJV2IhHcA*Mhhwu&yZWXv}w2!&no8Qeq$REIW{mapt9-brfF+8DZz(ja@@vG%@z zW4_kv%1T(ZnSdkN0 z5p6wki^_=TifXs!bGn{-cC$An7+J+3Rq{}D&4TAj&Bk^63fp7*93ujs+_vV9ysUJG z&+D{_?CV(<&2?LTFr0uZ4MUIGyJ8u8zs1A^Q*=RJA9^MreCX!Te-@&g{s6jnT1@yR z-t`I(mdrDAhLa@VImTBWIh}4s1t4&V@`~|PELDr_RVc8garh%TXatyciPG(ah5krt z=h1Rx`1qqjbbIkiWgo?Ty20rn&#Ny%qYJ?l3j7?URt4+JwO;)SfBB)$5RH6rbV#Jq zm;FwS>-JM;r9tv1Wn!_GrP@3hST!o0Nm{}f&H*bq+kG|VVy}%Dugzq|olNI3){AIw z!7|-_F^d?`6!VO>ohd%5*QolVhhb6T#q&+>vB?-n1yt&Uv&UY_zS#>x8e$ zp+~Bi9METv&!#j10@mjp&7j?tTJRx(YBgz$niUQSKS!292M*dQ)(dIcIZ0LA6ziX; zH4ZRb8N!RGUliK32-R*osBY!L4)K`z{~*S&z<= zqYVF9&X_u1fGe3sxR!J8iU0An+~_pRn;TlH{X!HgJ^y3NdSCx$_}49+MPO|lGa&rcncvVn5|rll=L{hy{iU zl$rF&w&0L4M4l17phTtQ-cj01=!J)LM8(;c-Q47!(PKWJKlU?FhLmmiElzFlk(t}o zvMt#ewjlyqe-!wcS`IOI?0iqTj#_smdr+wBoM^9%n72h;wG5v77}qqy=3PQ!T`ht1 zw|->-1N1L|v6q2^RK3l^kZ`5&cTr(u(UuA=hPDdu7mgXMz%@(QzDV5)8*JCPdCW0n`zg<#2lJ|v$B)xw#qGxkV`=*d)Pyti}l|d8G z5H#Pa6}z9+7$gK~ss9 z*V_#@nPTe=hU&BiF=~S~3IkQr{gvW<ygysVdZ6kB14@MgN(KE(VcE66S1%{O>~Y3_1H2-heYy_)jQjj->hJb0l_M+?_Cntt5wy;=B#C(mp*=lq1Rpw@Zugp!qGBW|tycUn(nA8IF z8~08IKRWTxgJoX^76JldA&_9L5OFWB70#Qh9@jE&(S(=7PIJEixMY*XzC}ny+SfzR zDs1H7FR5v}G;V>h4BV1b2^PT2Ey^h<=_0?yHXZm@%Zx2XS=()jx4Twv4rtsQ)44gd ze{(K&Q{l+QVjKW$fTzSl@KQh{s0J#7CZHi`ev#i2qyh=w>c{X7f@K?+;t`eP9g`58 z46hEmQ=?8(VtypYk2fz=q}bj_JXYsez*L{!&kU z^PF?)g+eNI6YKZ_%bhdpZo$$6odv_4xI!H`g6-LZY}o>ASpBV7d@Y~*SUmSKXFh8H zhh^09dX@5cmT-HNIHcBIG=|$6Ja;j83UIX)`FQ87SunmSwLS=p4+8C#T;q{c?VeQa zoKR&IU2GarpcjHt4aidR&s6lwR0_zF@kDaCz1PI{xsU~`w4v{fWR*3B#bLt-gir;4!1V`-W?Y zCt|5q7c8^VYdc{XO)lxp&gsn=n9j>q*>1@z|KC}Ne=3{8soL+ueez_% zDkIeo!MoL>4lgN3&;sFQ}YJO$bxmBX~lmi5D;G=kYKGeSruvtrS)ye8{Swx`ZZ=^ zb;A?@Y=Ebr5WEB`fNERy;GhX;2%76sJ@0BR?|idoyWljZ@Kl$GG;lmDG6(C^7A$~h zX!YOjW!K$G|8y<6=2HB-b1{El`O-}|y^GG7XPz+4pupl;xZ?kj6@gny3kX(uWB+FX zerysFEC`4d0TL_-YIw{c=HMHnXzaE$0q`^?UYWNOEHYM}aG%IDDT{zpd=~2@-8Q&p zZALb2EFb#1X?#`3GyrUXr=SqLv`)(XB)KDKzxBff(1bhPq`#L!VqefzDpAvm-X&~MM1|t*wO>;;1};6 zngZ~P&H#|NL7}B598~xq|0_taAl?CGGf&sx#P(k75N>Qq#sOF|_?HY*AP^ZLGC=`1 z`U-e5V`L`D5U8eq2vw*mUHTd;g@ed6G;tK>4 uEC^}<9S4Bv1)xKj3+Q4bLv=0000 + diff --git a/corpus/extended-ascii/ru/cold/read/payloads.json b/corpus/extended-ascii/ru/cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/extended-ascii/ru/cold/run.json b/corpus/extended-ascii/ru/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..ad6463bfc4bfb47f16fdb81a0794cba65ee794e6 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/ansi-lang/ru/notebook", + "expected_pages": 1, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/extended-ascii/ru/cold/scripts/cold.ps1 b/corpus/extended-ascii/ru/cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/extended-ascii/ru/cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/extended-ascii/ru/cold/scripts/read.ps1 b/corpus/extended-ascii/ru/cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/extended-ascii/ru/cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/extended-ascii/ru/cold/source.json b/corpus/extended-ascii/ru/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..c9a2d1f495428e88c662543b6ae55467d85b3f88 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 4816, + "sha256": "b2f857961b76258a1582b402c048b340e89962257681426b3bfc58df8e0adbfa", + "mtime_ns": 1790487072902780320 + }, + { + "path": "synthetic.one", + "bytes": 12456, + "sha256": "3d9562625271091ff5c85c24325ab2bf12a670e81aab2caffb6b00c724d7e19f", + "mtime_ns": 1790487072903016820 + } +] diff --git a/corpus/extended-ascii/ru/cold/teardown.json b/corpus/extended-ascii/ru/cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/extended-ascii/ru/cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/m6/native-template-controls-01/ORACLE.md b/corpus/m6/native-template-controls-01/ORACLE.md index 1398b8768f069f10b066deef297000096e66ef90..0b45ecc81398e040e4768509d5e6da38c37ab811 100644 --- a/corpus/m6/native-template-controls-01/ORACLE.md +++ b/corpus/m6/native-template-controls-01/ORACLE.md @@ -1,7 +1,9 @@ # Native default page template and unaligned image container -The captured UI operations selected Hearts from the native Templates pane, selected Hearts - Decorative as the section default, and created a new page. The default background appeared automatically; `default-template.png` and `ui-operations.json` record that result. `read/` contains the three native page exports and PDFs. The clone was deleted. +The captured UI operations selected Hearts from the native Templates pane, selected Hearts - Decorative as the section default, and created a new page. The default background appeared automatically; `default-template.png` and `ui-operations.json` record that result. The clone was deleted. The section property 0x2c001d62 refers to a separate page object space whose role-2 metadata has JCID 0x2003e. Property 0x1c001ca5 contains the UTF-16 template name Hearts. These native template fields are not in the published MS-ONE object list; the typed model identifies them as observed native metadata. The original personal notebook uses the same relationship with the name Ivy. -The embedded image object starts at byte 11396, four bytes past an eight-byte file boundary. Its payload is 10056 bytes; the footer begins at 21492 after four padding bytes. Native XML exports the same image bytes (SHA-256 2035407a0540e1c4f7934db08ba4add750fcb9a62863ddd9553e7871c81a99e3). MS-ONESTORE 2.6.13 pads the object length, independent of its file position. This fixture exposed the reader's incorrect absolute-offset alignment. The regression relocates the object through all eight offsets and separately corrupts its footer. +The embedded image object starts at byte 11396, four bytes past an eight-byte file boundary. Its payload is 10056 bytes; the footer begins at 21492 after four padding bytes. MS-ONESTORE 2.6.13 pads the object length, independent of its file position. This fixture exposed the reader's incorrect absolute-offset alignment. The regression relocates the object through all eight offsets and separately corrupts its footer. + +The stored picture is Snowbound's rendering of Hearts (SHA-256 6a0593db927c1a34a94cf11093e5bd8700843323ed4fe8c905df0c595cb09c26), written over Microsoft's at the same length with a PNG comment as padding, so the file is otherwise native. `read/` is a cold OneNote read of that notebook, whose XML exports the same image bytes; `default-template.png` still shows Microsoft's art as OneNote drew it during the capture. diff --git a/corpus/m6/native-template-controls-01/notebook/synthetic.one b/corpus/m6/native-template-controls-01/notebook/synthetic.one index cebce593d432621558945ae7b5a7c38df5f0edd9..f4383ebafc3d7ba7debaeb1bd0b749220679923b 100644 GIT binary patch delta 10096 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 S#Q$k=m(Kc$iXZn1O#TbMGgqAe delta 10096 zcmV-$Cy&^K!2*QA0<-X#qu-@L6>DA%=`~3a+``nnm?a1A`;j87%+qmK1+`Z4^$=J{8<^ z+{4n_xy_%}t=RAHwBD<--rl0tsoc)jv);bo#MS2X>b2nD?%wCyf2PFA=i}VZ*xHl3 z`u+X(?(^Ki(cH<^+MB<{=Hlb6%kt*!`TY9azR=vu*30PQ&g$yF`}^y={3#nbTO>Yvr9s?xN);^5o1%$w7w-=D**+qm%g`MTob;ikv1+^Wp! z)Y_K3+M&V2<>98-f2rD;y|mxn=(^O|@89v(<=d;r+q28;_wVP?-t+qO;`8(B!Px8B zf5f! z+o{E=*{R0n&d28ByVAhP=;)o)sNeDA>GksG_4U5v;jP=Mf4Skq@6X}pw9w4a%$?G! z*}l)z(bCDGwb0SevfJI*(bd1v!_LOjuF|)%(z^8e`O~Ggz|q*)mbu;V?9=So(VVx- z$kesj+_=}cvCprx%(=7G?C1OW`n}WHw#d2a;^(Q& zvCpK#)|FCg;yUMxE#^%Y_r@Y$L*YWxH;LX|I(b~P@u(H|P#MaI&orF?0004EOGiWihy@);0000CV{K4SN<(dMFgGzW00003NC0!Z z001N2NklK)14&L3qpVz0)d2t3Lpv7k+A8AW_mT%fax}1Y+RE# zv7OjXb9#HJFE4rNFUjk@m%QG4@4de_eP`>;f9%ZcW&Q!(?cIL%x8M1-nY(iwo1SsKy_y*8^Y)dY9gF}yn2@zJ-6zc34N6hyhruzO&&ifLqpTym>3!hAE9&xa|cH`fwdDS z=jFW7f3hurJ816wD(@6ZS;j8%oP@Z4e|j^ITN=RzXV%a-XHfpX^M*Ls(&Vwre0O&& zjucW)_YN)1C*1pyuXLZ(x>n_GGo+9nClqQ8Ih=;IP=L-25`gIvCYDe{tQ4 z>Yx)pLG3D-R`f#piD6B#*7PQ%`7`{(Uc{CNen%`aFmcz@mTU!Yfq;Otv)Zg7uQt`3c)I}4!OFG?I5cRYA)BmHVv!p8ds22(!WDkMZ2zb zb$jrE2rGSlP(@20i|3$ze_Pu>x0)ryN@oA6W;ND%+H!ULtSXt)u*$)?+_?T?w?7t0 zsui17*v9ht-|4QVcXb?JjBQr)?n+0uX(j_8mGJd^6(hV7>snop1z>qcpf+2SP;>ec za2&ofV3BoPUjlx?T4bGWKmrafNAR%7I;2^?;4GaJfkjr`yalt)fA;LE=G|51)=AxJ zn^Zqu8{3}GYSTi9a(=;qqL9@1EKgbys|98t#!?e^F=*EMOgiwpcj>CpwY5A?Na2X-l=XtGq z*!vB`lD>vwZ#}?!e}q}#Lxy4LHesFV^-K(^aag+jTB-fwf4A%VV_A!aHA}iM=~eK0 zJZiDSus0wXSL)~$r&jE`49_nDn=;>eKEbTuDbqm#m*7&nuEHMcv1Hcb0>hTYuyX4L zL0W!hZ72uh$|fJZX%&#>MwNuc{e9*u?_w|QE_Ue6Rvq?5e@eB3L(S(eZLk+>Q^QiEavv%XLN|jou2|6ILfu0}%f68iZ3v)(KldSkpch!s4xPO0Xo!-V=eE&tD;yH9xyh>j>&#NPcxLM*=MI zRzc0u31&DeAdY1=+9@KRbm1(!JJPKA`~?ygjJsGue+x?m@z!w?y69K0Mt{ls0Lxdy7-aiiRe}$e_~BGYyL^ZnuKb$QCG99aVW~TkToK8 z_0e}h)?`!=ElkM(X@Az_jJ8k<0kXy=E)*DquKBMa0o=aP1zqu_9W+_wWMNFRWTJ*~ zWmxmz7DK3RZVon-`wMgMwE3JNfD2iJl~zc;$ajL(u65PDOdPye+J#Ppys|I$Oi;Z!_%x6GNfzRqt__Sg66rLZg? zFxJjkw>^dwJp40K_KQ1+=2^xzfASoNCk4-ZTmzPw)zpezGHRjl&D5|Bv^q59uSBSY z?@7%{`&y=az`8^MpeH|<5*Bun;V{+Htc`M20sNGPrD8RO>KqrFwNUt>G=`<9^G&tg zR?DCkKA(b>qMDjkjZ zfcYtxUDh3_U%C`#r=To3f3VtLpAosnqPp!n$<;`dhz&dNaz@9^bUMAdFOWlTe+pDl zY~ZwYAkM15z;g(zXyM>eIRQPD&ZMi4M<)CGJmX@JIgwf=5AMas?1>k_rSs*asM>Nf z<$S7*ElcX^sbLYut|V0VAZBHY$X!puz0hRr>Fy>O>Ld527Nm~*e@SIYVLLW0P6wg$ zk00$kYZvmI^L^vq)YP4n<|HOt7gy`yQ`cRE$F+*h>bgh-Z-;gTf%D#egF#ac6`cJ5 zWevvwzmYFP);9pXUf3{|`YtE5hia3picAY&p(0x(!{2xvV%F=xUr3!c)j@pY zFFA>F51kXvEieb|$rYzhcnDF%pv%c}f4NtAeM)`SYj!2T309x=vRw%{4ohD2S$Ep>0#GJQ1j6i8q*)->K>tHd zhVnt987Jy$>#ugyWj!GMTG@O-=NnFWnNo4}fay^d;g%Y)Vi zyYvjZx6WRQX#cVQEQ4dknT&WzD?UXU`<8biQZ(+%h8DvP2ukme zNS=F}e@6=c*O`eFaK;tCG8n1l=icQ<1xm=ow79b&?yqprVbrp+gQZ7Y(2O2QQo z0JibMV_Cpr&C0|6L{I?a?`&Yv9xPedPrfOJwJZ}E)S^zzxrFs>POa~bL(NFDxC)o@ z2JeeS1)5!)g=U?rP_zRQsR0)Q!qh7=VRcrcf8;!wGwsu|)`GiPRK)aiV%}}wVFLMC)J@;Ps;Zo=7vntjB2b-NM@YJ^YDVQeg zz3!z6pTb0IT6hHz(Q>eNGgT}-8OA+F-2y2G7KKrLmd$o{afU%V`S9-7=dkU<3P9|h zW*3&kuAUcz(G1IVZiE65>;p^&nIy7te+B_d;~*9P0D`HEQUH9cWetTrTSCKv;9CcF zYT>-a3j#st<&?E#f+ap|d*K=iurX>y+?51&W=ZV|TcCf1NFtp8v2Y8`4NA=Fw0;eu zJ{YG~Uq~o5tMkW@^@&8*X~eK4bp+5*ose~TGAnViSyczI_wN>TuF0s=TJY>De>v++ z0P(PZbsA0&{j4jij&*icWy-$-K~UfoeUHM zXVg}9;JHL*orXOD?#3<-lESC5$;82ln#8O~un6_;HhS}Y|E|cup{)dG6nIw}XJJyf z_)H`zcrb%bZI#T_!Z|m{2UfzFe;7Bc=V6dFDH$O0SOjN~vo$cM0@St>F~}O94>BP` z*0aZwQLWD~$SR&%(~w;g?mv0TZo^)I74yt}xN;E~k5;mJfxiT+#V%l#a9PN%a5OFP zZo=tGe;WxtZ*`GLiM1&(3tPOxks5b}#eln5&aRKbyC0D@EDD+SjKz#Rf4SD8cO2AQ zYa-o+SLRa7TFkr>%5Ba+JW%#POaq~%Dc~Nq*;9cWYg26hVA`~xL$H^}%#3eiaCYUU zf5;mLWw6lN>bM+hVz^*MIZ=bPSbIjwuF0WodB7XkQfy~p&qX5q!bA9`GRzz-`K`NB zaZAMPUt6%XC|YHNwZ*Rje_ON52&;b`#F_}Ui8>jg`;VXZE#BR-B-oiU`Ky@MH@$2L zwo|4q5VKD6Db|u*kIB?q60A?~d4VO?t>PAu1nD%Ejw57Ctj@xOyhMtU9F&1wotZL4 zj07~F9NMyLXL1K#f)XFX6*Sh??W)S+*5RZv(SdZm257aP6=o- zV6&%5a93!0EvkSFe^8xHy)a7~FIk{ETi57JEQ)%|R&&Uo4sVlKIEuR*ifS{o^0XSE z>rq_6ZnKwM1*(knp^KzfXR^8f5S!*&%Ow^(c`cmA+11R`nF&4Yqbl(Wliteo?qd_*e+Rn+P11?%=}Jht?X1nT zI=0Es30Rj)R^Kg4U>|!$X9oL5B6-gM)+c0g?&Z6|)L(JtZgxH@#5)trtp}k5Uj5MH zaJ=W$zX;9yMN2B1w#__qwgeL8c|ZEsa2M9{FFgKH{?V6Te);zh`QxAX#3#;HRaKSH zn2hVEz5tB4f28a~K2lo^w50c~s_N|gyWjYu|68(X5l&M-{OYUkdHnHzdHJ{St6u)l zhtb287JCrl>t0;dd23r)Q{|%A?%Ic6^;B7DuVp4e-P#FuQ_WR~L`@}Py?Y7^pWe=iUL}@I^dTv$0e~rTGHiZ|p+}iR<_NQ}xyWpRl!4g?9wU$YN;J2OK2@fV@@)k&B|vO+Bs11<_)3;ot7dst?M(oPby1nEypaD`or&B9EOA-eTf_MQsSi0y zDAw9;K;MS|&3J+}wkFqEUa)31F*cQ1D!R7%SoMn-pphnPk<0(_YVG0>uvS`g1pW_L ze`^=rh}Bk<^>S^b2F+#$bQ6_j){0r5*OL3BW3uC_wC1!NE`vpuP29cIB2kl7TKtN? zfMLs)#G9=}#!-L`ENQbo;p+m5EsKY3SyO6OubDx8ZH#5XJ}Q}-4A|9TEgSl_g~*Hx zZcdWfiU*Ffxac1KzJ>6b!6NjL+tRZ-f3*L~1>7fmi-xDko==%=LSWm4MZ1EXHNLa} z|G^exPw!?QW0vV8+Gcfvjtk?iX##_OU=6BTh80|xRl7QilfKOaaj&>5$qEj$%PNFj zu^6Vh5_*MSKxLU#0DIqJ2WDgvegJ+ohHchFSlIl7l9phFTd%FNI>>TSV;hP?e=sYT zsL0^wFEU_t$m(%`w-eV-@KK4f9BydMf;H-UnAjI_p(_w?D}hG|^CdF}tUSZFH@0!V zn}Jvp5=NX#qK^xiL6Lcvs?M3v+s?e@+eit%$psAIGC2BvKbf=GJO)?i!UYhxTQq|e zWWkl<87C3}jL6?EtOBnEzs$#4f6Xl~8{)~5(-)LG{zDeOYnBL3IyA|KMM>G$o|d35|O_`!sDfIfxsCUPb zo7iiA#`-N>m3_c<5A7_}W%aXhk<={s#&r|P$8p~RzID6(T%oYJzI6V;e=8tkBuhS# z{T%w;m^t7#j|=;(|1f8a7P!hhvh_*6>i1prCvG-g2ERXFS0Y?41B-yAAI19=_j5eg zc7fk?uiKaDZ^&Uw^Ie3{SIx1n7kjV&*SQex=K8GNg0G`E*B@*d>YGA8B-VZq{04pD z>5Pr*?|ATSq~)wN^R}oTe?6N6+(Kb<20zv6O|?J|eC}=%!dkfRx_}-lVr<-%H7(jA z&yR;I!Qj7DEGz*Ej-1%$?3BQ^9tN4&-@F$Hn)e#;VHpNrZhuTP?kAIeZ?gR|{Mo6@ z!dY}fk#R@PEDnDTG;LNw5UAjNqMz|>hjXlOJey1bwl-tOmqe)GfBEYWcKbT%yjkZ! z|1!QGJ5jC7A%H+usLRIL#(zmN$XsQ{AqZPIu46Fi*T%uV6K8|sfnl>L%3@#xSxW*l zhTWdY42y()huEO(h%8|vCYTB;sK}sUGqP+|@VouhP+j+6hOqvx_kX7_2!sTJ;%y>GsJ?CQqax|C*h-Hq6=OHJHdsrNXZPOWH`VP8wyq%eTjTM@CnB(4B^=O|fa?-ZU|+G@wI$%r zp(3!H!>lW4dI2B}#=2S8eI~#Dcp-J#!UWt-?P@uA!QT$m9*os$!{BP6a7`N(7m3yb zE%^0zy`Tjzf0XI9P_P-h>I&k#Md*I1&jOaUSReo|Fd~$|Wf3f{5Ye#cweW8U3r3&d z5SnN~R*NY&_~mQ(o@b2{Y#N%n; z)QOh?ZWP(KE0C)MD_R~HiJ4%W53s6%&;FAXE7s#cf0Pc;;v7G$wdH^K6$REpVdues zZ4U-X?NzhC`-jjXV!eWcj$_*m>^g{kfF4xZbNXn8&70L$qBZ*OACqE*?z5G^BCe4U z(h>;VfAFUzmv!{#@;@!b3QNCM=jkw5_z_E`?BSoycYOn%Ey;DF+YQ}7t5XZYhHzLh z>*&;;e|-MQKZhq5rmt}M6{I8*ur?ZSN2fY&8Ppysb2R#C-`raMmcVCaY+OEq%Wqj#q6&F?6NM)wUw%2<=@0-Q&aw6t|| zJZfsKE|?;ex4X;BySfM*Mr5P>JJFewJ4D#sf76aiZGBxd?Y?w9m(l(Aur0gGyBY#` zepYhYq#}kSQC=+^MZ5WUDpCr%Y0vpYP6*wF^_pZ@(pK|_qpSNkDby{kPuC?rdJ5~a zdqi+7zLK3o0#wKu)wZ_QgK@Pr?R;7=AK&{u7o>Va#Yy7h9L_)==MLudab>jGB5J`S81;ty?ML|E^!v)4BbWR7`Yw<7IN>1%lYL^; zP3I-2ly_KC$n>78GuEjb-9qmFC1uq7fB#6q8uJ2&NyOivc>mFbeNKkeSOTqun*Ta1 z4n@P1S}E$LG*$(M{k#a&+zK{DEvC>a62YGe4ZGQpZ)K?E>eGt6f(r+cCaz1Dn1nnB z^#H{UsZqV0AX*HjP@PjW!53zsZ{~-lX@D+cNL3M#xkkM zH}aYkIvxvMX0f=%F00p0(PrIHe=Lq#h4olh z6pLSV2kpVK)CEFoDi&k}#pWJ+D!9oTc9TlSE$lWR>)*OZtQqz&&#F~%f1ymP_)ME? zPqL(mNs#v>IN1h~L?G+yIg7Af0*i3tVH-Ax{KR?i`p0qr_J}Rb`VAYK#y0FJO0wpL zn|myI1HWS9EZYDwJ`pR_z>7IB<^dhdvNFYdt^mp)qN#NWpcX}50 z8$xZZ9h=~jqQ)G@n#=VMf3H2V-n?y(hE`ksUHA(ic)}s00b*Bd+>75;Z5(%j&&k3+ zHr-&hJ%0s0&;h2PDytn_uc6^l-@Z~_UVft+-Th+6VF^}fe%`J`x)*=ZtaYPcIb(Mm zc7UiwRZcW3ARce4I5FFtb`xO%ssqSx2r3773&M6q*6qJIHe=FQe}qVYg{Y8K)Srbn zB8C;<4v)Fjri{BFV($f#!4}l|5j!nV8z!+L+D1cufv&{{oWd#Gpn#7icRfMPoy>}8 zdzP6I?o`;ckI?o61i6wZSkTK5vF38qn@!OkW&L<;+>R;M^+A6pMlKkJ6ZDEbSgya# zbq%F9qpx`p_uhW)e|90(B(HS=9RYGe(HI|d-9|&RY;?gC7_Tkx>k6fgNo#dGWLb0l z6JY1Y31c2Ctbd&5CKwil;^Id{Rz=T`H@j|yA(s#_RmqBd?8+kS5P|WG6l<=32$;6f zPLAt1Bv!=611y2!^Kz`fApqElNi+T{_%25=OKhM;UZ7Z%e?UkhPv^gg`S zS4aj8I3q6>->Ae&U}unN8zh$IN8 zxTgJVu&Kx!STPWAv{5gnD1v zsJR@OUZy4MIHSMd)2ac~+&p4f*LcqL9nakF(Y~muRjdMPSkqqi&%k>>|G`f~-1onv zD^)VRf2d+jtKchQ_pW{faRr7GgDx^T_z(!&A+LG$h?Bi zE|X67M`TW%;ut0ZgNFUr6dW=yVJ_C3VVA9vIdPg}NVa7#hiOcMA@v&AmBqu*4xd>Q zQs+%`5NyhlVW#*xaWV~t)N44_6Grl_3aQhke>&)-XV)GRtm}tTVMx7*r38T4M7mWy zUFx){4&qr2h@i$DPlX}%B3gApDHssJGPhsqw5bl_=9cT~dx9ftQ&Gshh$RHju=;Ct z?n82?O?8wPw*YIet|wUUs!gqn^Aw<0c`nODMO&>nwWhCCkT-5_(xF{<^|)$@%XGI* zf7-BKnuT#SH89I{rB96x1YSrQ);F{CS(mA@LhmjK>t)(R1)6rPb`>KmibPVd{)uHk z13jv&I1)W1o?vkMLtyCH706Z5t|Stz70pOKx)S6HJ=XQWL~3)3Ma@LFJkKi#Jlkq^ z40~P0P=K~y;4wBFca>nvbkxvHv>KIJf1w}hVYE@Q9S*uG%B`6XR4B8;n_DqSPv6`& z?2EbM7vn&|D>OUYa7wG`6;V1zxO?0@)HWTCi6Po+i&TK_n#>@0S2aI?!=_U+Bbq1a zQO%DifT*R&dJ?lbvw(WkhHirZxB+)eNds^<9!f%HE5d3o&YplLR$>L)95JgZf2{T{ zS>UoOv=nKGrNwlE##{`JhS*>WEPT9;^@_kSHv**9MXj>&0Bm$-e5AV zvCn;BR#_2{ogty+;CNbtAMJ3+f4!6R1T5T#X7#MeuuG^oGO6Db;<9tXDrZ8$9Ot>4WLWsg3~%UWcj;hZPDn;`ybR_A>JsggDHSZtwb^Kk?oW(|e~QwQ$kl;0 z3}@PjP_!MN@ZEiMtY(>?%Bb)~!9YGXF-)|uYW(6vR71jbOCtf2z$T|2!Dmb31%O?- z$t6py-Stl^g4wc!>^S3l?&Q&FyGNS2WM)+tfq}(8c|!({tdTY-KG1mjq(WY;1Y1G{ zfQzb;sqX0Y7$}`p>=-(hVf81)2G(9o(*lJr7FVL=DjLlcq&mQ|Dk!x8V|%8IBFXtt*$)nMFZBsHcQzfEpGW3TCrcCc_TYolh_kpS+D>SZ=T S{~sG^h!OkkPtCLBQ=m-Hp|5`c diff --git a/corpus/m6/native-template-controls-01/read/environment.json b/corpus/m6/native-template-controls-01/read/environment.json index 00b1c91c33877bb34f0dc2cf539d0a5a33ea3403..5f136cd4ef1353e4b1f1e9c219aef40a69ad2a86 100644 --- a/corpus/m6/native-template-controls-01/read/environment.json +++ b/corpus/m6/native-template-controls-01/read/environment.json @@ -1,7 +1,7 @@ { "powershell": "5.1.14409.1005", "schema": "xs2010", - "hostname": "ONE-M6-A33C1CD2", - "cold": false, + "hostname": "ONE-M6-F782B029", + "cold": true, "onenote": "14.0.4763.1000" } diff --git a/corpus/m6/native-template-controls-01/read/hierarchy.xml b/corpus/m6/native-template-controls-01/read/hierarchy.xml index ec464dcc8c5f9366feae4ca0516afde08819b5fc..a55855c144608eded7f667f6382bf012aa5ead4c 100644 --- a/corpus/m6/native-template-controls-01/read/hierarchy.xml +++ b/corpus/m6/native-template-controls-01/read/hierarchy.xml @@ -1,2 +1,2 @@  - + diff --git a/corpus/m6/native-template-controls-01/read/page-000.pdf b/corpus/m6/native-template-controls-01/read/page-000.pdf index 6de9701a0cc8f7ec5a0296425c0521a3b8b0db5b..683072610667c97b348d7454745f6ddc75d03733 100644 GIT binary patch literal 85011 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ƟO+tC5>%pgOH1NdDQK-tBLXsq4AkBg{$7-Z?xL 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%0Ar9U()1=S@U5qc+MYg~_t|IP4 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 W568FMTbKPS)4V*KZ|3`VrvDc&De+hU diff --git a/corpus/m6/native-template-controls-01/read/page-000.xml b/corpus/m6/native-template-controls-01/read/page-000.xml index f790c5cdcb93e85fafeaa6348f8119a9e748dfbf..36138ba823e2dbcc3096ab3ca02ed671b81ac8a7 100644 --- a/corpus/m6/native-template-controls-01/read/page-000.xml +++ b/corpus/m6/native-template-controls-01/read/page-000.xml @@ -1,179 +1,2 @@  -iVBORw0KGgoAAAANSUhEUgAAAKsAAAIoCAMAAAAhjet0AAADAFBMVEX////d0dncvM/S6+W+4qyw -3t7akbqZ06ir2qrewNLB5OHcxtTr9/fp1eH9+/z9+fvcmL7tyN274avlzdu44d/cvc/jydjP6uTE -5ub25O779/n58/bbscncw9Lbuc2f1q3Y7++03quy3t6i1qncztez3r7gxNXm9Oq14ODu3ufbpsTJ -5+Pcz9jak7v6/f327vPcwdHcydXam7/F5uLjrcvy5u35/PrcvtDcy9bL6OPO6uq/5OT1+/vczdf7 -8ve24N/cv9D46/L06vDcxdPw4uqf1aiq0rS84uDbtsyb06jfn8Os27jw+fm64uLhpsew3KrM6dTa -lrzaocHD5eGm2Knamb20397outTZ79/x1uXbq8fbs8vs9+/n0d7z+vTi8/Pqwdjr2eTcytXcwtK/ -5MnN6eTT7Ozd8fHansCy3aq64uC54Kvcx9S336um2bPao8Ok16n03erbrsjbqcWp2anG5s7H5uK7 -0sDJ6Oid1Kjf8eTp9fLn9fW+4+Gt26q54cPvz+HmtNDM0cyd0qvZvs/U0dLJobXQ0c+y293Y0dW/ -0cPOxtOu0rey0rr0+vnTpbbA0djYlrnd8OzT7NnRnLfLyNS12dy419u71drH0cni8u7SwtHRxNKh -0q7Gzda20r3m9PDY7ujD0cb8/v22yK7h1t3KvrLg8vDWwNDDz9el0rG9rbLNnbb7/frexNTIw7HP -r7TMubPV7Of7+fq909m2yLnq4uepzbHPpMPWm7i/3Ky6srHFza/d2eSxuq6dzqnn9OC+17LGyLDb -79Du4Oju5+vKsMi8yrzIytXL6Mru+PW1tq/Gq7Ot267A47r9/P273cTewtPk0dzf1Nvh4uDfq8Xy -7vHp2OLy6e7M6eiq1Kr6/fzFytXQ6sXz+vDr9vTVqsbZ3dm02KvS7OnY1+HD0q7B16339PbBqbPa -7+rCxMDXm77T7Oeu3LjYydLL6ejQpLvKuc3G5snXp7za1dfx+ffgzdne0dq94rCy2dvE2t+lxqvA -ysDRqrXNtLPU7OkRzzSdAAAAAWJLR0QAiAUdSAAAAAxjbVBQSkNtcDA3MTIAAAADSABzvAAAI95J -REFUeF7NnXdgFMe9x/eW050k1AtCgBoCgYSECiAJ05Gw6YhmejXVgOk2YGxckzixndhOc3p7qS8v -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 -NkH/HxtpiBH77U/NAAAAAElFTkSuQmCC - + diff --git a/corpus/m6/native-template-controls-01/read/page-001.pdf b/corpus/m6/native-template-controls-01/read/page-001.pdf index 0ac8ed808bfd97720ab024f2ebba2dee3cfb7589..11040ee567fdefb86570164433198d26730369f1 100644 GIT binary patch delta 9257 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 PzyBo`A}T8OpJM+9rSk`t delta 13080 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 JNA17G`(L4tC2{}& diff --git a/corpus/m6/native-template-controls-01/read/page-001.xml b/corpus/m6/native-template-controls-01/read/page-001.xml index ac17667115fd7991586c55deaa29fafb18625258..389fbd0fe6d35248578195aec4831642d6ccba71 100644 --- a/corpus/m6/native-template-controls-01/read/page-001.xml +++ b/corpus/m6/native-template-controls-01/read/page-001.xml @@ -1,179 +1,179 @@  -iVBORw0KGgoAAAANSUhEUgAAAKsAAAIoCAMAAAAhjet0AAADAFBMVEX////d0dncvM/S6+W+4qyw -3t7akbqZ06ir2qrewNLB5OHcxtTr9/fp1eH9+/z9+fvcmL7tyN274avlzdu44d/cvc/jydjP6uTE -5ub25O779/n58/bbscncw9Lbuc2f1q3Y7++03quy3t6i1qncztez3r7gxNXm9Oq14ODu3ufbpsTJ -5+Pcz9jak7v6/f327vPcwdHcydXam7/F5uLjrcvy5u35/PrcvtDcy9bL6OPO6uq/5OT1+/vczdf7 -8ve24N/cv9D46/L06vDcxdPw4uqf1aiq0rS84uDbtsyb06jfn8Os27jw+fm64uLhpsew3KrM6dTa -lrzaocHD5eGm2Knamb20397outTZ79/x1uXbq8fbs8vs9+/n0d7z+vTi8/Pqwdjr2eTcytXcwtK/ -5MnN6eTT7Ozd8fHansCy3aq64uC54Kvcx9S336um2bPao8Ok16n03erbrsjbqcWp2anG5s7H5uK7 -0sDJ6Oid1Kjf8eTp9fLn9fW+4+Gt26q54cPvz+HmtNDM0cyd0qvZvs/U0dLJobXQ0c+y293Y0dW/ -0cPOxtOu0rey0rr0+vnTpbbA0djYlrnd8OzT7NnRnLfLyNS12dy419u71drH0cni8u7SwtHRxNKh -0q7Gzda20r3m9PDY7ujD0cb8/v22yK7h1t3KvrLg8vDWwNDDz9el0rG9rbLNnbb7/frexNTIw7HP -r7TMubPV7Of7+fq909m2yLnq4uepzbHPpMPWm7i/3Ky6srHFza/d2eSxuq6dzqnn9OC+17LGyLDb -79Du4Oju5+vKsMi8yrzIytXL6Mru+PW1tq/Gq7Ot267A47r9/P273cTewtPk0dzf1Nvh4uDfq8Xy -7vHp2OLy6e7M6eiq1Kr6/fzFytXQ6sXz+vDr9vTVqsbZ3dm02KvS7OnY1+HD0q7B16339PbBqbPa -7+rCxMDXm77T7Oeu3LjYydLL6ejQpLvKuc3G5snXp7za1dfx+ffgzdne0dq94rCy2dvE2t+lxqvA -ysDRqrXNtLPU7OkRzzSdAAAAAWJLR0QAiAUdSAAAAAxjbVBQSkNtcDA3MTIAAAADSABzvAAAI95J -REFUeF7NnXdgFMe9x/eW050k1AtCgBoCgYSECiAJ05Gw6YhmejXVgOk2YGxckzixndhOc3p7qS8v -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 -NkH/HxtpiBH77U/NAAAAAElFTkSuQmCC +iVBORw0KGgoAAAANSUhEUgAAAKsAAAIoCAMAAAAhjet0AAADAFBMVEUAAAABAQECAgIDAwMXHhna +kbrakbrakbrakbrakbrakbrakbrakbrakbrakrvakrvblr3bmr/TnLjbnMDcn8LdosPPo7bdpsXd +q8jdr8ncssqEtpHdt83duc7du8/evNDdvNDcvM/cvM/cvM/cvM/cvM/cvM/cvM/cvM/cvM/cvM/c +vM/cvc/cvc/avc/TvtDTv9DTv9DUv9DYwNHbwNHcwNHcwdLewdLcwtLdw9PdxNPdxtS6yK7dyNXd +ydXeydbgydfizNrnzd3lztzd0Nnd0Nnd0Njd0Nnd0Nnd0Nnd0Nno0d/d0dnd0dnd0dnd0dnd0dnd +0dnd0dnd0dnd0dnd0dnd0dmY0qeh0q2Z06iZ06iZ06iZ06iZ06iZ06iZ06iZ06iZ06ia06ie06mZ +06ig06uk1LTq1OHh1Nyc1Kib1Kie1ana1duq1sHt1+Tl1+Dr2+Ss287u3Oex3Kux3Ku03auz3auw +3t6w3t6w3t7v3uiw3t6w3t6w3t6x3t6x3t6w3t6w3t6w3t6w3t7V3uC938i737+937m336uy396z +39+939W9387U3+C24N/y4Ory4Oq64Ku54Ku/4La/4LS94Ni44d+84ay94ay64uC+4qy+4qy74uC8 +4uC94q694q++4qy+4qy+4qy+4qy+4qy+4qzy4uu+4ty+4qy+4q2+4qy+4qy+4+C+4+K+49+94+G/ +5OHQ5OPN6eXN6eLN6ebP6uXR6+TS6+LS6+XS6+XS6+XS6+XS6+XS6+XS6+XS6+XS6+XT7OHT7N/T +7N/T7ODT7OLT7OLT7OTT7OLX7ujc8Ovl8/Hm9PHn9PPp9fTs9vXy9vfw9/fv9/f09/j39/j5+vr7 +/fz9/f39/f39/f78/v79/v79/v79/v79/v79/v79/v78/v79/v79/v79/v7///////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////+3cRl2AAAAGXRFWHRTbm93Ym91bmQgdGVtcGxhdGUAaGVhcnRzVrgCNgAAGoNJ +REFUeNrt3btPI1uaAHBSR+AxWhsEInLoXd0AjMwFDNeZI0czEzkCjahtQFhNYMlkyyKkJeAO9BJc +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 +kL0MGX+b0JsCkHuaCJ1rGWSfkLBJtIHRrM6NbW5rHAm74trSBD2HKeE2K+j5FnASGKdGWxqElVBh +gacEq1nPJ22Cnh2zJkHo129mPc0qYB2In24UIjIgPwCyAhwZrBRCqd5bWgLWjnDDrgR+g0n1XtUD +vvPE1rD6AmWF+B3mnf7iANBK6F1sKxmjp+f8p7jWnU0fVew+YUHw/Ni6I7BaIjQqgQp+pzRXYrbq +2vG/YWnduupSBL6jRzyRY9mDcyZPYW1jfa1g39A7ID1qIGYlXdOjP5Y9kEnPKGWzQbfhBd+3Qbqq +ST0tkDV1iyTdovzsetbBKy+QssZg3/FptwIf4hF9jwnxwQ067F+GeXToKCf5sA8qEH7nCikLlku0 +zWqFKh8dbG1tHcpCz8NQZEGRs2UPmH+V+LtseqTOnIvuYO/fwEp+3CyydHF8gAbEO4LI2JVSeL1i +j/8H9g/ABkdaki4AAAlPdEVYdENvbW1lbnQAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICBmyrzgAAAAAElFTkSuQmCC diff --git a/corpus/m6/native-template-controls-01/read/page-002.pdf b/corpus/m6/native-template-controls-01/read/page-002.pdf index 89fb7d27fbbdb4a03794effd961c4b799aec3ba8..954a2453d2cd802238876a912ee02bc70a42ff6f 100644 GIT binary patch literal 95138 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 A00000 literal 85011 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

    oaAA? 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 diff --git a/corpus/m6/native-template-controls-01/read/page-002.xml b/corpus/m6/native-template-controls-01/read/page-002.xml index e44203ad99d6e09579fd00769eadf953830dd469..21905ddc3b9598c677be8fa3e97c2a8e680b0db9 100644 --- a/corpus/m6/native-template-controls-01/read/page-002.xml +++ b/corpus/m6/native-template-controls-01/read/page-002.xml @@ -1,2 +1,179 @@  - +iVBORw0KGgoAAAANSUhEUgAAAKsAAAIoCAMAAAAhjet0AAADAFBMVEUAAAABAQECAgIDAwMXHhna +kbrakbrakbrakbrakbrakbrakbrakbrakbrakrvakrvblr3bmr/TnLjbnMDcn8LdosPPo7bdpsXd +q8jdr8ncssqEtpHdt83duc7du8/evNDdvNDcvM/cvM/cvM/cvM/cvM/cvM/cvM/cvM/cvM/cvM/c +vM/cvc/cvc/avc/TvtDTv9DTv9DUv9DYwNHbwNHcwNHcwdLewdLcwtLdw9PdxNPdxtS6yK7dyNXd +ydXeydbgydfizNrnzd3lztzd0Nnd0Nnd0Njd0Nnd0Nnd0Nnd0Nno0d/d0dnd0dnd0dnd0dnd0dnd +0dnd0dnd0dnd0dnd0dnd0dmY0qeh0q2Z06iZ06iZ06iZ06iZ06iZ06iZ06iZ06iZ06ia06ie06mZ +06ig06uk1LTq1OHh1Nyc1Kib1Kie1ana1duq1sHt1+Tl1+Dr2+Ss287u3Oex3Kux3Ku03auz3auw +3t6w3t6w3t7v3uiw3t6w3t6w3t6x3t6x3t6w3t6w3t6w3t6w3t7V3uC938i737+937m336uy396z +39+939W9387U3+C24N/y4Ory4Oq64Ku54Ku/4La/4LS94Ni44d+84ay94ay64uC+4qy+4qy74uC8 +4uC94q694q++4qy+4qy+4qy+4qy+4qy+4qzy4uu+4ty+4qy+4q2+4qy+4qy+4+C+4+K+49+94+G/ +5OHQ5OPN6eXN6eLN6ebP6uXR6+TS6+LS6+XS6+XS6+XS6+XS6+XS6+XS6+XS6+XS6+XT7OHT7N/T +7N/T7ODT7OLT7OLT7OTT7OLX7ujc8Ovl8/Hm9PHn9PPp9fTs9vXy9vfw9/fv9/f09/j39/j5+vr7 +/fz9/f39/f39/f78/v79/v79/v79/v79/v79/v79/v78/v79/v79/v79/v7///////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////+3cRl2AAAAGXRFWHRTbm93Ym91bmQgdGVtcGxhdGUAaGVhcnRzVrgCNgAAGoNJ +REFUeNrt3btPI1uaAHBSR+AxWhsEInLoXd0AjMwFDNeZI0czEzkCjahtQFhNYMlkyyKkJeAO9BJc +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 +kL0MGX+b0JsCkHuaCJ1rGWSfkLBJtIHRrM6NbW5rHAm74trSBD2HKeE2K+j5FnASGKdGWxqElVBh +gacEq1nPJ22Cnh2zJkHo129mPc0qYB2In24UIjIgPwCyAhwZrBRCqd5bWgLWjnDDrgR+g0n1XtUD +vvPE1rD6AmWF+B3mnf7iANBK6F1sKxmjp+f8p7jWnU0fVew+YUHw/Ni6I7BaIjQqgQp+pzRXYrbq +2vG/YWnduupSBL6jRzyRY9mDcyZPYW1jfa1g39A7ID1qIGYlXdOjP5Y9kEnPKGWzQbfhBd+3Qbqq +ST0tkDV1iyTdovzsetbBKy+QssZg3/FptwIf4hF9jwnxwQ067F+GeXToKCf5sA8qEH7nCikLlku0 +zWqFKh8dbG1tHcpCz8NQZEGRs2UPmH+V+LtseqTOnIvuYO/fwEp+3CyydHF8gAbEO4LI2JVSeL1i +j/8H9g/ABkdaki4AAAlPdEVYdENvbW1lbnQAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICBmyrzgAAAAAElFTkSuQmCC + diff --git a/corpus/outline-edit/README.md b/corpus/outline-edit/README.md index 92f9b0d0a9aba3b94b0dedcaddeeab5a1b48d6df..42b3c1c4fe77b26dd0d0d650724244199eef7875 100644 --- a/corpus/outline-edit/README.md +++ b/corpus/outline-edit/README.md @@ -46,7 +46,7 @@ The controller selects the target again after activating the native window and transfers XML as UTF-8 data. Captured authoring scripts retain their exact source; identical immutable artifacts link to one canonical corpus copy. -`layout/candidate` applies `PreparedEdit::outline` to the five geometry and saved +`layout/candidate` applies Rust outline edits to the five geometry and saved collapse cases in `before`. `layout/cold` captures that Rust output through a fresh OneNote cache. Every active identity, child/content/structure reference, child level, and collapse default survives; text, fields, tags and explicit formatting compare diff --git a/corpus/page-model/README.md b/corpus/page-model/README.md index c1307cf93d91157ea6b88a82ede89a3b253f182d..6445e48b1851d4f3aa2066d2641789c74d1609ea 100644 --- a/corpus/page-model/README.md +++ b/corpus/page-model/README.md @@ -1,6 +1,6 @@ # Page-model edits through a cold native reopen -`candidate` is `corpus/outline-edit/before` after eight compound `PreparedEdit::page` +`candidate` is `corpus/outline-edit/before` after eight compound page-model publications, one per edited page, each combining several kinds of change in a single transaction: paragraph reorder and Unicode replacement with a bold range; a subtree move with a new nested child and an italic/colored/resized range; a subtree deletion diff --git a/corpus/paragraph-edit/README.md b/corpus/paragraph-edit/README.md index 13ebe199e69cb9b6431fcd8a29d23a249168f47b..652844d8e69cde9043718ec511614cebed57fcc4 100644 --- a/corpus/paragraph-edit/README.md +++ b/corpus/paragraph-edit/README.md @@ -26,7 +26,7 @@ notebook before completing inspection. Cold-open the split notebook for the join pass. Captured COM IDs belong to their originating sessions; discover fresh IDs when regenerating the corpus. -`rust-split` retains twelve `ParagraphSplit` outputs, their cold native captures, +`rust-split` retains twelve Rust paragraph-split outputs, their cold native captures, and a second native session typing into four empty boundary paragraphs. The hyperlink case has no output: the operation rejected fields when this row was captured, and the writer now splits before a link as OneNote does. The Rust diff --git a/corpus/protected-edit/README.md b/corpus/protected-edit/README.md index 21d294a63600c317ebb157826b4e0d042aeecc67..bc7dc43bc32e6c0817ecf781b64e2f6c3445e44a 100644 --- a/corpus/protected-edit/README.md +++ b/corpus/protected-edit/README.md @@ -1,7 +1,7 @@ # Protected page edit `candidate/notebook` is `native-encrypted/encrypted-01` after -`Notebook::save_unlocked` appended text to the first paragraph and added a +a Rust edit of the unlocked page appended text to the first paragraph and added a paragraph, exported by `an_unlocked_page_is_saved_under_the_section_key` (`ONESTORE_PROTECTED_EXPORT`). `candidate/read` is OneNote 2010's COM read from a fresh clone with an empty cache after unlocking the section in its UI with the diff --git a/corpus/table-edit/README.md b/corpus/table-edit/README.md index 24f1e88e56ac8043ec15f6b143fd8f39a5785ec6..5c413768c060e666a9906f88746c6aa4d26685f0 100644 --- a/corpus/table-edit/README.md +++ b/corpus/table-edit/README.md @@ -27,7 +27,7 @@ modification time, the indent array copied from a sibling cell (OneNote's default without one), and the flags every native cell carries, rebuilds each table's row and cell order to the model's, and writes the column count, widths, locks and border flag. New tables start as an empty paragraph whose content the structure pass replaces. -An emptied cell keeps a replacement paragraph, as the tree writer provides. +An edit that ends with an empty cell is refused; its ops refill the cell themselves. `tools/test_table_edit.py` checks all three captures without a VM. Regenerate with `ONESTORE_TABLE_EXPORT`, `ONESTORE_TABLE_EDIT_EXPORT` and `ONESTORE_NESTED_TABLE_EXPORT` set to new absolute directories while running diff --git a/crates/notebook/Cargo.toml b/crates/notebook/Cargo.toml index 798548ff3a48fc39ae90b621a5de01e91b23c191..be20d9945a195e6b3cecd1af67c01d0e0a5df09a 100644 --- a/crates/notebook/Cargo.toml +++ b/crates/notebook/Cargo.toml @@ -36,3 +36,7 @@ required-features = ["smb"] [[example]] name = "smb_concurrent_client" required-features = ["smb"] + +[[example]] +name = "smb_publish_probe" +required-features = ["smb"] diff --git a/crates/notebook/README.md b/crates/notebook/README.md index aca6350886a28468c7c89088df71beb923831914..dd79930b674765da96546172bb80dfbba09d5144 100644 --- a/crates/notebook/README.md +++ b/crates/notebook/README.md @@ -11,7 +11,7 @@ each by hash. Local success does not acknowledge publication to a shared noteboo editor ── Edit ──► section thread ─ Section::apply, INSERT edit ─┐ (the parsed section: base + sealed + open) │ one fsync per burst sync thread: remote.stamp() == base ? publish(sealed batch) ──────┤ base chunks += transaction - otherwise read once, replay the queue on it ─────────┘ conflict, or base := remote + otherwise read once, replay the queue on it ─────────┘ base := remote, conflict pages queued ``` ## Edits @@ -56,15 +56,11 @@ same queue after a relaunch. A section is `Send + Sync`: `apply(author, edit)` returns at once and reports a refusal as `Event::Rejected`; `events()` drains remote changes (`Changed(spaces)`), publication outcomes, unreachable files and failures; `notify` runs on a background thread whenever an event waits. -`conflict()`, `remote_page(space)` and `resolve(id, Resolution)` present and end a -conflict; `release(id, archive, Resolution)` ends an uncertain attempt. +`conflicts()` lists each page's conflict pages, which `page` reads and `delete_pages` +removes; `release(id, archive, Resolution)` ends an uncertain attempt. The author an +edit names is the host's: the app passes the account's full name. `import_page` creates a page holding a copy of another; `delete_pages` removes pages. -Until the application emits ops, `save`, `queue_save`, `saved`, `saving`, -`conflicts`, `queue`, `review` and `Event::Refreshed` keep the whole-page API: a -save lowers the page it is given against the stored page (`onestore::op::lower_page`) -and applies the ops. - `Section::resume(file, replica, notify)` starts from an owned `Replica` without consulting the remote file; with the `smb` feature, `Section::resume_smb(path, replica, limit, connect, notify)` binds a share-relative path, `connect` running on @@ -81,10 +77,15 @@ queue replays on it (`merge.rs`): an op whose objects the remote left alone appl as it is; a text op shifts past the remote's changes to the same text when its range stays clear of them; a move, deletion or property the remote already made is done; a page the remote already holds as the local edits leave it drops their ops. -Anything else is a `Conflict` on the batch: the queue stays on its base and the -remote image is kept for review. `resolve(id, Mine)` rewrites the remote page to the -local one (`lower_page`, once), `Theirs` drops the local ops on that page; either -applies from the conflicted batch on. +Anything else conflicts, as in OneNote 2010: the op is dropped with every later op naming +what it named, the remote's version stays the page, and the local version becomes a +conflict page under it (`SectionOp::Conflict`, queued as one more edit and published with +the rest), its conflicting objects marked, named for the author of the first edit that did +not replay. Page lists merge as OneNote 2010 merges page series: a page the remote moved +keeps the remote's place, a page moved here goes before the next page in the local order +the remote left in place, or last, and a page the remote removed comes back as a new copy +of the local one, placed the same way (`corpus/conflict-page/native-pages`, +`native-restore`). Nothing blocks the queue. Publication attempts are recorded before network I/O. An attempt confirms only when the remote holds its revisions, or every page it changed as it changed them, @@ -96,7 +97,9 @@ A durable `Published` receipt survives reopening. A schema-14 cache is converted when opened: it is exported to `.v14-recovery`, each queued page is lowered against the page the conversion has so far, and every converted page must equal the page in the old working image, -or the conversion rolls back and the open fails naming the archive. +or the conversion rolls back and the open fails naming the archive; a page it held in +conflict becomes a conflict page. A schema-15 cache's batches lose the review conflict +they recorded when opened. With the optional `smb` feature, `SmbRemote::new(client, path, limit)` binds an `notebook::smb::Client` to one share-relative file and snapshot limit. Remote identity uses the logical root @@ -161,7 +164,7 @@ conflict policy lives in the client. ## Recovery archives `export_recovery(new_path)` captures the base and remote images, the edit queue, -uncertain attempts, conflicts, receipts, downloaded media and the edit-ID sequence in one +uncertain attempts, receipts, downloaded media and the edit-ID sequence in one SQLite snapshot. It refuses existing destinations and leaves the live queue unchanged. Export to a local directory from a background thread: copying holds the cache mutex while capturing the database. Failure after the final rename can @@ -261,16 +264,16 @@ targets are `None`. With the optional `protected` feature, `Notebook::unlock(path, password)` reads a `Locked` section as `Unlocked { pages, .. }`, and -`Notebook::save_unlocked(path, password, &mut unlocked, space, page, author)` -saves an edited page under the section's key, straight to the file: nothing of -a protected section is cached or queued, a section written since `unlocked` -was read fails the save, and a wrong password is +`Notebook::apply_unlocked(path, password, &mut unlocked, author, edit)` +applies page ops and stores them under the section's key, straight to the file: +nothing of a protected section is cached or queued, a section written since +`unlocked` was read refuses the edit, and a wrong password is `Error::Protected(PasswordMismatch)`. `Section::import_page(page, author)` copies a page, usually read from another -section, to the end of this one as a page creation and a save queued like the +section, to the end of this one as one `SectionOp::Import` queued like the user's own edits, under fresh identities (`Page::copy`); payloads travel with -the model, so the copy publishes offline later like any save. Content outside +the model, so the copy publishes offline later like any edit. Content outside the model refuses to copy. A move is an import here followed by `Section::delete_pages` there. @@ -309,15 +312,20 @@ let document = onestore::document::Document::parse(&revisions)?; ``` Paths are relative to the share. The read limit bounds the complete physical -snapshot. Call from a background thread outside a Tokio runtime. Use identities -from the document and the same snapshot with `Client::commit_text` or -`Client::commit_property_bytes`; their errors retain `onestore::CommitState`. -`PreparedEdit::page` separates preparation from I/O: inspect the immutable image -and persist the intended revision identity before `Client::commit_transaction` publishes -its `transaction()`. `Client::stamp` reads a file's header and length without coordination, -for polling. `Client::confirm_snapshot` checks and flushes an observed image's stamp, then refreshes -its header version metadata without adding a revision. The caller must first -establish which intents that image contains and reread before another commit. +snapshot. Call from a background thread outside a Tokio runtime. +`Client::commit_transaction` publishes a `Transaction` from `Section::seal`; its errors +retain `onestore::CommitState`. `Client::stamp` reads a file's header and length in one +round trip without coordination, for polling. `Client::confirm` checks and flushes a +stamp, then refreshes its header version metadata without adding a revision. The caller +must first establish which intents that image contains and reread before another commit. + +A commit is eight round trips while its appended bytes and patches fit fifteen 64 KiB +writes: open; the coordination locks, identity queries and the stamp check's reads as one +compound request; the path's identity; the appended bytes and patches, the header, the +counter (twice at a carry) and the version, each as its writes and a flush in one +compound; close. The server +performs a compound in order and answers its flush once the writes are durable, so the +writes carry no write-through flag. Readers use shared native guards while writers publish under native write-open and byte-lock exclusion. Maintenance is excluded during each operation; pathname diff --git a/crates/notebook/examples/cache_probe.rs b/crates/notebook/examples/cache_probe.rs index 0e1df5bad63a10f9728981594fcb4ff3c34eeeb9..2176f33a28e75d22cf3bf794055d64bede8f1517 100644 --- a/crates/notebook/examples/cache_probe.rs +++ b/crates/notebook/examples/cache_probe.rs @@ -32,6 +32,17 @@ fn content(bytes: &[u8]) -> (ExGuid, ExGuid, String) { .unwrap() } +/// The images the queue leaves and started from, as a recovery archive records them. +fn images(cache: &Replica, path: &Path) -> Result<(Vec, Vec), Box> { + let archive = path.with_extension(format!("probe-{}", std::process::id())); + cache.export_recovery(&archive)?; + let recovery = notebook::Recovery::open(&archive)?; + let images = (recovery.snapshot()?, recovery.remote_snapshot()?); + drop(recovery); + std::fs::remove_file(&archive)?; + Ok(images) +} + fn payload(operation: u64, size: usize) -> String { format!("{operation}:🦀{}", "x".repeat(size)) } @@ -73,8 +84,7 @@ fn main() -> Result<(), Box> { } let cache = Replica::open(path)?; if mode == "read" { - let snapshot = cache.snapshot()?; - let base = cache.remote_snapshot()?; + let (snapshot, base) = images(&cache, path)?; let (sid, target, mut expected) = content(&base); let store = Store::parse(&snapshot)?; assert!(store.checksum_mismatches.is_empty()); @@ -164,8 +174,7 @@ fn main() -> Result<(), Box> { let instruction = lines.next().unwrap()?; let (operation, acknowledgement) = instruction.split_once(' ').unwrap(); let operation: u64 = operation.parse()?; - let source = cache.snapshot()?; - let (sid, oid, text) = content(&source); + let (sid, oid, text) = content(&images(&cache, path)?.0); let replacement = payload(operation, size); println!("editing {operation}"); io::stdout().flush()?; diff --git a/crates/notebook/examples/keystroke_probe.rs b/crates/notebook/examples/keystroke_probe.rs index b7829a358d5851bbc136d47c7f89db3861f717b1..d0579cd7c47d4792a6888875e22b1a5cecafc4d5 100644 --- a/crates/notebook/examples/keystroke_probe.rs +++ b/crates/notebook/examples/keystroke_probe.rs @@ -66,15 +66,15 @@ impl Remote for Memory { self.read += self.bytes.len() as u64; Ok(self.bytes.clone()) } - fn stamp(&mut self) -> io::Result> { + fn stamp(&mut self) -> io::Result { self.read += 1024; - Ok(Stamp::of(&self.bytes).ok()) + Stamp::of(&self.bytes).map_err(io::Error::other) } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { transaction.commit(self) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - onestore::confirm_snapshot(self, snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + onestore::confirm(self, base) } } @@ -135,6 +135,7 @@ fn main() -> Result<(), Box> { identity: None, created: None, margin_origin: [0.0; 2], + color: None, objects: vec![PageObject::Outline(Outline { id: new_id()?, title: false, diff --git a/crates/notebook/examples/migrate_probe.rs b/crates/notebook/examples/migrate_probe.rs index c26e8032a9628e230bad606ae71ce34f6ca22908..fa449976cf8984da3df03528e0f9076106b1bc73 100644 --- a/crates/notebook/examples/migrate_probe.rs +++ b/crates/notebook/examples/migrate_probe.rs @@ -28,14 +28,14 @@ impl Remote for Memory { fn read(&mut self) -> io::Result> { Ok(self.0.clone()) } - fn stamp(&mut self) -> io::Result> { - Ok(Stamp::of(&self.0).ok()) + fn stamp(&mut self) -> io::Result { + Stamp::of(&self.0).map_err(io::Error::other) } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { transaction.commit(self) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - onestore::confirm_snapshot(self, snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + onestore::confirm(self, base) } } @@ -72,11 +72,11 @@ fn main() -> Result<(), Box> { let pending = replica.pending()?; let summary = replica.recovery_summary()?; println!( - "{path}: converted in {:.1} ms; {} edits; {:?}; conflict {:?}", + "{path}: converted in {:.1} ms; {} edits; {:?}; conflict pages {:?}", elapsed.as_secs_f64() * 1e3, pending.len(), summary, - replica.conflict()? + replica.conflicts()? ); for edit in &pending { println!( @@ -89,11 +89,15 @@ fn main() -> Result<(), Box> { for (space, title, level) in replica.pages()? { println!(" page {space} {level} {title:?}"); } - let blocked = replica.conflict()?.is_some() - || replica.recovery_summary()?.uncertain_edits > 0; + let blocked = replica.recovery_summary()?.uncertain_edits > 0; if !pending.is_empty() && !blocked { let local = pages(&replica)?; - let mut remote = Memory(replica.remote_snapshot()?); + // The remote the queue was made on, as a recovery archive records it. + let archive = copy.with_extension("probe-recovery"); + replica.export_recovery(&archive)?; + let mut remote = + Memory(notebook::Recovery::open(&archive)?.remote_snapshot()?); + std::fs::remove_file(&archive)?; let synced = replica.sync_once(&mut remote)?; let published = { let arena = onestore::Arena::default(); diff --git a/crates/notebook/examples/open_probe.rs b/crates/notebook/examples/open_probe.rs index d093914bf64d997dbad657b4246d415fcf8f7639..d3370a13769574a67c332621068a8b3520e5cd34 100644 --- a/crates/notebook/examples/open_probe.rs +++ b/crates/notebook/examples/open_probe.rs @@ -44,6 +44,7 @@ fn generated(count: usize) -> Result, Box> { identity: None, created: None, margin_origin: [0.0; 2], + color: None, objects: vec![PageObject::Outline(Outline { id: new_id()?, title: false, @@ -106,14 +107,14 @@ impl Remote for Memory { fn read(&mut self) -> io::Result> { Ok(self.0.clone()) } - fn stamp(&mut self) -> io::Result> { - Ok(Stamp::of(&self.0).ok()) + fn stamp(&mut self) -> io::Result { + Stamp::of(&self.0).map_err(io::Error::other) } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { transaction.commit(self) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - onestore::confirm_snapshot(self, snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + onestore::confirm(self, base) } } diff --git a/crates/notebook/examples/queue_scale.rs b/crates/notebook/examples/queue_scale.rs index 4b5e4393cc08bab0482a7c7d57993a199014e4eb..28833b139bc2dbd89b4429e5a73f9aa2ce4f2d10 100644 --- a/crates/notebook/examples/queue_scale.rs +++ b/crates/notebook/examples/queue_scale.rs @@ -1,8 +1,8 @@ use notebook::{EditStatus, Remote, Replica}; use onestore::{ - CommitError, PageCreation, PreparedEdit, RevisionIndex, Store, Transaction, - document::Document, - page::{Page, PageObject, Paragraph, text::Edit}, + Arena, CommitError, ExGuid, PageCreation, Section, Stamp, Transaction, + op::{Edit, Op, PageOp, SectionOp}, + page::PageObject, }; use std::{io, path::PathBuf, time::Instant}; @@ -12,11 +12,14 @@ impl Remote for FileRemote { fn read(&mut self) -> io::Result> { onestore::read_file(&self.0) } + fn stamp(&mut self) -> io::Result { + Stamp::of(&self.read()?).map_err(io::Error::other) + } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { transaction.commit_file(&self.0) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - onestore::confirm_file_snapshot(&self.0, snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + onestore::confirm_file(&self.0, base) } } @@ -29,13 +32,14 @@ fn main() -> Result<(), Box> { std::fs::create_dir(&directory)?; let source = onestore::create_section("queue.one", "First", "Fixture")?; let creation = PageCreation::new(None, Some("Second"), "Fixture")?; - let source = PreparedEdit::create_page(&source, &creation)? - .as_bytes() - .to_vec(); - let pages = { - let store = Store::parse(&source)?; - let index = RevisionIndex::parse(&store)?; - Document::parse(&index)?.pages()? + let (source, pages) = { + let arena = Arena::default(); + let mut section = Section::open(&arena, source)?; + let ops = vec![Op::Section(SectionOp::Create(creation))]; + section.apply("Fixture", &Edit { at: 133_000_000_000_000_000, ops })?; + section.seal()?; + let pages: Vec = section.pages()?.into_iter().map(|(space, ..)| space).collect(); + (section.image(), pages) }; assert_eq!(pages.len(), 2); let path = directory.join("cache.sqlite"); @@ -48,40 +52,32 @@ fn main() -> Result<(), Box> { for n in 0..count { let edit_start = Instant::now(); let slot = n % 2; - let space = pages[slot].0; - let mut page = cache.page(space)?; + let space = pages[slot]; + let page = cache.page(space)?; let text = page .objects - .iter_mut() + .iter() .find_map(|object| match object { - PageObject::Outline(outline) => { - outline.paragraphs.iter_mut().find_map(|p| p.text_mut()) - } PageObject::Title(title) => title .outlines - .iter_mut() - .flat_map(|outline| &mut outline.paragraphs) - .find_map(|p| p.text_mut().filter(|text| text.date_field.is_none())), + .iter() + .flat_map(|outline| &outline.paragraphs) + .find_map(|p| p.text().filter(|text| text.date_field.is_none())), _ => None, }) .unwrap(); expected[slot] = format!("Edit {n} 🦀 e\u{301}"); let end = u32::try_from(text.text.text().encode_utf16().count())?; - let format = text.text.format_at(0)?.clone(); - text.text.apply(Edit { + let op = PageOp::Text { + text: text.id, range: 0..end, - replacement: Paragraph::new(expected[slot].clone(), format), - })?; - let before = cache.page(space)?; - let ops = onestore::op::lower_page(&before, &page)?; + with: expected[slot].clone(), + }; let id = cache.apply( "Fixture", - onestore::op::Edit { + Edit { at: 133_000_000_000_000_000, - ops: ops - .into_iter() - .map(|op| onestore::op::Op::Page { space, op }) - .collect(), + ops: vec![Op::Page { space, op }], }, )?; assert!(ids.last().is_none_or(|previous| *previous < id)); @@ -108,8 +104,7 @@ fn main() -> Result<(), Box> { .collect::>(), ids ); - let source = cache.snapshot()?; - verify(&source, &pages, &expected)?; + verify(&cache, &pages, &expected)?; println!( "{}", serde_json::json!({"phase":"reopen","seconds":start.elapsed().as_secs_f64()}) @@ -132,9 +127,13 @@ fn main() -> Result<(), Box> { serde_json::json!({"phase":"publish","count":ids.len(),"ms":step.elapsed().as_secs_f64()*1000.0}) ); assert!(cache.pending()?.is_empty()); - let published = cache.snapshot()?; - assert_eq!(onestore::read_file(&remote.0)?, published); - verify(&published, &pages, &expected)?; + let published = onestore::read_file(&remote.0)?; + verify(&cache, &pages, &expected)?; + let arena = Arena::default(); + let section = Section::open(&arena, published.clone())?; + for space in &pages { + assert_eq!(section.page(*space)?, cache.page(*space)?); + } drop(cache); let cache = Replica::open(&path)?; for id in &ids { @@ -152,7 +151,11 @@ fn main() -> Result<(), Box> { .collect::>(), ids ); - verify(&archive.snapshot()?, &pages, &expected)?; + let arena = Arena::default(); + let section = Section::open(&arena, archive.snapshot()?)?; + for (slot, space) in pages.iter().enumerate() { + assert_eq!(section.page(*space)?.title, expected[slot]); + } println!( "{}", serde_json::json!({"phase":"complete","count":count, @@ -163,17 +166,12 @@ fn main() -> Result<(), Box> { } fn verify( - bytes: &[u8], - pages: &[(onestore::ExGuid, onestore::ExGuid)], + cache: &Replica, + pages: &[ExGuid], expected: &[String; 2], ) -> Result<(), Box> { - let store = Store::parse(bytes)?; - assert!(store.checksum_mismatches.is_empty()); - let index = RevisionIndex::parse(&store)?; - index.validate_current()?; - let document = Document::parse(&index)?; - for (slot, (space, _)) in pages.iter().enumerate() { - assert_eq!(Page::from_space(&document, *space)?.title, expected[slot]); + for (slot, space) in pages.iter().enumerate() { + assert_eq!(cache.page(*space)?.title, expected[slot]); } Ok(()) } diff --git a/crates/notebook/examples/recovery_probe.rs b/crates/notebook/examples/recovery_probe.rs index 6a14689bbdde8f6ab1b9263621432439cbc10a44..fe95596907bcbfc4cbc8c7415f8f88ae21fb822f 100644 --- a/crates/notebook/examples/recovery_probe.rs +++ b/crates/notebook/examples/recovery_probe.rs @@ -1,10 +1,10 @@ mod support { pub mod view; } -use support::view::view; +use support::view::{cached, view}; use notebook::{EditStatus, Remote, Replica}; -use onestore::{CommitError, CommitIo, Transaction}; +use onestore::{CommitError, CommitIo, Stamp, Transaction}; use serde_json::json; use std::{ env, @@ -73,16 +73,19 @@ impl Remote for Disk { phase("publish-after"); result } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { + fn stamp(&mut self) -> io::Result { + Stamp::of(&self.read()?).map_err(io::Error::other) + } + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { phase("confirm-before"); - let result = onestore::confirm_snapshot(self, snapshot); + let result = onestore::confirm(self, base); phase("confirm-after"); result } } fn report(cache: &Replica, root: &Path) -> Result<(), Box> { - let local = view(&cache.snapshot()?)?; + let (_, _, local) = cached(cache)?; let remote = view(&fs::read(root.join("remote.one"))?)?; let (status, revision) = match cache.status(1)? { Some(EditStatus::Pending) => ("pending", None), @@ -90,14 +93,13 @@ fn report(cache: &Replica, root: &Path) -> Result<(), Box ("uncertain", Some(revision.to_string())) } Some(EditStatus::Published { revision }) => ("published", Some(revision.to_string())), - Some(EditStatus::Conflict(_)) => ("conflict", None), Some(EditStatus::Archived { archive }) => ("archived", Some(archive)), None => ("missing", None), }; println!( "{}", json!({"event":"state", "status":status, "revision":revision, - "local_text":local.text, "remote_text":remote.text, "remote_revision":remote.revision.to_string(), + "local_text":local, "remote_text":remote.text, "remote_revision":remote.revision.to_string(), "pending":cache.pending()?.iter().map(|pending| match &pending.edit.ops[..] { [onestore::op::Op::Page { op: onestore::op::PageOp::Text { range, with, .. }, .. }] => json!({"id":pending.id,"replacement":with,"range":[range.start,range.end]}), diff --git a/crates/notebook/examples/save_probe.rs b/crates/notebook/examples/save_probe.rs index 1e2af9eb89cb96d0fe96b23bf5c22000557ec5e2..6020ced7fe383cb16bb0df7a32a740613cf93674 100644 --- a/crates/notebook/examples/save_probe.rs +++ b/crates/notebook/examples/save_probe.rs @@ -1,12 +1,13 @@ -//! Times importing a generated page into a copy of a section, then saving a one-character -//! edit of it: `save_probe SECTION PARAGRAPHS`. +//! Times importing a generated page into a copy of a section, then typing one character +//! into it until durable and queued: `save_probe SECTION PARAGRAPHS`. use notebook::session::Section; use onestore::{ document::{Format, Layout}, + op::{Edit, Op, PageOp}, page::{ Outline, Page, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, - text::{Edit, new_id}, + text::new_id, }, }; use std::time::{Duration, Instant}; @@ -49,6 +50,7 @@ fn main() -> Result<(), Box> { identity: None, created: None, margin_origin: [0.0; 2], + color: None, objects: vec![PageObject::Outline(Outline { id: new_id()?, title: false, @@ -67,38 +69,38 @@ fn main() -> Result<(), Box> { let start = Instant::now(); let space = section.import_page(&imported, "Probe")?; println!("import {count}: {:?}", start.elapsed()); - let before = section.page(space)?; - let edit = |page: &Page, word: &str| -> Result> { - let mut after = page.clone(); - let text = after - .objects - .iter_mut() - .find_map(|object| match object { - PageObject::Outline(outline) if outline.paragraphs.len() == count => { - outline.paragraphs[count / 2].text_mut() - } - _ => None, - }) - .ok_or("the imported outline is missing")?; - let format = text.text.format_at(0)?.clone(); - text.text.apply(Edit { - range: 0..0, - replacement: Paragraph::new(word.into(), format), - })?; - Ok(after) + let text = section + .page(space)? + .objects + .iter() + .find_map(|object| match object { + PageObject::Outline(outline) if outline.paragraphs.len() == count => { + outline.paragraphs[count / 2].text().map(|text| text.id) + } + _ => None, + }) + .ok_or("the imported outline is missing")?; + let typed = |word: &str| Edit { + at: 133_000_000_000_000_000, + ops: vec![Op::Page { + space, + op: PageOp::Text { + text, + range: 0..0, + with: word.into(), + }, + }], }; - let after = edit(&before, "x")?; let start = Instant::now(); - section.save(space, &before, &after, "Probe")?; - println!("save: {:?}", start.elapsed()); - let next = edit(&after, "y")?; + section.replica().apply("Probe", typed("x"))?; + println!("typed, durable: {:?}", start.elapsed()); let start = Instant::now(); - section.queue_save(space, after, next, "Probe")?; - println!("queue_save: {:?}", start.elapsed()); + section.apply("Probe", typed("y"))?; + println!("typed, queued: {:?}", start.elapsed()); let deadline = start + Duration::from_secs(600); while Instant::now() < deadline { - if let Some((_, save)) = section.saved().pop() { - println!("queued save done: {:?} {save:?}", start.elapsed()); + if section.replica().pending()?.len() == 2 { + println!("queued edit durable: {:?}", start.elapsed()); break; } std::thread::sleep(Duration::from_millis(1)); diff --git a/crates/notebook/examples/session_client.rs b/crates/notebook/examples/session_client.rs index 2cae639fd49e77790f6457afab66f52ee6067608..06f0f0109f87f17dd635bc9b32977647d1e28a95 100644 --- a/crates/notebook/examples/session_client.rs +++ b/crates/notebook/examples/session_client.rs @@ -1,15 +1,16 @@ -//! Drives a section the way the application does: open it through a session, save -//! page-model edits, wait for their publication and report the page texts. +//! Drives a section the way the application does: open it through a session, apply +//! edits, wait for their publication and report the page texts. //! `session_client SECTION CACHE_DIR LABEL` edits the first body paragraph of every //! page whose title starts with "Move" and adds one outline per page; each launch //! reports the pending queue it found and the receipts it obtained. use notebook::{ EditStatus, - session::{Event, Save, Section}, + session::{Event, Section}, }; use onestore::{ ExGuid, + op::PageOp, page::{Outline, Page, PageObject, PageParagraph, ParagraphContent, TextObject, text::new_id}, }; use std::{ @@ -46,28 +47,9 @@ fn texts(page: &Page) -> Vec { out } -fn edit(page: &mut Page, label: &str) { +/// Types `label` before the first body paragraph and adds an outline below the body. +fn edit(page: &Page, label: &str) -> Vec { let text = body_text(page).expect("a body paragraph"); - for object in &mut page.objects { - let PageObject::Outline(outline) = object else { - continue; - }; - if let Some(paragraph) = outline - .paragraphs - .iter_mut() - .find(|p| p.text().is_some_and(|t| t.id == text)) - { - let target = paragraph.text_mut().unwrap(); - let format = target.text.format_at(0).unwrap().clone(); - target - .text - .apply(onestore::page::text::Edit { - range: 0..0, - replacement: onestore::page::Paragraph::new(format!("{label} "), format), - }) - .unwrap(); - } - } let template = page .objects .iter() @@ -110,12 +92,21 @@ fn edit(page: &mut Page, label: &str) { paragraphs: vec![paragraph], unsupported: Vec::new(), }; - let at = page - .objects - .iter() - .position(|o| matches!(o, PageObject::Title(_))) - .unwrap_or(page.objects.len()); - page.objects.insert(at, PageObject::Outline(outline)); + let title = page.objects.iter().find_map(|object| match object { + PageObject::Title(title) => Some(title.id), + _ => None, + }); + vec![ + PageOp::Text { + text, + range: 0..0, + with: format!("{label} "), + }, + PageOp::Add { + object: PageObject::Outline(outline), + before: title, + }, + ] } fn main() -> Result<(), Box> { @@ -138,13 +129,21 @@ fn main() -> Result<(), Box> { .iter() .filter(|(_, title, _)| title.starts_with("Move")) { - let before = section.page(*space)?; - let mut after = before.clone(); - edit(&mut after, label); - match section.save(*space, &before, &after, "session client")? { - Save::Queued(id) => queued.push((id, *space, title.clone(), texts(&after))), - other => return Err(format!("{title}: {other:?}").into()), + let mut page = section.page(*space)?; + let ops = edit(&page, label); + for op in &ops { + onestore::op::predict(&mut page, op)?; } + let unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?; + let edit = onestore::op::Edit { + at: (unix.as_secs() + 11_644_473_600) * 10_000_000, + ops: ops + .into_iter() + .map(|op| onestore::op::Op::Page { space: *space, op }) + .collect(), + }; + let id = section.replica().apply("session client", edit)?; + queued.push((id, *space, title.clone(), texts(&page))); } let deadline = Instant::now() + Duration::from_secs(120); let mut receipts = Vec::new(); @@ -176,7 +175,7 @@ fn main() -> Result<(), Box> { } Event::Failed(error) => return Err(error.into()), Event::Rejected { error, .. } => return Err(error.into()), - Event::Refreshed | Event::Changed(_) => {} + Event::Changed(_) => {} } } // A batch reports its newest edit; each edit's receipt is its own. diff --git a/crates/notebook/examples/smb_concurrent_client.rs b/crates/notebook/examples/smb_concurrent_client.rs index 9c55d513e961b1c553f0b0957032c8fb94cd6d2e..797684d46afed217caf77c0488bf72ccd9fde4f2 100644 --- a/crates/notebook/examples/smb_concurrent_client.rs +++ b/crates/notebook/examples/smb_concurrent_client.rs @@ -1,5 +1,7 @@ #[path = "../../onestore/examples/support/concurrent.rs"] mod concurrent; +#[path = "support/edit.rs"] +mod edit; use notebook::smb::{Client, Credentials}; use std::{env, time::Duration}; @@ -16,7 +18,10 @@ fn main() -> Result<(), Box> { &args, |path| client.read(path, 256 * 1024 * 1024), |path, source, space, object, range, replacement| { - client.commit_text(path, source, space, object, range, replacement) + client.commit_transaction( + path, + &edit::replaced(source, space, object, range, replacement)?, + ) }, ) } diff --git a/crates/notebook/examples/smb_offline_client.rs b/crates/notebook/examples/smb_offline_client.rs index 73e90b9eb2bfb40e097ca0813d8695c521943bb3..dbf7ceeae4fe935f5bb7c558ffbca12d6b1de756 100644 --- a/crates/notebook/examples/smb_offline_client.rs +++ b/crates/notebook/examples/smb_offline_client.rs @@ -3,7 +3,7 @@ mod concurrent; use notebook::smb::{Client, Credentials}; use notebook::{EditStatus, Error, Remote, Replica, SmbRemote}; -use onestore::{CommitError, CommitState, ExGuid, RevisionIndex, Store, Transaction}; +use onestore::{CommitError, CommitState, ExGuid, RevisionIndex, Stamp, Store, Transaction}; use serde_json::json; use std::{ env, @@ -18,7 +18,7 @@ mod support { pub mod view; } use concurrent::{DocumentView, document_view}; -use support::view::view; +use support::view::{cached, view}; impl DocumentView { fn changes(&self, before: &Self) -> Self { @@ -79,6 +79,13 @@ impl Remote for Traced { Ok(bytes) } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { + // Publication reads nothing while the stamp holds; observe an unseen base once. + if Stamp::of(&self.read).ok().as_ref() != Some(transaction.base()) { + self.read().map_err(|error| CommitError { + state: CommitState::NotCommitted, + error, + })?; + } let mut image = self.read.clone(); transaction.apply(&mut image).map_err(|error| CommitError { state: CommitState::NotCommitted, @@ -193,10 +200,22 @@ impl Remote for Traced { thread::sleep(Duration::from_millis(10)); } } + if result.is_ok() { + self.read = image; + self.before = Some((after.text, documents)); + } result } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { + fn stamp(&mut self) -> io::Result { + self.remote.stamp() + } + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { let captured = (|| -> Result<_, Box> { + // A confirmation follows the read of its image, except for edits that changed nothing. + if Stamp::of(&self.read).ok().as_ref() != Some(base) { + self.read = self.remote.read()?; + } + let snapshot = &self.read[..]; let store = Store::parse(snapshot)?; let index = RevisionIndex::parse(&store)?; let revisions = index @@ -240,7 +259,7 @@ impl Remote for Traced { error: io::Error::other(error.to_string()), })?; let started = now(); - let result = self.remote.confirm(snapshot); + let result = self.remote.confirm(base); println!( "{}", json!({"event":"remote_confirm", "started_us":started, "finished_us":now(), "revisions":revisions, "current_revisions":current, "capture":capture.as_ref().and_then(|path| path.file_name()).map(|name| name.to_string_lossy()), "text":self.before.as_ref().map(|(text,_)|text), @@ -343,6 +362,7 @@ fn main() -> Result<(), Box> { } })?; let mut ids = Vec::new(); + let mut tokens = Vec::new(); let result = (|| -> Result<(), Box> { let mut generated = 0; let mut received = 0; @@ -380,62 +400,46 @@ fn main() -> Result<(), Box> { 8 }; if generated < operations && pending.len() < capacity { - let source = cache.snapshot()?; - let target = view(&source)?; - let at = u32::try_from(target.text.encode_utf16().count())?; + let (space, object, text) = cached(&cache)?; + let at = u32::try_from(text.encode_utf16().count())?; let token = format!(" [{}:{}]", args[2], generated); let started = now(); match cache.apply( "Offline document writer", - append(target.space, target.object, at, &token), + append(space, object, at, &token), ) { Ok(id) => { println!( "{}", - json!({"event":"local_commit", "id":id, "operation":generated, "space":target.space.to_string(), "object":target.object.to_string(), "before":target.text, "token":token, "started_us":started, "finished_us":now()}) + json!({"event":"local_commit", "id":id, "operation":generated, "space":space.to_string(), "object":object.to_string(), "before":text, "token":token, "started_us":started, "finished_us":now()}) ); ids.push(id); + tokens.push(token); generated += 1; } other => return Err(format!("Unexpected local result: {other:?}").into()), } } - if let Some(conflict) = cache.conflict()? { - // Concurrent appends meet at the end of the text: take the remote text and - // append the queued tokens it lacks after it again. - let remote = view(&cache.remote_snapshot()?)?; - let tokens: Vec = cache - .pending()? + if received == ids.len() && cache.pending()?.is_empty() { + // Concurrent appends meet at the end of the text: a merge keeps the remote's + // text and this writer's on a conflict page. Append the published tokens + // the text lacks after it again. + let (space, object, text) = cached(&cache)?; + let missing: Vec = tokens .iter() - .flat_map(|queued| queued.edit.ops.iter()) - .filter_map(|op| match op { - onestore::op::Op::Page { - op: onestore::op::PageOp::Text { with, .. }, - .. - } => Some(with.clone()), - _ => None, - }) - .filter(|token| !remote.text.contains(token.as_str())) + .filter(|token| !text.contains(token.as_str())) + .cloned() .collect(); - match cache.resolve(conflict.id, notebook::Resolution::Theirs) { - Ok(()) => { - let mut at = u32::try_from(remote.text.encode_utf16().count())?; - for token in &tokens { - cache.apply( - "Offline document writer", - append(remote.space, remote.object, at, token), - )?; - at += u32::try_from(token.encode_utf16().count())?; - } - println!( - "{}", - json!({"event":"reviewed_append", "id":conflict.id, "remote":remote.text, "tokens":tokens, "at_us":now()}) - ); - } - Err(Error::Io(error)) - if [io::ErrorKind::WouldBlock, io::ErrorKind::InvalidInput] - .contains(&error.kind()) => {} - Err(error) => return Err(error.into()), + let mut at = u32::try_from(text.encode_utf16().count())?; + for token in &missing { + ids.push(cache.apply("Offline document writer", append(space, object, at, token))?); + at += u32::try_from(token.encode_utf16().count())?; + } + if !missing.is_empty() { + println!( + "{}", + json!({"event":"reviewed_append", "remote":text, "tokens":missing, "at_us":now()}) + ); } } if generated == operations && received == ids.len() { diff --git a/crates/notebook/examples/smb_publish_probe.rs b/crates/notebook/examples/smb_publish_probe.rs new file mode 100644 index 0000000000000000000000000000000000000000..a6f5cb71cc1562cc4f18d06d39f695c82ab16be6 --- /dev/null +++ b/crates/notebook/examples/smb_publish_probe.rs @@ -0,0 +1,110 @@ +//! Publishes typed keystrokes to a Samba lab one at a time and reports each publication's +//! wall time: `smb_publish_probe ADDRESS KEYSTROKES [CONTROL]`. With `CONTROL`, the +//! `tools/smb-proxy.py` control file, each publication is marked in the proxy's trace. + +use notebook::{ + EditStatus, Replica, SmbRemote, + smb::{Client, Credentials}, +}; +use onestore::op::{Edit, Op, PageOp}; +use std::{ + env, fs, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; + +fn main() -> Result<(), Box> { + let args: Vec<_> = env::args().skip(1).collect(); + let [address, count, control @ ..] = &args[..] else { + return Err("smb_publish_probe ADDRESS KEYSTROKES [CONTROL]".into()); + }; + let count: u32 = count.parse()?; + let mark = |phase: String| -> std::io::Result<()> { + if let [control] = control { + fs::write( + format!("{control}.tmp"), + format!("{{\"phase\":\"{phase}\"}}"), + )?; + fs::rename(format!("{control}.tmp"), control)?; + // The proxy polls its control file every 50 ms. + std::thread::sleep(Duration::from_millis(120)); + } + Ok(()) + }; + let connect = || { + Client::connect( + address, + "agent", + Credentials::default(), + Duration::from_secs(5), + ) + }; + let path = format!( + "probe-{}.one", + SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos() + ); + let source = onestore::create_section(&path, "Body", "Probe")?; + connect()?.create(&path, &source)?; + let directory = tempfile::tempdir()?; + let replica = Replica::create(directory.path().join("cache.sqlite"), &source)?; + let space = replica.pages()?[0].0; + let text = replica + .page(space)? + .objects + .iter() + .find_map(|object| match object { + onestore::page::PageObject::Outline(outline) => outline + .paragraphs + .iter() + .find_map(|p| p.text().map(|t| t.id)), + _ => None, + }) + .ok_or("no text")?; + let mut remote = SmbRemote::new(connect()?, path.clone(), 1 << 24); + mark("idle".into())?; + let mut idle = Vec::new(); + for _ in 0..count { + let started = Instant::now(); + assert_eq!(replica.sync_once(&mut remote)?.edit, None); + idle.push(started.elapsed()); + } + let mut published = Vec::new(); + for n in 0..count { + replica.apply( + "Probe", + Edit { + at: 133_000_000_000_000_000, + ops: vec![Op::Page { + space, + op: PageOp::Text { + text, + range: 4 + n..4 + n, + with: "x".into(), + }, + }], + }, + )?; + mark(format!("publish-{n}"))?; + let started = Instant::now(); + let synced = replica.sync_once(&mut remote)?; + published.push(started.elapsed()); + assert!(matches!( + synced.edit, + Some((_, EditStatus::Published { .. })) + )); + } + mark("done".into())?; + let quantiles = |mut times: Vec| { + times.sort(); + let at = |q: f64| times[((times.len() - 1) as f64 * q) as usize].as_secs_f64() * 1000.0; + format!( + "p50 {:.2} ms, p90 {:.2} ms, max {:.2} ms", + at(0.5), + at(0.9), + at(1.0) + ) + }; + println!("idle poll: {}", quantiles(idle)); + println!("publish: {}", quantiles(published)); + connect()?.delete(&path)?; + Ok(()) +} diff --git a/crates/notebook/examples/smb_reconnect_client.rs b/crates/notebook/examples/smb_reconnect_client.rs index d72a42c9c0a88db608811c8e5b08601222c2f8ee..814635fe985b4894d77a70be1ce638c7078c4012 100644 --- a/crates/notebook/examples/smb_reconnect_client.rs +++ b/crates/notebook/examples/smb_reconnect_client.rs @@ -1,9 +1,11 @@ #[path = "../../onestore/examples/support/concurrent.rs"] mod concurrent; +#[path = "support/edit.rs"] +mod edit; use notebook::smb::{Client, Credentials}; use onestore::{ - CommitError, CommitState, ExGuid, RevisionIndex, Store, + CommitError, CommitState, ExGuid, RevisionIndex, Stamp, Store, document::{Document, Kind}, }; use serde_json::json; @@ -155,14 +157,14 @@ fn main() -> Result<(), Box> { &args, read, |path, source, space, object, range, replacement| { - let outcome = client.borrow().as_ref().unwrap().commit_text( - path, - source, - space, - object, - range, - replacement, - ); + let outcome = + edit::replaced(source, space, object, range, replacement).and_then(|transaction| { + client + .borrow() + .as_ref() + .unwrap() + .commit_transaction(path, &transaction) + }); let Err(error) = outcome else { return Ok(()); }; @@ -209,14 +211,12 @@ fn main() -> Result<(), Box> { error: io::Error::other(failure.to_string()), })?; if published { - let confirmation = client.borrow().as_ref().unwrap().commit_text( - path, - ¤t, - space, - object, - 0..0, - "", - ); + let confirmation = Stamp::of(¤t) + .map_err(|failure| CommitError { + state: CommitState::NotCommitted, + error: io::Error::other(failure.to_string()), + }) + .and_then(|base| client.borrow().as_ref().unwrap().confirm(path, &base)); if let Err(failure) = confirmation && failure.state != CommitState::Committed { diff --git a/crates/notebook/examples/support/edit.rs b/crates/notebook/examples/support/edit.rs new file mode 100644 index 0000000000000000000000000000000000000000..888dc0d03cfc8e27b2af03972a7da368d7b7d05c --- /dev/null +++ b/crates/notebook/examples/support/edit.rs @@ -0,0 +1,49 @@ +use onestore::{ + Arena, CommitError, CommitState, ExGuid, Section, Transaction, + op::{Edit, Op, PageOp}, +}; +use std::{ + io, + ops::Range, + time::{SystemTime, UNIX_EPOCH}, +}; + +/// The transaction replacing `range` of `text` in `image`, typed now. +pub fn replaced( + image: &[u8], + space: ExGuid, + text: ExGuid, + range: Range, + with: &str, +) -> Result { + let failed = |error: String| CommitError { + state: CommitState::NotCommitted, + error: io::Error::new(io::ErrorKind::InvalidData, error), + }; + let arena = Arena::default(); + let mut section = Section::open(&arena, image.to_vec()).map_err(|e| failed(e.to_string()))?; + let at = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| failed(e.to_string()))? + .as_nanos() as u64 + / 100 + + 116_444_736_000_000_000; + let edit = Edit { + at, + ops: vec![Op::Page { + space, + op: PageOp::Text { + text, + range, + with: with.to_owned(), + }, + }], + }; + section + .apply("Concurrent client", &edit) + .map_err(|e| failed(format!("{e:?}")))?; + section + .seal() + .map_err(|e| failed(e.to_string()))? + .ok_or_else(|| failed("The edit changed nothing".into())) +} diff --git a/crates/notebook/examples/support/view.rs b/crates/notebook/examples/support/view.rs index 280be0443d90acca9f8db8dd3d7f0dc63d1b5ecc..96977f14d8ccb470793b81099e4be8aeff8c4785 100644 --- a/crates/notebook/examples/support/view.rs +++ b/crates/notebook/examples/support/view.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use onestore::{ ExGuid, RevisionIndex, Store, document::{Document, Kind}, @@ -55,3 +57,42 @@ pub fn view(bytes: &[u8]) -> Result> { } Ok(found.pop().unwrap()) } + +/// The concurrent-edit paragraph of `page`: its text object and text. +fn in_page(page: &onestore::page::Page) -> Option<(ExGuid, String)> { + let mut pending: Vec<&onestore::page::PageParagraph> = page + .objects + .iter() + .flat_map(|object| match object { + onestore::page::PageObject::Outline(outline) => outline.paragraphs.iter().collect(), + _ => Vec::new(), + }) + .collect(); + pending.reverse(); + while let Some(paragraph) = pending.pop() { + if let Some(text) = paragraph.text() + && text.text.text().starts_with("Concurrent edits:") + { + return Some((text.id, text.text.text().to_owned())); + } + if let onestore::page::ParagraphContent::Table(table) = ¶graph.content { + pending.extend(table.rows.iter().flat_map(|row| &row.cells).flat_map(|cell| &cell.paragraphs).rev()); + } + } + None +} + +/// The concurrent-edit paragraph as the queued edits leave it: its page, text object and +/// text. +pub fn cached(cache: ¬ebook::Replica) -> Result<(ExGuid, ExGuid, String), Box> { + let mut found = Vec::new(); + for (space, ..) in cache.pages()? { + if let Some((object, text)) = in_page(&cache.page(space)?) { + found.push((space, object, text)); + } + } + match <[_; 1]>::try_from(found) { + Ok([found]) => Ok(found), + Err(_) => Err("Expected one concurrent-edit paragraph".into()), + } +} diff --git a/crates/notebook/src/bin/onestore-diagnostic.rs b/crates/notebook/src/bin/onestore-diagnostic.rs index cb5fbe8ecd9e720d60238cfec56dd06c59a8ad46..f5bc0fa7ff1552d0572fb5b56ede701b2c139ffd 100644 --- a/crates/notebook/src/bin/onestore-diagnostic.rs +++ b/crates/notebook/src/bin/onestore-diagnostic.rs @@ -1,4 +1,11 @@ -use onestore::{ExGuid, Insertion, PreparedEdit, TextAttribute}; +use onestore::{ + Arena, ExGuid, Section, TextAttribute, + document::{Format, Layout}, + op::{Op, PageOp}, + page::{ + Outline, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, text::new_id, + }, +}; use serde::Deserialize; use serde_json::{Value, json}; use std::{ @@ -40,6 +47,33 @@ enum Action { }, } +/// A new paragraph holding `text` in OneNote's default text style. +fn paragraph(text: String) -> Result> { + let format = Format { + font: Some("Calibri".into()), + font_size: Some(11.0), + language: Some(0x409), + ..Default::default() + }; + Ok(PageParagraph { + id: new_id()?, + parent: None, + level: 1, + style: None, + format: Format::default(), + content: ParagraphContent::Text(TextObject { + id: new_id()?, + date_field: None, + text: Paragraph::new(text, format), + tags: Vec::new(), + }), + lists: Vec::new(), + tags: Vec::new(), + media: Default::default(), + collapsed: false, + }) +} + fn run(args: &[std::ffi::OsString]) -> Result> { if let [mode, root] = args && mode == "catalog" @@ -79,34 +113,93 @@ fn run(args: &[std::ffi::OsString]) -> Result> let edit: Edit = serde_json::from_reader(io::stdin().lock())?; let sid = edit.space; let oid = edit.object; - let prepared = match edit.action { + let (op, author) = match edit.action { Action::Text { start, end, replacement, - } => PreparedEdit::text(&bytes, sid, oid, start..end, &replacement)?, + } => ( + PageOp::Text { + text: oid, + range: start..end, + with: replacement, + }, + String::new(), + ), Action::Format { start, end, attributes, - } => PreparedEdit::format(&bytes, sid, oid, start..end, &attributes)?, + } => ( + PageOp::Format { + text: oid, + range: start..end, + set: attributes, + clear: Vec::new(), + }, + String::new(), + ), Action::Paragraph { before, text, author, - } => PreparedEdit::insert( - &bytes, - sid, - &Insertion::paragraph(oid, before, &text, &author)?, - )?, + } => ( + PageOp::Insert { + container: oid, + before, + paragraphs: vec![paragraph(text)?], + }, + author, + ), Action::Outline { x, y, text, author } => { - PreparedEdit::insert(&bytes, sid, &Insertion::outline(oid, x, y, &text, &author)?)? + let store = onestore::Store::parse(&bytes)?; + let index = onestore::RevisionIndex::parse(&store)?; + if !onestore::document::Document::parse(&index)? + .pages()? + .contains(&(sid, oid)) + { + return Err("Choose the page to hold the outline".into()); + } + ( + PageOp::Add { + object: PageObject::Outline(Outline { + id: new_id()?, + title: false, + min_width: None, + layout: Layout { + x: Some(x), + y: Some(y), + ..Default::default() + }, + indents: Vec::new(), + paragraphs: vec![paragraph(text)?], + unsupported: Vec::new(), + }), + before: None, + }, + author, + ) } }; + let arena = Arena::default(); + let mut section = Section::open(&arena, bytes)?; + let unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?; + let at = (unix.as_secs() + 11_644_473_600) * 10_000_000 + u64::from(unix.subsec_nanos() / 100); + section.apply( + &author, + &onestore::op::Edit { + at, + ops: vec![Op::Page { space: sid, op }], + }, + )?; + let transaction = section.seal()?; if check { return Ok(json!({"ok": true})); } - Ok(match prepared.commit_file(&args[1]) { + let Some(transaction) = transaction else { + return Ok(json!({"ok": true, "state": "Unchanged"})); + }; + Ok(match transaction.commit_file(&args[1]) { Ok(()) => json!({"ok": true, "state": "Committed"}), Err(error) => json!({"ok": false, "state": format!("{:?}", error.state), "kind": format!("{:?}", error.error.kind()), "error": error.error.to_string()}), diff --git a/crates/notebook/src/lib.rs b/crates/notebook/src/lib.rs index 75c9457dbe646828d4687291cc1c9576df7e6a4c..4456baadbf222a8b4ae28e3c6e6907bf5ec88a94 100644 --- a/crates/notebook/src/lib.rs +++ b/crates/notebook/src/lib.rs @@ -29,7 +29,7 @@ mod schema; pub mod session; pub use recovery::{Recovery, RecoverySummary}; mod sync; -pub use sync::{ConflictKind, EditStatus, Remote, Synced}; +pub use sync::{EditStatus, Remote, Synced}; mod worker; mod working; #[cfg(feature = "smb")] @@ -72,19 +72,8 @@ pub struct PendingEdit { pub edit: Edit, } -/// Unpublished work the remote section no longer accepts: the batch holding edit `id` -/// changed page `space` where the remote changed it too. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Conflict { - pub id: u64, - pub space: ExGuid, - pub kind: ConflictKind, -} - -/// How a conflict or an uncertain attempt ends. `Mine` keeps the local pages: a conflicted -/// page is rewritten from the remote page to the local one; a released attempt publishes -/// again. `Theirs` drops the local edits to the conflicted page, or the whole unpublished -/// branch of a released attempt. +/// How an uncertain attempt ends after review: `Mine` publishes it again, `Theirs` drops +/// the whole unpublished branch. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Resolution { Mine, @@ -161,6 +150,7 @@ impl Replica { // the file as it was. match version { migrate::VERSION => migrate::migrate(&mut connection, path)?, + schema::PREVIOUS => schema::upgrade(&mut connection)?, schema::VERSION => {} _ => { return Err(io::Error::new( @@ -248,9 +238,14 @@ impl Replica { self.ask(|reply| working::Request::Pages { reply }) } + /// The conflict pages of each page that has them (`onestore::Section::conflicts`). + pub fn conflicts(&self) -> Result)>> { + self.ask(|reply| working::Request::Conflicts { reply }) + } + /// The section image the queued edits leave, the unsealed ones sealed as one more - /// revision whose identities differ per call: O(section), for tests and diagnostics. - pub fn snapshot(&self) -> Result> { + /// revision whose identities differ per call: O(section). + pub(crate) fn snapshot(&self) -> Result> { self.ask(|reply| working::Request::Flush { reply })?; working::image(&*self.lock()?) } diff --git a/crates/notebook/src/merge.rs b/crates/notebook/src/merge.rs index c29a016bd5a9f1af16ffb12c22e80e5860801cd5..f2f21fbb893ad8d4e23e8610011bb64d7c09838a 100644 --- a/crates/notebook/src/merge.rs +++ b/crates/notebook/src/merge.rs @@ -2,11 +2,16 @@ //! section when the objects it reads are as the base image had them; text ops on a text //! the remote also changed shift past the remote's changes when they stay clear of them; //! a move, deletion or outline setting the remote already made is dropped as done. -//! Anything else is a conflict: the queue stays on its base until it is resolved. +//! An op on a page the remote changed where it did is dropped, with every later op naming +//! the objects it named, and the page conflicts: as OneNote 2010 does, the remote's version +//! stays the page and the local one becomes a conflict page under it (`conflict_page`). +//! Page-list edits merge as OneNote 2010 merges page series: a page the remote moved keeps +//! the remote's place, and a page the queue placed goes before the next page in the queue's +//! order the remote left in place (`corpus/conflict-page/native-pages`, `native-restore`). //! Objects the queue itself created are not in the base, so nothing remote can have //! changed them. -use crate::{ConflictKind, PendingEdit, Result}; +use crate::{PendingEdit, Result}; use onestore::{ ExGuid, OutlineEdit, PageCreation, PageEdit, PagePosition, Section, document::{Format, Layout, Tag}, @@ -21,143 +26,113 @@ use std::{ ops::Range, }; -/// A conflict being resolved: from edit `first` on, ops on `space` are dropped. Keeping -/// mine replaces the first of them by the ops rewriting the remote page to `target` (or -/// putting `target` back as a page where the remote removed it), or, for the section's -/// page list, keeps what of each section op still applies. -pub(crate) struct Resolving<'p> { - pub first: u64, - pub space: ExGuid, - pub mine: bool, - pub target: Option<&'p Page>, -} +/// The remote changed what an op reads: the op does not replay. +struct Conflicting; -pub(crate) enum Outcome { - /// Every edit applied; these edits' ops changed. An edit whose ops were all dropped - /// stays, empty, to be published and acknowledged with its batch. - Applied(Vec<(u64, Edit)>), - Conflict { - id: u64, - space: ExGuid, - kind: ConflictKind, - }, +/// The queue replayed on the remote section. +pub(crate) struct Merged { + /// Edits whose ops changed. An edit whose ops were all dropped stays, empty, to be + /// published and acknowledged with its batch. + pub rewritten: Vec<(u64, Edit)>, + /// Pages whose local version the remote could not take, with who made the first edit + /// that did not replay and the objects the dropped ops named. + pub conflicts: BTreeMap)>, } /// Replays `edits` on `new`, the remote section; `old` is the base they were made on. +/// Ops on the pages in `converged`, which the remote holds as the edits leave them, are done. pub(crate) fn rebase( old: &mut Section<'_>, new: &mut Section<'_>, edits: &[PendingEdit], converged: &BTreeSet, - resolving: Option>, -) -> Result { - let root = new.root(); +) -> Result { let before: BTreeMap = old.revisions().collect(); let after: BTreeMap = new.revisions().collect(); let orders = (order(old)?, order(new)?); let mut state = Replay { before, after, + local: orders.0.clone(), + unmoved: unmoved(old, new)?, orders, created: BTreeSet::new(), diffs: BTreeMap::new(), }; - let mut rewritten = Vec::new(); - let mut resolved = false; + let mut merged = Merged { + rewritten: Vec::new(), + conflicts: BTreeMap::new(), + }; for queued in edits { - let branch = resolving - .as_ref() - .filter(|resolving| queued.id >= resolving.first); let mut ops = Vec::new(); let mut changed = false; for op in &queued.edit.ops { - let conflict = |kind, space| Outcome::Conflict { - id: queued.id, - space, - kind, + let apply = |state: &mut Replay, new: &mut Section<'_>, op: &Op| { + state.apply(new, &queued.author, queued.edit.at, op) }; - if let Some(resolving) = branch { - let dropped = match op { - Op::Page { space, .. } => *space == resolving.space, - Op::Section(_) => resolving.space == root, - }; - if dropped { - changed = true; - let rewrite = match op { - Op::Page { space, .. } if !resolved && resolving.mine => { - resolving.target.map(|target| (*space, target)) - } - _ => None, - }; - resolved |= matches!(op, Op::Page { .. }); - if let Some((space, target)) = rewrite { - if !state.listed(space) { - // Kept while the remote removed it: the page comes back. - match state.restore( - new, - space, - target, - &queued.author, - queued.edit.at, - )? { - Ok(restored) => ops.extend(restored), - Err(kind) => return Ok(conflict(kind, space)), - } + match op { + Op::Page { space, op: page_op } => { + if converged.contains(space) { + changed = true; + continue; + } + // An op naming what a dropped op named would build on it. + let named = named(page_op); + if let Some((_, dropped)) = merged.conflicts.get_mut(space) + && named.iter().any(|id| dropped.contains(id)) + { + dropped.extend(named); + changed = true; + continue; + } + let replayed = match state.check(old, new, *space, page_op)? { + Ok(None) => { + changed = true; continue; } - let current = new.page(space)?; - for op in onestore::op::lower_page(¤t, target) - .map_err(|error| OpError::Unsupported(error.message))? - { - let op = Op::Page { space, op }; - if let Some(kind) = - state.apply(new, &queued.author, queued.edit.at, &op)? - { - return Ok(conflict(kind, space)); - } - ops.push(op); + Ok(Some(mapped)) => { + let mapped = Op::Page { + space: *space, + op: mapped, + }; + apply(&mut state, new, &mapped)?.then_some(mapped) } - } - if let (Op::Section(op), true) = (op, resolving.mine) { - // Keeping mine for the section list: the first form that applies. - for op in state.salvage(new, op) { - let op = Op::Section(op); - if state - .apply(new, &queued.author, queued.edit.at, &op)? - .is_none() - { - ops.push(op); - break; - } + Err(Conflicting) => None, + }; + match replayed { + Some(mapped) => { + changed |= mapped != *op; + ops.push(mapped); + } + None => { + changed = true; + merged + .conflicts + .entry(*space) + .or_insert_with(|| (queued.author.clone(), BTreeSet::new())) + .1 + .extend(named); } } - continue; } - } - let space = match op { - Op::Page { space, .. } => *space, - Op::Section(_) => root, - }; - if matches!(op, Op::Page { .. }) && converged.contains(&space) { - changed = true; - continue; - } - let mapped = match state.check(old, new, op)? { - Ok(Some(mapped)) => mapped, - Ok(None) => { - changed = true; - continue; + Op::Section(section_op) => { + advance(&mut state.local, section_op); + // The first form of the edit the remote's page list takes, or none. + let mut kept = None; + for form in state.forms(new, section_op)? { + let form = Op::Section(form); + if apply(&mut state, new, &form)? { + kept = Some(form); + break; + } + } + changed |= kept.as_ref() != Some(op); + ops.extend(kept); } - Err(kind) => return Ok(conflict(kind, space)), - }; - changed |= mapped != *op; - if let Some(kind) = state.apply(new, &queued.author, queued.edit.at, &mapped)? { - return Ok(conflict(kind, space)); } - ops.push(mapped); } if changed { - rewritten.push(( + merged.rewritten.push(( queued.id, Edit { at: queued.edit.at, @@ -166,7 +141,176 @@ pub(crate) fn rebase( )); } } - Ok(Outcome::Applied(rewritten)) + Ok(merged) +} + +/// The stored objects a page op names; a conflict page marks those it holds. +fn named(op: &PageOp) -> Vec { + match op { + PageOp::Text { text, .. } + | PageOp::Format { text, .. } + | PageOp::Link { text, .. } + | PageOp::Equation { text, .. } + | PageOp::Split { text, .. } => vec![*text], + PageOp::Date { fields, .. } => fields.iter().map(|(text, _)| *text).collect(), + PageOp::Color(_) => Vec::new(), + PageOp::Join { left, right } => vec![*left, *right], + PageOp::Insert { container, .. } => vec![*container], + PageOp::Move { object, .. } | PageOp::Delete { object } | PageOp::Outline { object, .. } => { + vec![*object] + } + PageOp::Level { paragraph, .. } + | PageOp::Paragraph { paragraph, .. } + | PageOp::Style { paragraph, .. } + | PageOp::List { paragraph, .. } => vec![*paragraph], + PageOp::Tags { target, .. } => vec![*target], + PageOp::Add { object, .. } => vec![object.id()], + PageOp::Picture { picture: object, .. } + | PageOp::Attachment { + attachment: object, .. + } + | PageOp::Strokes { ink: object, .. } => vec![*object], + PageOp::Table { table, edit } => match edit { + TableEdit::Cell { cell, .. } => vec![*cell], + TableEdit::DeleteRow(row) => vec![*row], + _ => vec![*table], + }, + } +} + +/// The edit keeping `page`, the version of page `space` the remote could not take, and +/// applies it to `new`: a conflict page under the remote's page marking `objects` where +/// `page` holds them, or, where the remote removed the page, the page put back as a copy +/// where `local`, the section as the queue leaves it, has it. Content outside the page +/// model stays on the remote's page only; `at` is now. +#[allow(clippy::too_many_arguments)] +pub(crate) fn conflict_page( + new: &mut Section<'_>, + old: &mut Section<'_>, + local: &mut Section<'_>, + space: ExGuid, + page: &Page, + author: &str, + objects: &BTreeSet, + at: u64, +) -> Result { + let mut page = page.clone(); + page.objects.retain(|object| !matches!(object, PageObject::Unsupported(_))); + let listed = order(new)?; + let ops = if listed.iter().any(|(listed, _)| *listed == space) { + let index = Index::of(&page); + let mut marked: Vec = objects + .iter() + .copied() + .filter(|id| index.nodes.contains_key(id)) + .collect(); + let copy = page.copy_with(&mut marked)?; + let titled = page + .objects + .iter() + .any(|object| matches!(object, PageObject::Title(_))); + vec![Op::Section(SectionOp::Conflict { + of: space, + creation: PageCreation::new(None, titled.then_some(page.title.as_str()), author)?, + page: copy, + objects: marked, + })] + } else { + // A copy under fresh identities, as OneNote 2010 puts a removed page back. + let (base, unmoved) = (order(old)?, unmoved(old, new)?); + let queued = order(local)?; + let at = queued.iter().position(|(queued, _)| *queued == space); + let before = at + .map_or(&[][..], |at| &queued[at + 1..]) + .iter() + .map(|(next, _)| *next) + .find(|next| { + listed.iter().any(|(listed, _)| listed == next) + && (unmoved.contains(next) || base.iter().all(|(page, _)| page != next)) + }); + let creation = PageCreation::new(before, Some(&page.title), author)?; + let mut ops = vec![Op::Section(SectionOp::Import { + creation: creation.clone(), + page: page.copy()?, + })]; + if let Some(level) = at.map(|at| queued[at].1).filter(|level| *level > 1) { + ops.push(Op::Section(SectionOp::Pages(vec![PageEdit::set_level( + creation.space(), + level, + )?]))); + } + ops + }; + let edit = Edit { at, ops }; + new.apply(author, &edit)?; + Ok(edit) +} + +/// Pages the base and the remote both list that the remote left in place: in the same +/// series at the same level, in base order among themselves. OneNote 2010 gives a page it +/// moves a series of its own; a page moved within its series is out of base order. +fn unmoved(old: &mut Section<'_>, new: &mut Section<'_>) -> Result> { + let series = (old.series()?, new.series()?); + let base: BTreeMap = order(old)? + .into_iter() + .enumerate() + .map(|(at, (space, level))| (space, (at, level))) + .collect(); + let kept: Vec<(ExGuid, usize)> = order(new)? + .into_iter() + .filter_map(|(space, level)| { + let (at, before) = base.get(&space)?; + (*before == level && series.0.get(&space) == series.1.get(&space)).then_some((space, *at)) + }) + .collect(); + // A longest subsequence of `kept` in base order (patience sorting). + let mut tails: Vec = Vec::new(); + let mut previous = vec![None; kept.len()]; + for (i, (_, at)) in kept.iter().enumerate() { + let k = tails.partition_point(|tail| kept[*tail].1 < *at); + previous[i] = k.checked_sub(1).map(|k| tails[k]); + if k == tails.len() { + tails.push(i); + } else { + tails[k] = i; + } + } + let mut unmoved = BTreeSet::new(); + let mut next = tails.last().copied(); + while let Some(i) = next { + unmoved.insert(kept[i].0); + next = previous[i]; + } + Ok(unmoved) +} + +/// Applies a queued page-list edit to `local`, the page order as the queue leaves it. +fn advance(local: &mut Order, op: &SectionOp) { + let insert = |local: &mut Order, page: (ExGuid, u32), before: Option| { + let at = before + .and_then(|before| local.iter().position(|(space, _)| *space == before)) + .unwrap_or(local.len()); + local.insert(at, page); + }; + match op { + SectionOp::Create(creation) | SectionOp::Import { creation, .. } => { + insert(local, (creation.space(), 1), creation.before()); + } + SectionOp::Pages(edits) => { + for edit in edits { + let Some(at) = local.iter().position(|(space, _)| *space == edit.space()) else { + continue; + }; + local[at].1 = edit.level(); + if let PagePosition::Before(before) = edit.position() { + let page = local.remove(at); + insert(local, page, before); + } + } + } + SectionOp::Delete(spaces) => local.retain(|(space, _)| !spaces.contains(space)), + SectionOp::Conflict { .. } | SectionOp::Color(_) => {} + } } fn order(section: &mut Section<'_>) -> Result { @@ -186,6 +330,10 @@ struct Replay { after: BTreeMap, /// Page order of the base and the remote. orders: (Order, Order), + /// Page order as the queue leaves it, up to the op replaying. + local: Order, + /// Pages the remote left in place (`unmoved`). + unmoved: BTreeSet, /// Page spaces the replayed edits created. created: BTreeSet, /// Per page the remote changed, what it changed; `None` when it left the page alone. @@ -193,14 +341,8 @@ struct Replay { } impl Replay { - /// Applies one op to the remote section; `Some` names why it did not apply. - fn apply( - &mut self, - new: &mut Section<'_>, - author: &str, - at: u64, - op: &Op, - ) -> Result> { + /// Applies one op to the remote section; false when the section refused it. + fn apply(&mut self, new: &mut Section<'_>, author: &str, at: u64, op: &Op) -> Result { match new.apply( author, &Edit { @@ -210,64 +352,49 @@ impl Replay { ) { Ok(()) => { if let Op::Section( - SectionOp::Create(creation) | SectionOp::Import { creation, .. }, + SectionOp::Create(creation) + | SectionOp::Import { creation, .. } + | SectionOp::Conflict { creation, .. }, ) = op { self.created.insert(creation.space()); } - Ok(None) + Ok(true) } Err(OpError::Failed(error)) => Err(error.into()), - Err(error) => Ok(Some(match op { - // A page list the remote reordered refuses the op, however it says so. - Op::Section(_) => ConflictKind::StructureChanged, - Op::Page { .. } if matches!(error, OpError::Unsupported(_)) => { - ConflictKind::UnsupportedEdit - } - Op::Page { space, .. } if !self.listed(*space) => ConflictKind::TargetUnavailable, - Op::Page { .. } => ConflictKind::ContentChanged, - })), + Err(_) => Ok(false), } } - /// The op as it replays on the remote section, or why it cannot. + /// The page op as it replays on the remote section; `None` when the remote made it. fn check( &mut self, old: &mut Section<'_>, new: &mut Section<'_>, - op: &Op, - ) -> Result, ConflictKind>> { - match op { - Op::Page { space, op } => { - if !self.listed(*space) { - return Ok(Err(ConflictKind::TargetUnavailable)); - } - let diff = match self.diffs.get_mut(space) { - Some(diff) => diff, - None => { - let diff = match (self.before.get(space), self.after.get(space)) { - (Some(before), Some(after)) if before != after => Some(Diff { - old: Index::of(&old.page(*space)?), - new: Index::of(&new.page(*space)?), - regions: BTreeMap::new(), - }), - _ => None, - }; - self.diffs.entry(*space).or_insert(diff) - } + space: ExGuid, + op: &PageOp, + ) -> Result, Conflicting>> { + if !self.listed(space) { + return Ok(Err(Conflicting)); + } + let diff = match self.diffs.get_mut(&space) { + Some(diff) => diff, + None => { + let diff = match (self.before.get(&space), self.after.get(&space)) { + (Some(before), Some(after)) if before != after => Some(Diff { + old: Index::of(&old.page(space)?), + new: Index::of(&new.page(space)?), + regions: BTreeMap::new(), + }), + _ => None, }; - Ok(match diff { - None => Ok(Some(Op::Page { - space: *space, - op: op.clone(), - })), - Some(diff) => diff - .check(op) - .map(|op| op.map(|op| Op::Page { space: *space, op })), - }) + self.diffs.entry(space).or_insert(diff) } - Op::Section(section) => Ok(self.section(section).map(|()| Some(op.clone()))), - } + }; + Ok(match diff { + None => Ok(Some(op.clone())), + Some(diff) => diff.check(op), + }) } /// Whether the remote section lists the page, or the replayed edits created it; a page @@ -276,151 +403,82 @@ impl Replay { self.created.contains(&space) || self.orders.1.iter().any(|(page, _)| *page == space) } - /// Puts `page`, which the remote removed, back into the section and returns the ops - /// that did: a copy under fresh identities at its level, before the first page after it - /// that the remote still has and that can take a page before it, or last. - fn restore( - &mut self, - new: &mut Section<'_>, - space: ExGuid, - page: &Page, - author: &str, - at: u64, - ) -> Result, ConflictKind>> { - let old = self.orders.0.clone(); - let position = old.iter().position(|(listed, _)| *listed == space); - let anchors = position - .map_or(&[][..], |position| &old[position + 1..]) - .iter() - .map(|(next, _)| Some(*next)) - .filter(|next| next.is_some_and(|next| self.listed(next))) - .chain([None]) - .collect::>(); - let copy = page.copy()?; - let mut refusal = ConflictKind::StructureChanged; - for before in anchors { - let creation = PageCreation::new(before, Some(&page.title), author)?; - let import = Op::Section(SectionOp::Import { - creation: creation.clone(), - page: copy.clone(), - }); - match self.apply(new, author, at, &import)? { - Some(kind) => refusal = kind, - None => { - let mut ops = vec![import]; - if let Some(level) = position.map(|at| old[at].1).filter(|level| *level > 1) { - let indent = Op::Section(SectionOp::Pages(vec![PageEdit::set_level( - creation.space(), - level, - )?])); - if let Some(kind) = self.apply(new, author, at, &indent)? { - return Ok(Err(kind)); - } - ops.push(indent); - } - return Ok(Ok(ops)); - } - } - } - Ok(Err(refusal)) + /// Whether the remote moved or re-leveled a page the base listed. + fn placed(&self, page: ExGuid) -> bool { + !self.unmoved.contains(&page) && self.orders.0.iter().any(|(listed, _)| *listed == page) + } + + /// Where a page the queue put before `before` goes on the remote: before the first page + /// from `before` on, in the queue's order, that the remote lists where the base had it, + /// or last, as OneNote 2010 places a page series only one side changed. + fn anchor(&self, listed: &Order, page: ExGuid, before: Option) -> Option { + let from = self.local.iter().position(|(space, _)| Some(*space) == before)?; + self.local[from..].iter().map(|(space, _)| *space).find(|space| { + *space != page && !self.placed(*space) && listed.iter().any(|(listed, _)| listed == space) + }) } - fn section(&self, op: &SectionOp) -> std::result::Result<(), ConflictKind> { - let (old, new) = &self.orders; - let find = - |list: &[(ExGuid, u32)], space: ExGuid| list.iter().position(|(s, _)| *s == space); - match op { - SectionOp::Create(_) | SectionOp::Import { .. } => Ok(()), - SectionOp::Delete(pages) => { - for page in pages { - if find(new, *page).is_none() && !self.created.contains(page) { - return Err(ConflictKind::TargetUnavailable); - } - if self.before.get(page) != self.after.get(page) { - return Err(ConflictKind::ContentChanged); - } + /// Forms of a section op to try on the remote section, in order: a page placed by + /// `anchor`, the page edits of pages the remote still has and did not move, removals of + /// pages the remote still has (a page it changed is not removed), a conflict page's + /// content as a page of its own where the remote removed its page. + fn forms(&self, new: &mut Section<'_>, op: &SectionOp) -> Result> { + let conflicts: BTreeSet = new + .conflicts()? + .into_iter() + .flat_map(|(_, pages)| pages.into_iter().map(|page| page.space)) + .collect(); + let listed = order(new)?; + let placed = |creation: &PageCreation| { + creation.reposition(self.anchor(&listed, creation.space(), creation.before())) + }; + Ok(match op { + SectionOp::Create(creation) => vec![SectionOp::Create(placed(creation)?)], + SectionOp::Import { creation, page } => vec![SectionOp::Import { + creation: placed(creation)?, + page: page.clone(), + }], + SectionOp::Color(_) => vec![op.clone()], + SectionOp::Conflict { + of, creation, page, .. + } => vec![if self.listed(*of) { + op.clone() + } else { + SectionOp::Import { + creation: creation.clone(), + page: page.clone(), } - Ok(()) - } + }], + // A page the remote moved or re-leveled keeps the remote's placement. SectionOp::Pages(edits) => { - // Where the batch puts each page, from the base order. - let mut wanted: Vec = old.iter().map(|(space, _)| *space).collect(); + let mut kept = Vec::new(); for edit in edits { - if let PagePosition::Before(before) = edit.position() - && let Some(at) = wanted.iter().position(|space| *space == edit.space()) + if self.placed(edit.space()) + || listed.iter().all(|(listed, _)| *listed != edit.space()) { - let space = wanted.remove(at); - let at = before - .and_then(|before| wanted.iter().position(|space| *space == before)) - .unwrap_or(wanted.len()); - wanted.insert(at, space); - } - } - let wanted: Vec<(ExGuid, u32)> = - wanted.into_iter().map(|space| (space, 0)).collect(); - for edit in edits { - let space = edit.space(); - let Some(at) = find(old, space) else { continue; - }; - let Some(now) = find(new, space) else { - return Err(ConflictKind::TargetUnavailable); - }; - if old[at].1 != new[now].1 && new[now].1 != edit.level() { - return Err(ConflictKind::StructureChanged); - } - if edit.position() != PagePosition::Keep { - // Each page the remote also has must keep its side of the moved page, - // or already be on the side the batch wants. - let side = |list: &[(ExGuid, u32)], peer: ExGuid| { - Some(find(list, peer)? < find(list, space)?) - }; - let conflicting = old.iter().any(|(peer, _)| { - *peer != space - && find(new, *peer).is_some() - && side(new, *peer) != side(old, *peer) - && side(new, *peer) != side(&wanted, *peer) - }); - if conflicting { - return Err(ConflictKind::StructureChanged); - } } + kept.push(match edit.position() { + PagePosition::Keep => edit.clone(), + PagePosition::Before(before) => edit.reposition( + PagePosition::Before(self.anchor(&listed, edit.space(), before)), + edit.level(), + )?, + }); } - Ok(()) - } - } - } - - /// Forms of a section op to try on the remote section list, in order: as it is, a - /// creation appended instead, moves and removals of the pages still there. - fn salvage(&self, new: &mut Section<'_>, op: &SectionOp) -> Vec { - let pages: BTreeSet = new - .pages() - .map(|pages| pages.into_iter().map(|(space, ..)| space).collect()) - .unwrap_or_default(); - let present = |space: &ExGuid| pages.contains(space) || self.created.contains(space); - match op { - SectionOp::Create(creation) => std::iter::once(creation.clone()) - .chain(creation.reposition(None).ok()) - .map(SectionOp::Create) - .collect(), - SectionOp::Import { .. } => vec![op.clone()], - SectionOp::Pages(edits) => { - let edits: Vec = edits - .iter() - .filter(|edit| present(&edit.space())) - .cloned() - .collect(); - if edits.is_empty() { + if kept.is_empty() { Vec::new() } else { - vec![SectionOp::Pages(edits)] + vec![SectionOp::Pages(kept)] } } SectionOp::Delete(deleted) => { let deleted: Vec = deleted .iter() - .filter(|space| present(space)) + .filter(|space| { + self.listed(**space) && self.before.get(space) == self.after.get(space) + || conflicts.contains(space) + }) .copied() .collect(); if deleted.is_empty() { @@ -429,7 +487,7 @@ impl Replay { vec![SectionOp::Delete(deleted)] } } - } + }) } } @@ -491,7 +549,7 @@ impl Diff { /// Where the remote changed a text's characters and formats; its tags and date field /// are not positions an op names. - fn region(&mut self, text: ExGuid) -> std::result::Result, ConflictKind> { + fn region(&mut self, text: ExGuid) -> std::result::Result, Conflicting> { if !self.regions.contains_key(&text) { if self.ours(text) { return Ok(None); @@ -500,7 +558,7 @@ impl Diff { self.old.nodes.get(&text).map(|node| &node.own), self.new.nodes.get(&text).map(|node| &node.own), ) else { - return Err(ConflictKind::ContentChanged); + return Err(Conflicting); }; if a.text == b.text { return Ok(None); @@ -512,21 +570,20 @@ impl Diff { } /// The op as it replays on the remote page; `None` when the remote already made it. - fn check(&mut self, op: &PageOp) -> std::result::Result, ConflictKind> { - use ConflictKind::ContentChanged as Changed; - let require = |ok: bool| if ok { Ok(()) } else { Err(Changed) }; + fn check(&mut self, op: &PageOp) -> std::result::Result, Conflicting> { + let require = |ok: bool| if ok { Ok(()) } else { Err(Conflicting) }; let mut op = op.clone(); match &mut op { PageOp::Text { text, range, with } => { if let Some(region) = self.region(*text)? { - let mapped = region.map(range).ok_or(Changed)?; + let mapped = region.map(range).ok_or(Conflicting)?; region.replaced(range, with.encode_utf16().count() as u32); *range = mapped; } } PageOp::Format { text, range, .. } => { if let Some(region) = self.region(*text)? { - *range = region.map(range).ok_or(Changed)?; + *range = region.map(range).ok_or(Conflicting)?; } } PageOp::Link { text, .. } | PageOp::Equation { text, .. } => { @@ -547,7 +604,7 @@ impl Diff { if let Some(region) = self.region(*text)? { // The cut moves with the character after it: text the remote typed at // the cut stays left of it. - let cut = region.map(&(*at..*at + 1)).ok_or(Changed)?; + let cut = region.map(&(*at..*at + 1)).ok_or(Conflicting)?; let right_changes = region.split(*at); if !right_changes.0.is_empty() { self.regions.insert(*right, right_changes); @@ -565,7 +622,8 @@ impl Diff { self.region(text)?; } } - PageOp::Insert { .. } | PageOp::Add { .. } => {} + // A page's colour is the latest one set, whichever side set it. + PageOp::Insert { .. } | PageOp::Add { .. } | PageOp::Color(_) => {} PageOp::Move { object, parent, @@ -633,7 +691,7 @@ impl Diff { } _ => false, }; - return if done { Ok(None) } else { Err(Changed) }; + return if done { Ok(None) } else { Err(Conflicting) }; } require(before.is_some())? } diff --git a/crates/notebook/src/migrate.rs b/crates/notebook/src/migrate.rs index 86fd430ad49763b4647863ad309e110a0d511387..6b139418ac872ac7f2b224a24230a8a13ac09fc3 100644 --- a/crates/notebook/src/migrate.rs +++ b/crates/notebook/src/migrate.rs @@ -1,7 +1,8 @@ //! Converts a schema-14 cache, whose queue holds whole-page edits over a working image, //! to this schema's op queue. The cache is exported first; each queued page is lowered to //! ops against the page the conversion has so far, and every converted page must equal -//! the page in the old working image, or nothing changes. +//! the page in the old working image, or nothing changes. A page edit schema 14 held in +//! conflict becomes the conflict page a merge makes now, the remote's page staying. use crate::{Result, base, queue, schema}; use onestore::{ @@ -87,7 +88,6 @@ struct Converted { edit: Edit, /// Schema 14's publication evidence, when this edit was attempted. attempted: Option, - conflict: Option<(i64, ExGuid)>, } pub(crate) fn migrate(connection: &mut Connection, path: &Path) -> Result<()> { @@ -107,6 +107,53 @@ pub(crate) fn migrate(connection: &mut Connection, path: &Path) -> Result<()> { Ok(()) } +/// The conflict page keeping `local`, the version of page `space` its merge with `remote` +/// did not take, marking the texts `remote` does not hold as they are. +fn conflict(space: ExGuid, remote: &Page, local: &Page, author: &str) -> Result { + let texts = |page: &Page| -> BTreeMap { + let mut texts = BTreeMap::new(); + let mut pending: Vec<&onestore::page::PageParagraph> = Vec::new(); + for object in &page.objects { + match object { + onestore::page::PageObject::Outline(outline) => pending.extend(&outline.paragraphs), + onestore::page::PageObject::Title(title) => { + pending.extend(title.outlines.iter().flat_map(|outline| &outline.paragraphs)) + } + _ => {} + } + } + while let Some(paragraph) = pending.pop() { + match ¶graph.content { + onestore::page::ParagraphContent::Text(text) => { + texts.insert(text.id, format!("{:?}", text.text)); + } + onestore::page::ParagraphContent::Table(table) => pending.extend( + table.rows.iter().flat_map(|row| &row.cells).flat_map(|cell| &cell.paragraphs), + ), + _ => {} + } + } + texts + }; + let kept = texts(remote); + let mut objects: Vec = texts(local) + .into_iter() + .filter(|(id, text)| kept.get(id) != Some(text)) + .map(|(id, _)| id) + .collect(); + let page = local.copy_with(&mut objects)?; + let titled = local + .objects + .iter() + .any(|object| matches!(object, onestore::page::PageObject::Title(_))); + Ok(Op::Section(SectionOp::Conflict { + of: space, + creation: PageCreation::new(None, titled.then_some(local.title.as_str()), author)?, + page, + objects, + })) +} + fn convert(transaction: &rusqlite::Transaction<'_>) -> Result<()> { let (base, patch): (Vec, Vec) = transaction.query_row("SELECT base, working FROM replica WHERE id=1", [], |row| { @@ -118,9 +165,9 @@ fn convert(transaction: &rusqlite::Transaction<'_>) -> Result<()> { Ok((row.get(0)?, row.get(1)?)) }) .optional()?; - let conflicts: BTreeMap = transaction - .prepare("SELECT edit_id, kind FROM conflicts")? - .query_map([], |row| Ok((row.get(0)?, row.get(1)?)))? + let conflicts: std::collections::BTreeSet = transaction + .prepare("SELECT edit_id FROM conflicts")? + .query_map([], |row| row.get(0))? .collect::>()?; let queued: Vec<(i64, String, String)> = transaction .prepare("SELECT id, space, operation FROM edits ORDER BY id")? @@ -146,6 +193,11 @@ fn convert(transaction: &rusqlite::Transaction<'_>) -> Result<()> { let operation: Operation = serde_json::from_str(&operation) .map_err(|error| invalid(format!("Queued edit {id} is unreadable: {error}")))?; let (author, ops) = match operation { + Operation::Page(intent) if conflicts.contains(&id) => { + let current = section.page(space)?; + let op = conflict(space, ¤t, &intent.after, &intent.author)?; + (intent.author, vec![op]) + } Operation::Page(intent) => { let current = section.page(space)?; let ops = onestore::op::lower_page(¤t, &intent.after)?; @@ -185,17 +237,12 @@ fn convert(transaction: &rusqlite::Transaction<'_>) -> Result<()> { } sealed = Some(section.seal()?); } - // A conflicting page-list edit is the section's conflict, whichever page it names. - let conflicted = match edit.ops.first() { - Some(Op::Section(_)) => section.root(), - _ => space, - }; + // A schema-14 conflict is queued work: the next rebase keeps both versions. converted.push(Converted { id, author, edit, attempted, - conflict: conflicts.get(&id).map(|kind| (*kind, conflicted)), }); } let listed = |image: &[u8]| -> Result> { @@ -258,12 +305,6 @@ fn convert(transaction: &rusqlite::Transaction<'_>) -> Result<()> { } else { batch = Some(current); } - if let Some((kind, space)) = edit.conflict { - transaction.execute( - "UPDATE batches SET conflict=?1, space=?2 WHERE id=?3", - params![kind, space.to_string(), current], - )?; - } queue::insert( transaction, Some(crate::unsigned(edit.id)?), diff --git a/crates/notebook/src/queue.rs b/crates/notebook/src/queue.rs index 66bf8399eca6aa71097a5da93af31e98521d8d66..c4c9c89b5528b285f766a886c53a50780d3b996b 100644 --- a/crates/notebook/src/queue.rs +++ b/crates/notebook/src/queue.rs @@ -74,7 +74,7 @@ fn slots(edit: &mut Edit, mut visit: impl FnMut(&mut Payload)) { } => cells(list, &mut visit), _ => {} }, - Op::Section(SectionOp::Import { page, .. }) => { + Op::Section(SectionOp::Import { page, .. } | SectionOp::Conflict { page, .. }) => { for added in &mut page.objects { object(added, &mut visit); } @@ -235,8 +235,12 @@ pub(crate) fn spaces(edit: &Edit, root: onestore::ExGuid) -> Vec { spaces.push(creation.space()); } + SectionOp::Conflict { of, creation, .. } => { + spaces.extend([*of, creation.space()]); + } SectionOp::Pages(edits) => spaces.extend(edits.iter().map(|edit| edit.space())), SectionOp::Delete(pages) => spaces.extend(pages), + SectionOp::Color(_) => {} } } } diff --git a/crates/notebook/src/recovery.rs b/crates/notebook/src/recovery.rs index 1999ad76d2bfd6f289551327e43762fcf59c86cd..dbb9a08142bc220b206d48227810a44d2b36bf01 100644 --- a/crates/notebook/src/recovery.rs +++ b/crates/notebook/src/recovery.rs @@ -8,7 +8,6 @@ const RECOVERY_ID: u32 = 0x4f4e4552; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)] pub struct RecoverySummary { pub queued_edits: u64, - pub conflicts: u64, pub uncertain_edits: u64, pub published_receipts: u64, /// Bytes of the image the queued edits apply to. @@ -38,7 +37,8 @@ impl Recovery { io::Error::new(io::ErrorKind::InvalidData, "Not a recovery archive").into(), ); } - if version != schema::VERSION { + // A schema-15 archive differs only in columns nothing reads. + if ![schema::PREVIOUS, schema::VERSION].contains(&version) { return Err(io::Error::new( io::ErrorKind::InvalidData, format!( @@ -166,18 +166,16 @@ fn summary(connection: &Connection) -> Result { let length = |image| -> Result { Ok(base::stamp(connection, image)?.map_or(0, |stamp| stamp.length)) }; - let (queued_edits, conflicts, uncertain_edits, published_receipts): (i64, i64, i64, i64) = + let (queued_edits, uncertain_edits, published_receipts): (i64, i64, i64) = connection.query_row( "SELECT (SELECT count(*) FROM edits), - (SELECT count(*) FROM edits JOIN batches ON batches.id=edits.batch WHERE conflict IS NOT NULL), (SELECT count(*) FROM edits JOIN batches ON batches.id=edits.batch WHERE attempted=1), (SELECT count(*) FROM receipts)", [], - |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)), + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), )?; Ok(RecoverySummary { queued_edits: unsigned(queued_edits)?, - conflicts: unsigned(conflicts)?, uncertain_edits: unsigned(uncertain_edits)?, published_receipts: unsigned(published_receipts)?, base_bytes: length(base::Image::Base)?, diff --git a/crates/notebook/src/schema.rs b/crates/notebook/src/schema.rs index cb9a5628939f7337a71909d976fac544e94dae94..7dd045367f88e1540d262fca1571008c21eebce3 100644 --- a/crates/notebook/src/schema.rs +++ b/crates/notebook/src/schema.rs @@ -1,7 +1,9 @@ use super::*; -use rusqlite::Transaction; +use rusqlite::{OptionalExtension, Transaction}; -pub(crate) const VERSION: u32 = 15; +pub(crate) const VERSION: u32 = 16; +/// The schema whose batches still recorded review conflicts. +pub(crate) const PREVIOUS: u32 = 15; /// Tables a schema-14 cache shares with this one. pub(crate) const KEPT: &str = " @@ -28,11 +30,8 @@ pub(crate) const QUEUE: &str = " sealed TEXT, revisions TEXT, attempted INTEGER NOT NULL DEFAULT 0 CHECK(attempted IN (0,1)), - conflict INTEGER CHECK(conflict BETWEEN 0 AND 3), - space TEXT, CHECK((sealed IS NULL) = (revisions IS NULL)), - CHECK(attempted=0 OR sealed IS NOT NULL), - CHECK((conflict IS NULL) = (space IS NULL)) + CHECK(attempted=0 OR sealed IS NOT NULL) ) STRICT; CREATE TABLE edits ( id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(id>0), @@ -51,3 +50,57 @@ pub(crate) fn create(transaction: &Transaction<'_>) -> Result<()> { transaction.execute_batch(KEPT)?; Ok(()) } + +/// Converts a schema-15 cache: its batches lose the review conflict and its page, which +/// a merge no longer records. A conflict it held is queued work the next rebase keeps as +/// conflict pages. Foreign keys stay off while `edits` briefly references the old table. +pub(crate) fn upgrade(connection: &mut Connection) -> Result<()> { + connection.execute_batch("PRAGMA foreign_keys=OFF")?; + let upgraded = (|| -> Result<()> { + let transaction = connection.transaction_with_behavior(TransactionBehavior::Exclusive)?; + let sequence: Option = transaction + .query_row( + "SELECT seq FROM sqlite_sequence WHERE name='batches'", + [], + |row| row.get(0), + ) + .optional()?; + transaction.execute_batch( + "CREATE TABLE upgraded ( + id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(id>0), + sealed TEXT, + revisions TEXT, + attempted INTEGER NOT NULL DEFAULT 0 CHECK(attempted IN (0,1)), + CHECK((sealed IS NULL) = (revisions IS NULL)), + CHECK(attempted=0 OR sealed IS NOT NULL) + ) STRICT; + INSERT INTO upgraded(id, sealed, revisions, attempted) + SELECT id, sealed, revisions, attempted FROM batches; + DROP TABLE batches; + ALTER TABLE upgraded RENAME TO batches;", + )?; + if let Some(sequence) = sequence { + transaction.execute( + "UPDATE sqlite_sequence SET seq=max(seq, ?1) WHERE name='batches'", + [sequence], + )?; + } + let dangling: bool = transaction.query_row( + "SELECT EXISTS(SELECT 1 FROM pragma_foreign_key_check)", + [], + |row| row.get(0), + )?; + if dangling { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "The schema-15 cache holds edits without their batch", + ) + .into()); + } + transaction.pragma_update(None, "user_version", VERSION)?; + transaction.commit()?; + Ok(()) + })(); + connection.execute_batch("PRAGMA foreign_keys=ON")?; + upgraded +} diff --git a/crates/notebook/src/session.rs b/crates/notebook/src/session.rs index 3ae33747104978bd0a241b0c381789626d3ebb3f..2e78dc28de2e40d27939f19b774898dd0e1041c9 100644 --- a/crates/notebook/src/session.rs +++ b/crates/notebook/src/session.rs @@ -2,11 +2,11 @@ //! publishes their edits to the section file in the background. use crate::{ - Conflict, ConflictKind, EditStatus, Error, PendingEdit, Remote, Replica, Resolution, Result, + EditStatus, Error, PendingEdit, Remote, Replica, Resolution, Result, SyncWorker, discover, }; use onestore::{ - CommitError, ExGuid, PageCreation, PreparedEdit, RevisionIndex, Stamp, Store, Transaction, + CommitError, ExGuid, PageCreation, RevisionIndex, Stamp, Store, Transaction, document::Document, op::{Edit, Op, SectionOp}, page::Page, @@ -18,7 +18,6 @@ use std::{ path::{Path, PathBuf}, sync::{ Arc, Mutex, - atomic::{AtomicUsize, Ordering}, mpsc::{self, Receiver, Sender}, }, time::Duration, @@ -81,25 +80,19 @@ pub trait Storage: Send + Sync { fn create_directory(&self, path: &str) -> Result<()>; /// Renames or moves a file or directory; an existing target is an error. fn rename(&self, from: &str, to: &str) -> Result<()>; + /// Deletes a file or an empty directory. + fn delete(&self, path: &str) -> Result<()>; /// Names a section or TOC file for its notebook, as `onestore::place`. fn place(&self, path: &str, ancestor: [u8; 16], name: &str) -> Result<()>; - fn commit(&self, path: &str, edit: &PreparedEdit<'_>) -> Result<()>; - fn set_property( - &self, - path: &str, - source: &[u8], - space: ExGuid, - object: ExGuid, - property: u32, - value: &[u8], - ) -> Result<()>; + /// Publishes a transaction made on the file's current image. + fn commit(&self, path: &str, transaction: &Transaction) -> Result<()>; } /// A password-protected section as `Notebook::unlock` read it. #[cfg(feature = "protected")] pub struct Unlocked { pub pages: Vec<(ExGuid, Page)>, - /// The stored section the pages came from, which a save must still find in place. + /// The stored section the pages came from, which an edit must still find in place. snapshot: Vec, } @@ -145,31 +138,21 @@ impl Storage for Directory { Ok(std::fs::rename(self.path(from), self.path(to))?) } + fn delete(&self, path: &str) -> Result<()> { + let path = self.path(path); + Ok(if path.is_dir() { + std::fs::remove_dir(path)? + } else { + std::fs::remove_file(path)? + }) + } + fn place(&self, path: &str, ancestor: [u8; 16], name: &str) -> Result<()> { Ok(onestore::place_file(self.path(path), ancestor, name)?) } - fn commit(&self, path: &str, edit: &PreparedEdit<'_>) -> Result<()> { - Ok(edit.commit_file(self.path(path))?) - } - - fn set_property( - &self, - path: &str, - source: &[u8], - space: ExGuid, - object: ExGuid, - property: u32, - value: &[u8], - ) -> Result<()> { - Ok(onestore::commit_file_property( - self.path(path), - source, - space, - object, - property, - value, - )?) + fn commit(&self, path: &str, transaction: &Transaction) -> Result<()> { + Ok(transaction.commit_file(self.path(path))?) } } @@ -225,33 +208,18 @@ impl Storage for Share { Ok(self.client.rename(&self.path(from), &self.path(to))?) } + fn delete(&self, path: &str) -> Result<()> { + Ok(self.client.delete(&self.path(path))?) + } + fn place(&self, path: &str, ancestor: [u8; 16], name: &str) -> Result<()> { Ok(self.client.place(&self.path(path), ancestor, name)?) } - fn commit(&self, path: &str, edit: &PreparedEdit<'_>) -> Result<()> { + fn commit(&self, path: &str, transaction: &Transaction) -> Result<()> { Ok(self .client - .commit_transaction(&self.path(path), &edit.transaction())?) - } - - fn set_property( - &self, - path: &str, - source: &[u8], - space: ExGuid, - object: ExGuid, - property: u32, - value: &[u8], - ) -> Result<()> { - Ok(self.client.commit_property_bytes( - &self.path(path), - source, - space, - object, - property, - value, - )?) + .commit_transaction(&self.path(path), transaction)?) } } @@ -279,6 +247,22 @@ impl Notebook { Self::with(Box::new(Directory(root.clone())), Some(root), cache) } + /// Creates a notebook in the new folder `root`, as OneNote 2010 creates one: a table of + /// contents in the notebook colour `color` (COLORREF) and "New Section 1" holding the + /// page `page` creates. + pub fn create( + root: impl AsRef, + cache: impl AsRef, + color: u32, + page: &PageCreation, + ) -> Result { + std::fs::create_dir(root.as_ref())?; + let mut notebook = Self::open(root, cache)?; + notebook.edit_toc("", &[onestore::TocEdit::Color(color)])?; + notebook.create_section("", "New Section 1", page)?; + Ok(notebook) + } + /// Opens the notebook at `root` on the share `client` is connected to. Sections open /// through `Section::resume_smb` with the catalog's paths. #[cfg(feature = "smb")] @@ -393,18 +377,38 @@ impl Notebook { fn edit_toc(&self, folder: &str, edits: &[onestore::TocEdit]) -> Result<()> { let (toc, _) = self.toc(folder)?; - let source = self.storage.read(&toc)?; - self.storage - .commit(&toc, &PreparedEdit::table_of_contents(&source, edits)?) + self.commit_toc(&toc, edits) } - /// Creates `name.one` in `folder` with one empty page and lists it last in the folder's - /// TOC, as OneNote creates a section. Returns the new catalog path. - pub fn create_section(&mut self, folder: &str, name: &str, author: &str) -> Result { + fn commit_toc(&self, toc: &str, edits: &[onestore::TocEdit]) -> Result<()> { + let source = self.storage.read(toc)?; + match onestore::edit_table_of_contents(&source, edits)? { + Some(transaction) => self.storage.commit(toc, &transaction), + None => Ok(()), + } + } + + /// Creates `name.one` in `folder` holding the page `page` creates, in the next of + /// OneNote's section colours, and lists it last in the folder's TOC, as OneNote creates + /// a section. Returns the new catalog path. + pub fn create_section(&mut self, folder: &str, name: &str, page: &PageCreation) -> Result { let filename = format!("{name}.one"); - self.folder(folder)?; + let color = next_color(self.folder(folder)?); let (_, ancestor) = self.toc(folder)?; - let bytes = onestore::create_section(&filename, "", author)?; + let mut bytes = onestore::create_empty_section(&filename, Some(color))?; + let transaction = { + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, bytes.clone())?; + let edit = Edit { + at: crate::now(), + ops: vec![Op::Section(SectionOp::Create(page.clone()))], + }; + section.apply(page.author(), &edit)?; + section.seal()? + }; + if let Some(transaction) = transaction { + transaction.apply(&mut bytes)?; + } let path = catalog_path(folder, &filename); self.storage.create(&path, &bytes)?; self.storage.place(&path, ancestor, &filename)?; @@ -483,23 +487,16 @@ impl Notebook { /// Sets a section's colour (COLORREF) in its own metadata, where OneNote keeps it. pub fn set_section_color(&mut self, path: &str, color: Option) -> Result<()> { let path = self.section_path(path)?.path.clone(); - let source = self.storage.read(&path)?; - let store = onestore::Store::parse(&source)?; - let index = onestore::RevisionIndex::parse(&store)?; - let document = Document::parse(&index)?; - let revision = document.active(document.root)?; - let metadata = *revision - .roots - .get(&2) - .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?; - self.storage.set_property( - &path, - &source, - document.root, - metadata, - 0x14001cbe, - &color.unwrap_or(0xffff_ffff).to_le_bytes(), - )?; + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, self.storage.read(&path)?)?; + let edit = Edit { + at: crate::now(), + ops: vec![Op::Section(SectionOp::Color(color))], + }; + section.apply("", &edit)?; + if let Some(transaction) = section.seal()? { + self.storage.commit(&path, &transaction)?; + } self.refresh()?; Ok(()) } @@ -518,15 +515,51 @@ impl Notebook { self.refresh().map(drop) } - /// Deletes a section the way OneNote does: the file moves into the notebook's - /// `OneNote_RecycleBin` folder, a section group with its own TOC that lists it (and that - /// the root TOC lists), and its own folder's TOC entry goes. + /// Deletes a section or section group the way OneNote 2010 does. A section's file moves + /// into the notebook's `OneNote_RecycleBin` folder, a section group with its own TOC that + /// lists it (and that the root TOC lists). A group's sections, in its groups too, move + /// there the same way, then its folders go. Either way its folder's TOC entry goes. pub fn delete(&mut self, path: &str) -> Result<()> { let (folder, entry) = split(path); let (filename, identity) = self.entry(folder, entry)?; - if !filename.to_ascii_lowercase().ends_with(".one") { + if path == RECYCLE_BIN { return Err(io::Error::from(io::ErrorKind::InvalidInput).into()); } + if filename.to_ascii_lowercase().ends_with(".one") { + self.bin_section(path, &filename, identity)?; + } else { + // Sections first, then each folder once it is empty, deepest first. + let mut sections = Vec::new(); + let mut folders = Vec::new(); + let mut pending = vec![self.folder(path)?]; + while let Some(group) = pending.pop() { + sections.extend( + group + .sections + .iter() + .map(|section| (section.path.clone(), section.file_id)), + ); + folders.push((group.path.clone(), group.toc.as_ref().map(|toc| toc.filename.clone()))); + pending.extend(&group.groups); + } + for (section, identity) in sections { + let (_, name) = split(§ion); + self.bin_section(§ion, name, identity)?; + } + for (group, toc) in folders.into_iter().rev() { + if let Some(toc) = toc { + self.storage.delete(&catalog_path(&group, &toc))?; + } + self.storage.delete(&group)?; + } + } + self.edit_toc(folder, &[onestore::TocEdit::Remove { identity }])?; + self.refresh().map(drop) + } + + /// The recycle bin's TOC path and file identity, creating the bin as OneNote does: a + /// section group the root TOC lists. + fn bin(&self) -> Result<(String, [u8; 16])> { let bin_toc = catalog_path(RECYCLE_BIN, TOC); if !self.storage.exists(&bin_toc) { if !self.storage.exists(RECYCLE_BIN) { @@ -545,37 +578,116 @@ impl Notebook { }], )?; } - let mut target = filename.clone(); + let source = self.storage.read(&bin_toc)?; + Ok((bin_toc, onestore::Store::parse(&source)?.header.file_id)) + } + + /// Moves the section file at `path` into the recycle bin, under a name no binned + /// section has, and lists it there; its own folder's TOC is the caller's. + fn bin_section(&self, path: &str, filename: &str, identity: [u8; 16]) -> Result<()> { + let (bin_toc, bin_identity) = self.bin()?; + let mut target = filename.to_owned(); let mut attempt = 1; while self.storage.exists(&catalog_path(RECYCLE_BIN, &target)) { attempt += 1; - let (stem, extension) = filename.rsplit_once('.').unwrap_or((&filename, "")); + let (stem, extension) = filename.rsplit_once('.').unwrap_or((filename, "")); target = format!("{stem} ({attempt}).{extension}"); } let binned = catalog_path(RECYCLE_BIN, &target); - self.storage - .rename(&catalog_path(folder, &filename), &binned)?; - let source = self.storage.read(&bin_toc)?; - self.storage.place( - &binned, - onestore::Store::parse(&source)?.header.file_id, - &target, - )?; - self.storage.commit( + self.storage.rename(path, &binned)?; + self.storage.place(&binned, bin_identity, &target)?; + self.commit_toc( &bin_toc, - &PreparedEdit::table_of_contents( - &source, + &[onestore::TocEdit::Add { + filename: target, + identity, + group: false, + }], + ) + } + + /// Keeps copies of `pages` in the notebook's recycle bin, as OneNote 2010 does with a + /// page it deletes: in `OneNote_RecycleBin/OneNote_DeletedPages.one`, each at the top + /// level with its identity, title, date and creation time. The caller then deletes the + /// pages from their section. + pub fn recycle_pages(&mut self, pages: &[Page], author: &str) -> Result<()> { + const DELETED: &str = "OneNote_DeletedPages.one"; + let (bin_toc, bin_identity) = self.bin()?; + let path = catalog_path(RECYCLE_BIN, DELETED); + if !self.storage.exists(&path) { + // OneNote's "Deleted Pages" section is grey. + let bytes = onestore::create_empty_section(DELETED, Some(0x00e1e1e1))?; + self.storage.create(&path, &bytes)?; + self.storage.place(&path, bin_identity, DELETED)?; + self.commit_toc( + &bin_toc, &[onestore::TocEdit::Add { - filename: target, - identity, + filename: DELETED.into(), + identity: onestore::Store::parse(&bytes)?.header.file_id, group: false, }], - )?, - )?; - self.edit_toc(folder, &[onestore::TocEdit::Remove { identity }])?; + )?; + } + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, self.storage.read(&path)?)?; + let mut ops = Vec::new(); + for page in pages { + let mut creation = PageCreation::new(None, Some(&page.title), author)?; + if let (Some(identity), Some(created)) = (page.identity, page.created) { + creation = creation.keeping(identity, created)?; + } + if let Some([date, time]) = page.date_text() { + creation = creation.dated(&date, &time)?; + } + ops.push(Op::Section(SectionOp::Import { + creation, + page: page.copy()?, + })); + } + section.apply(author, &Edit { at: crate::now(), ops })?; + if let Some(transaction) = section.seal()? { + self.storage.commit(&path, &transaction)?; + } self.refresh().map(drop) } + /// Moves a section or section group into the folder at catalog path `folder`, last, as + /// OneNote moves one dragged onto a group: the file or folder moves and each TOC's + /// entry follows it. Returns its new catalog path. + pub fn move_entry(&mut self, path: &str, folder: &str) -> Result { + let (from, entry) = split(path); + let (filename, identity) = self.entry(from, entry)?; + self.folder(folder)?; + let group = !filename.to_ascii_lowercase().ends_with(".one"); + let target = catalog_path(folder, &filename); + if from == folder + || path == RECYCLE_BIN + || group && (folder == path || folder.starts_with(&format!("{path}/"))) + || self.storage.exists(&target) + { + return Err(io::Error::from(io::ErrorKind::InvalidInput).into()); + } + let (_, ancestor) = self.toc(folder)?; + self.storage.rename(path, &target)?; + let placed = if group { + catalog_path(&target, TOC) + } else { + target.clone() + }; + self.storage.place(&placed, ancestor, &filename)?; + self.edit_toc( + folder, + &[onestore::TocEdit::Add { + filename, + identity, + group, + }], + )?; + self.edit_toc(from, &[onestore::TocEdit::Remove { identity }])?; + self.refresh()?; + Ok(target) + } + /// The stored filename and TOC identity of a folder's section or group. fn entry(&self, folder: &str, name: &str) -> Result<(String, [u8; 16])> { let parent = self.folder(folder)?; @@ -660,30 +772,31 @@ impl Notebook { Ok(Unlocked { pages, snapshot }) } - /// Saves an edited page of `unlocked` under the section's key, straight to the file: - /// nothing of a protected section is cached or queued. A section written since - /// `unlocked` was read fails the save; unlock again for its pages. + /// Applies `edit` to pages of `unlocked` and stores it under the section's key, straight + /// to the file: nothing of a protected section is cached or queued. A section written + /// since `unlocked` was read refuses the edit; unlock again for its pages. #[cfg(feature = "protected")] - pub fn save_unlocked( + pub fn apply_unlocked( &self, path: &str, password: &str, unlocked: &mut Unlocked, - space: ExGuid, - page: &Page, author: &str, + edit: &Edit, ) -> Result<()> { let path = self.section_path(path)?.path.clone(); - let edit = onestore::PreparedEdit::page_protected( - &unlocked.snapshot, - password, - space, - page, - author, - )?; - self.storage.commit(&path, &edit)?; - let written = edit.as_bytes().to_vec(); - unlocked.snapshot = written; + let transaction = { + let store = Store::parse(&unlocked.snapshot)?; + let index = RevisionIndex::parse(&store)?; + onestore::protected::UnlockedSection::open( + &index, + password, + onestore::protected::Limits::default(), + )? + .apply(author, edit)? + }; + self.storage.commit(&path, &transaction)?; + transaction.apply(&mut unlocked.snapshot)?; Ok(()) } @@ -706,6 +819,27 @@ impl Notebook { } } +/// The section colours OneNote 2010 gives new sections, COLORREF, in the order it gives +/// them (`evidence/notebook-management/new-notebook`). +const SECTION_COLORS: [u32; 3] = [0x00e4a88a, 0x0078b0f6, 0x00bba4d5]; + +/// The colour OneNote gives a new section in `folder`: the first of its colours none of +/// the folder's sections has, or the next in turn when all are taken. +fn next_color(folder: &discover::Folder) -> u32 { + let taken: Vec = folder + .sections + .iter() + .filter_map(|section| match section.state { + discover::SectionState::Readable { color, .. } => color, + _ => None, + }) + .collect(); + SECTION_COLORS + .into_iter() + .find(|color| !taken.contains(color)) + .unwrap_or(SECTION_COLORS[folder.sections.len() % SECTION_COLORS.len()]) +} + fn component(name: &str) -> bool { !name.is_empty() && !name.contains(['/', '\\', '\0']) && name != "." && name != ".." } @@ -733,8 +867,6 @@ pub enum Event { /// The section refused an edit `apply` handed it; the pages it names are as they were /// before it, so an editor showing them should reload them. Rejected { spaces: Vec, error: String }, - /// Old session API, until the application reloads on `Changed`: pages changed remotely. - Refreshed, /// A publication attempt finished with this durable state. Attempt { id: u64, status: EditStatus }, /// The section file could not be reached; the replica keeps its state. @@ -743,25 +875,6 @@ pub enum Event { Failed(String), } -/// The outcome of saving an edited page (old session API). -#[derive(Debug, PartialEq, Eq)] -pub enum Save { - /// The model equals the stored page. - Unchanged, - /// The edit is durable locally under this id. - Queued(u64), - /// The stored page no longer matches `before`; reload it before saving again. - Stale, -} - -/// A queued edit and its durable state. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct QueuedEdit { - pub id: u64, - pub space: ExGuid, - pub status: EditStatus, -} - type Notify = Arc; /// A section file with its replica and background publication. `Send + Sync`: edits @@ -773,10 +886,6 @@ pub struct Section { events: Mutex>, sender: Sender, notify: Notify, - saved: Mutex)>>, - saves: Sender<(ExGuid, std::result::Result)>, - /// Saves queued and not yet reported. - unsaved: Arc, } impl Section { @@ -850,7 +959,6 @@ impl Section { ) -> Result { let replica = Arc::new(replica); let (sender, events) = mpsc::channel(); - let (saves, saved) = mpsc::channel(); let notify = Mutex::new(notify); let notify: Notify = Arc::new(move || { if let Ok(notify) = notify.lock() { @@ -865,7 +973,6 @@ impl Section { Ok(synced) => { if !synced.changed.is_empty() { events.push(Event::Changed(synced.changed.clone())); - events.push(Event::Refreshed); } if let Some((id, status)) = &synced.edit { events.push(Event::Attempt { @@ -899,9 +1006,6 @@ impl Section { events: Mutex::new(events), sender, notify, - saved: Mutex::new(saved), - saves, - unsaved: Arc::new(AtomicUsize::new(0)), }) } @@ -971,8 +1075,8 @@ impl Section { Ok(space) } - /// Removes pages permanently, queued like the user's own edits (a move across - /// sections is `import_page` there, then this here). + /// Removes pages or conflict pages permanently, queued like the user's own edits (a + /// move across sections is `import_page` there, then this here). pub fn delete_pages(&self, pages: &[ExGuid]) -> Result { self.replica.apply( "", @@ -991,23 +1095,10 @@ impl Section { self.replica.pending() } - /// The unpublished batch the remote no longer accepts, if any. Show `remote_page` - /// against `page` for its space, then `resolve`. - pub fn conflict(&self) -> Result> { - self.replica.conflict() - } - - /// The page as last observed in the section file, for reviewing a conflict. - pub fn remote_page(&self, space: ExGuid) -> Result { - let snapshot = self.replica.remote_snapshot()?; - let arena = onestore::Arena::default(); - Ok(onestore::Section::open(&arena, snapshot)?.page(space)?) - } - - /// Ends a conflict: `Mine` publishes the local page over the remote change, `Theirs` - /// drops the local edits to the conflicted page (and its later ones). - pub fn resolve(&self, id: u64, resolution: Resolution) -> Result<()> { - self.replica.resolve(id, resolution) + /// The conflict pages of each page that has them, as the local edits leave them + /// (`onestore::Section::conflicts`); `page` reads one, `delete_pages` removes it. + pub fn conflicts(&self) -> Result)>> { + self.replica.conflicts() } /// Retires an uncertain attempt after review, exporting the queue to `archive` first: @@ -1056,114 +1147,6 @@ impl Section { None => Ok(()), } } - - // Old whole-page session API, kept until the application emits ops. - - /// Saves an edited page. `before` is the model the edit started from; a stored page - /// that differs from it means the section changed underneath the editor. - pub fn save(&self, space: ExGuid, before: &Page, after: &Page, author: &str) -> Result { - let outcome = self.replica.ask(|reply| crate::working::Request::Save { - space, - before: before.clone(), - after: after.clone(), - author: author.to_owned(), - reply, - })?; - Ok(outcome.unwrap_or(Save::Unchanged)) - } - - /// `save` on the section thread, returning at once; `saved` reports the outcome. - /// Saves queue in order, and one whose `before` is the previous save's `after` - /// continues it: pass each save's `after` as the next one's `before`. - pub fn queue_save(&self, space: ExGuid, before: Page, after: Page, author: &str) -> Result<()> { - let (saves, notify, unsaved) = ( - self.saves.clone(), - Arc::clone(&self.notify), - Arc::clone(&self.unsaved), - ); - self.unsaved.fetch_add(1, Ordering::Relaxed); - self.replica.send(crate::working::Request::Save { - space, - before, - after, - author: author.to_owned(), - reply: Box::new(move |outcome| { - unsaved.fetch_sub(1, Ordering::Release); - let outcome = match outcome { - Ok(None) => return, - Ok(Some(save)) => Ok(save), - Err(error) => Err(error.to_string()), - }; - if saves.send((space, outcome)).is_ok() { - notify(); - } - }), - }) - } - - /// Whether a queued save has yet to report, so the stored page may trail the editor's. - pub fn saving(&self) -> bool { - self.unsaved.load(Ordering::Acquire) > 0 - } - - /// Outcomes of queued saves since the last poll, oldest first; `notify` runs for each. - /// Consecutive saves continuing one another finish as one, reported once. - pub fn saved(&self) -> Vec<(ExGuid, std::result::Result)> { - self.saved - .lock() - .map(|saved| saved.try_iter().collect()) - .unwrap_or_default() - } - - /// Every queued edit with its state: pending, awaiting confirmation of a retained - /// attempt, or a conflict awaiting review. `space` is the first page it edits. - pub fn queue(&self) -> Result> { - self.replica - .pending()? - .into_iter() - .map(|edit| { - Ok(QueuedEdit { - id: edit.id, - space: edit - .edit - .ops - .iter() - .find_map(|op| match op { - Op::Page { space, .. } => Some(*space), - Op::Section(_) => None, - }) - .unwrap_or(self.replica.root), - status: self.replica.status(edit.id)?.unwrap_or(EditStatus::Pending), - }) - }) - .collect() - } - - /// The conflict as a queue entry, for the review dialog. - pub fn conflicts(&self) -> Result> { - Ok(self - .replica - .conflict()? - .map(|conflict| { - ( - QueuedEdit { - id: conflict.id, - space: conflict.space, - status: EditStatus::Conflict(conflict.kind), - }, - conflict.kind, - ) - }) - .into_iter() - .collect()) - } - - /// Resolves the conflict holding `id` with a page reviewed against `remote_page`: the - /// remote page is rewritten to `after`. - pub fn review(&self, id: u64, after: &Page) -> Result<()> { - self.replica - .resolve_with(id, Resolution::Mine, Some(after.clone())) - } } /// The section file itself as the publication target, under OneNote-compatible exclusion. @@ -1176,21 +1159,21 @@ impl Remote for FileRemote { /// Read without the whole-file lock, which would block OneNote's readers: a change /// detector, never a snapshot to edit. - fn stamp(&mut self) -> io::Result> { + fn stamp(&mut self) -> io::Result { use std::io::Read; let mut file = std::fs::File::open(&self.0)?; let mut header = [0; 1024]; file.read_exact(&mut header)?; let length = file.metadata()?.len(); - Ok(Some(Stamp { header, length })) + Ok(Stamp { header, length }) } fn publish(&mut self, transaction: &Transaction) -> std::result::Result<(), CommitError> { transaction.commit_file(&self.0) } - fn confirm(&mut self, snapshot: &[u8]) -> std::result::Result<(), CommitError> { - onestore::confirm_file_snapshot(&self.0, snapshot) + fn confirm(&mut self, base: &Stamp) -> std::result::Result<(), CommitError> { + onestore::confirm_file(&self.0, base) } } diff --git a/crates/notebook/src/smb/directory.rs b/crates/notebook/src/smb/directory.rs index d947b6d92e1baebad2130624677e690ef8a76847..1649cc9e07b18c39b8a8c2d0899c51d5e90b4a5b 100644 --- a/crates/notebook/src/smb/directory.rs +++ b/crates/notebook/src/smb/directory.rs @@ -35,11 +35,7 @@ impl Client { create_contexts: Vec::new(), }, )?; - let file = File { - client: self, - id: Some(response.file_id), - length: response.end_of_file, - }; + let file = File::new(self, &response); let mut entries = BTreeMap::new(); loop { let response: io::Result = diff --git a/crates/notebook/src/smb/mod.rs b/crates/notebook/src/smb/mod.rs index 6b10a7fcc89c548290c3cfa4826a38bccdf57e38..051352e80331ccf33c6ac38f4967b4937fc6f773 100644 --- a/crates/notebook/src/smb/mod.rs +++ b/crates/notebook/src/smb/mod.rs @@ -1,9 +1,9 @@ //! Blocking SMB access to OneNote sections, table-of-contents files and payloads. -use onestore::{CommitError, CommitIo, CommitState, ExGuid}; +use onestore::{CommitError, CommitIo, CommitState}; use smb2::{ Session, Tree, - client::connection::{Connection, NegotiatedParams}, + client::connection::{CompoundOp, Connection, Frame, NegotiatedParams}, msg::{ close::{CloseRequest, CloseResponse}, create::{ @@ -22,7 +22,7 @@ use smb2::{ flags::{Capabilities, FileAccessMask}, }, }; -use std::{io, ops::Range, sync::Mutex, time::Duration}; +use std::{io, sync::Mutex, time::Duration}; use tokio::runtime::{Handle, Runtime}; mod directory; @@ -149,6 +149,54 @@ impl Client { .map_err(|_| io::Error::from(io::ErrorKind::TimedOut)) .and_then(|result| result.map_err(io::Error::other)) .inspect_err(|_| self.retire())?; + self.unpack(&self.body(command, frame)?) + } + + /// Sends `requests` as one related compound request, which the server performs in order, + /// and returns each response body or status error. A request after a CREATE names the + /// file it opened as `FileId::SENTINEL`; each request fits one credit. + fn compound(&self, requests: &[(Command, &dyn Pack)]) -> io::Result>>> { + if Handle::try_current().is_ok() { + return Err(io::ErrorKind::InvalidInput.into()); + } + let connection = self + .connection + .lock() + .map_err(|_| io::ErrorKind::Other)? + .clone() + .ok_or(io::ErrorKind::NotConnected)?; + let operations: Vec<_> = requests + .iter() + .map(|(command, body)| CompoundOp::new(*command, *body, Some(self.tree.tree_id))) + .collect(); + let frames = { + let runtime = self.runtime.lock().map_err(|_| io::ErrorKind::Other)?; + runtime + .as_ref() + .ok_or(io::ErrorKind::NotConnected)? + .block_on(async { + tokio::time::timeout(self.timeout, connection.execute_compound(&operations)) + .await + }) + }; + let frames = frames + .map_err(|_| io::Error::from(io::ErrorKind::TimedOut)) + .and_then(|result| result.map_err(io::Error::other)) + .inspect_err(|_| self.retire())?; + Ok(frames + .into_iter() + .zip(requests) + .map(|(frame, (command, _))| { + let frame = frame + .map_err(io::Error::other) + .inspect_err(|_| self.retire())?; + self.body(*command, frame) + }) + .collect()) + } + + /// The body of a response to `command`, or its status as an error. + fn body(&self, command: Command, frame: Frame) -> io::Result> { if frame.header.command != command { self.retire(); return Err(io::ErrorKind::InvalidData.into()); @@ -170,12 +218,17 @@ impl Client { }, )); } - T::unpack(&mut ReadCursor::new(&frame.body)).map_err(|error| { + Ok(frame.body) + } + + fn unpack(&self, body: &[u8]) -> io::Result { + T::unpack(&mut ReadCursor::new(body)).map_err(|error| { self.retire(); io::Error::new(io::ErrorKind::InvalidData, error) }) } + /// Opens as OneNote 2010 does: readers share everything, a writer denies other writers. fn open(&self, path: &str, write: bool) -> io::Result> { self.open_shared(path, write, if write { 5 } else { 7 }) } @@ -186,7 +239,7 @@ impl Client { if write { 0xc0000000 } else { 0x80000000 }, sharing, CreateDisposition::FileOpen, - 0x42, + 0x40, ) } @@ -198,28 +251,11 @@ impl Client { disposition: CreateDisposition, options: u32, ) -> io::Result> { - if path.is_empty() || path.contains('\0') || path.encode_utf16().count() > 32767 { - return Err(io::ErrorKind::InvalidInput.into()); - } let response: CreateResponse = self.request( Command::Create, - CreateRequest { - requested_oplock_level: OplockLevel::None, - impersonation_level: ImpersonationLevel::Impersonation, - desired_access: FileAccessMask::new(access), - file_attributes: 0, - share_access: ShareAccess(sharing), - create_disposition: disposition, - create_options: options, - name: smb2::encode_path(&path.replace('\\', "/")), - create_contexts: Vec::new(), - }, + create_request(path, access, sharing, disposition, options)?, )?; - Ok(File { - client: self, - id: Some(response.file_id), - length: response.end_of_file, - }) + Ok(File::new(self, &response)) } /// Creates a file holding `bytes`; an existing file is an error. @@ -286,7 +322,7 @@ impl Client { /// Names a section or TOC file for its notebook (`onestore::place`) under native /// writer coordination. pub fn place(&self, path: &str, ancestor: [u8; 16], name: &str) -> Result<(), CommitError> { - self.commit(path, |file| { + self.commit(path, &[(0, 1024)], |file| { onestore::place(file, ancestor, name).map_err(|error| CommitError { state: CommitState::NotCommitted, error, @@ -338,7 +374,9 @@ impl Client { path: &str, snapshot: impl FnOnce(&mut File<'_>) -> io::Result>>, ) -> io::Result> { - let mut file = self.open(path, false)?.coordinate(path, false)?; + let mut file = self + .open(path, false)? + .coordinate(path, false, &[(0, 1024)])?; let result = snapshot(&mut file) .and_then(|snapshot| snapshot.ok_or_else(|| io::ErrorKind::WouldBlock.into())); let closed = file.close(); @@ -347,79 +385,67 @@ impl Client { Ok(snapshot) } - pub fn commit_text( - &self, - path: &str, - source: &[u8], - space: ExGuid, - object: ExGuid, - range: Range, - replacement: &str, - ) -> Result<(), CommitError> { - self.commit(path, |file| { - onestore::commit_text(file, source, space, object, range, replacement) - }) - } - - pub fn commit_property_bytes( - &self, - path: &str, - source: &[u8], - space: ExGuid, - object: ExGuid, - property: u32, - value: &[u8], - ) -> Result<(), CommitError> { - self.commit(path, |file| { - onestore::commit_property_bytes(file, source, space, object, property, value) - }) - } - /// Publishes a transaction using the same native writer coordination as text commits. pub fn commit_transaction( &self, path: &str, transaction: &onestore::Transaction, ) -> Result<(), CommitError> { - self.commit(path, |file| transaction.commit(file)) + self.commit(path, &checked(transaction.base()), |file| { + transaction.commit(file) + }) } - /// The header and length of a revision store, read without writer coordination or path - /// identity checks: a change detector for polling, never a snapshot to edit. + /// The header and length of a revision store, in one round trip, without writer + /// coordination or path identity checks: a change detector for polling, never a snapshot + /// to edit. pub fn stamp(&self, path: &str) -> io::Result { - let mut file = self.open(path, false)?; - let mut header = [0; 1024]; - let mut read = 0; - let result = loop { - match file.read_at(read as u64, &mut header[read..]) { - Ok(0) => break Err(io::ErrorKind::UnexpectedEof.into()), - Ok(count) => read += count, - Err(error) => break Err(error), - } - if read == header.len() { - break Ok(()); - } + let create = create_request(path, 0x80000000, 7, CreateDisposition::FileOpen, 0x40)?; + let read = read_request(FileId::SENTINEL, 0, 1024); + let close = CloseRequest { + file_id: FileId::SENTINEL, + flags: 0, }; - let length = file.length; - let closed = file.close(); - result?; + let [created, read, closed]: [_; 3] = self + .compound(&[ + (Command::Create, &create), + (Command::Read, &read), + (Command::Close, &close), + ])? + .try_into() + .map_err(|_| io::ErrorKind::InvalidData)?; + let created: CreateResponse = self.unpack(&created?)?; + if closed.is_err() { + self.retire(); + } + let read: ReadResponse = self.unpack(&read?)?; closed?; - Ok(onestore::Stamp { header, length }) + let header = read + .data + .try_into() + .map_err(|_| io::ErrorKind::UnexpectedEof)?; + Ok(onestore::Stamp { + header, + length: created.end_of_file, + }) } - /// Confirms an observed snapshot's durability under native writer coordination. - pub fn confirm_snapshot(&self, path: &str, source: &[u8]) -> Result<(), CommitError> { - self.commit(path, |file| onestore::confirm_snapshot(file, source)) + /// Confirms that the file still has `base`'s stamp and is durable, then refreshes its + /// version, under native writer coordination. + pub fn confirm(&self, path: &str, base: &onestore::Stamp) -> Result<(), CommitError> { + self.commit(path, &checked(base), |file| onestore::confirm(file, base)) } + /// Opens `path` for writing under OneNote's coordination, making `reads` with the locks. fn commit( &self, path: &str, + reads: &[(u64, usize)], operation: impl FnOnce(&mut File<'_>) -> Result<(), CommitError>, ) -> Result<(), CommitError> { let mut file = self .open(path, true) - .and_then(|file| file.coordinate(path, true)) + .and_then(|file| file.coordinate(path, true, reads)) .map_err(|error| CommitError { state: CommitState::NotCommitted, error, @@ -443,24 +469,103 @@ impl Drop for Client { struct File<'a> { client: &'a Client, id: Option, - /// The end of file when opened. - length: u64, + /// Writes `flush` sends ahead of its flush, in order. + writes: Vec<(u64, Vec)>, + /// Reads made under the coordination locks, each returned once to the same `read_at`. + prefetched: Vec<(u64, usize, Vec)>, } -impl File<'_> { - fn coordinate(self, path: &str, write: bool) -> io::Result { - self.lock(0xfffffffb, 0x11)?; +impl<'a> File<'a> { + fn new(client: &'a Client, created: &CreateResponse) -> Self { + Self { + client, + id: Some(created.file_id), + writes: Vec::new(), + prefetched: Vec::new(), + } + } + + /// Takes OneNote 2010's coordination locks in one request (the shared reader byte, and + /// to write the exclusive writer byte), makes `reads` under them, then checks that the + /// handle is still the file at `path`: maintenance replaces a file while holding its + /// locks. + fn coordinate(mut self, path: &str, write: bool, reads: &[(u64, usize)]) -> io::Result { + let id = self.id.unwrap(); + let mut locks = vec![LockElement { + offset: 0xfffffffb, + length: 1, + flags: 0x11, + }]; if write { - self.lock(0xfffffffd, 0x12)?; + locks.push(LockElement { + offset: 0xfffffffd, + length: 1, + flags: 0x12, + }); } - let current = self.client.open(path, false)?; - let same = self.identity()? == current.identity()?; - current.close()?; - if !same { + let lock = LockRequest { + file_id: id, + lock_sequence: 0, + locks, + }; + let [index, volume] = identity_requests(id); + let reading: Vec<_> = reads + .iter() + .map(|(offset, length)| read_request(id, *offset, *length)) + .collect(); + let mut requests: Vec<(Command, &dyn Pack)> = vec![ + (Command::Lock, &lock), + (Command::QueryInfo, &index), + (Command::QueryInfo, &volume), + ]; + requests.extend( + reading + .iter() + .map(|read| (Command::Read, read as &dyn Pack)), + ); + let mut responses = self.client.compound(&requests)?.into_iter(); + let mut next = || responses.next().ok_or(io::ErrorKind::InvalidData); + let _: LockResponse = self.client.unpack(&next()??)?; + let own = self.client.identity(next()?, next()?)?; + for (offset, length) in reads { + let data = match next()? { + Err(error) if error.kind() == io::ErrorKind::UnexpectedEof => Vec::new(), + body => self.client.unpack::(&body?)?.data, + }; + if data.len() > *length { + self.client.retire(); + return Err(io::ErrorKind::InvalidData.into()); + } + self.prefetched.push((*offset, *length, data)); + } + let create = create_request(path, 0x80, 7, CreateDisposition::FileOpen, 0)?; + let [index, volume] = identity_requests(FileId::SENTINEL); + let close = CloseRequest { + file_id: FileId::SENTINEL, + flags: 0, + }; + let [created, index, volume, closed]: [_; 4] = self + .client + .compound(&[ + (Command::Create, &create), + (Command::QueryInfo, &index), + (Command::QueryInfo, &volume), + (Command::Close, &close), + ])? + .try_into() + .map_err(|_| io::ErrorKind::InvalidData)?; + created?; + if closed.is_err() { + self.client.retire(); + } + let current = self.client.identity(index, volume)?; + closed?; + if own != current { return Err(io::ErrorKind::ResourceBusy.into()); } Ok(self) } + #[cfg(test)] fn lock(&self, offset: u64, flags: u32) -> io::Result<()> { let _: LockResponse = self.client.request( Command::Lock, @@ -477,19 +582,37 @@ impl File<'_> { Ok(()) } - fn identity(&self) -> io::Result<(u64, u32)> { - let index: QueryInfoResponse = self.client.request( - Command::QueryInfo, - QueryInfoRequest { - info_type: InfoType::File, - file_info_class: 6, - output_buffer_length: 8, - additional_information: 0, - flags: 0, - file_id: self.id.unwrap(), - input_buffer: Vec::new(), - }, - )?; + fn close(mut self) -> io::Result<()> { + self.release() + } + + fn release(&mut self) -> io::Result<()> { + if let Some(file_id) = self.id.take() { + let result: io::Result = self + .client + .request(Command::Close, CloseRequest { file_id, flags: 0 }); + if result.is_err() { + self.client.retire(); + } + result?; + } + Ok(()) + } +} +impl Drop for File<'_> { + fn drop(&mut self) { + let _ = self.release(); + } +} + +impl Client { + /// A file's volume serial number and index, from the responses to `identity_requests`. + fn identity( + &self, + index: io::Result>, + volume: io::Result>, + ) -> io::Result<(u64, u32)> { + let index: QueryInfoResponse = self.unpack(&index?)?; let index = u64::from_le_bytes( index .output_buffer @@ -499,47 +622,69 @@ impl File<'_> { if index == 0 { return Err(io::ErrorKind::Unsupported.into()); } - let volume: QueryInfoResponse = self.client.request( - Command::QueryInfo, - QueryInfoRequest { - info_type: InfoType::Filesystem, - file_info_class: 1, - output_buffer_length: 1024, - additional_information: 0, - flags: 0, - file_id: self.id.unwrap(), - input_buffer: Vec::new(), - }, - )?; + let volume: QueryInfoResponse = self.unpack(&volume?)?; let serial = volume .output_buffer .get(8..12) .ok_or(io::ErrorKind::InvalidData)?; Ok((index, u32::from_le_bytes(serial.try_into().unwrap()))) } +} - fn close(mut self) -> io::Result<()> { - self.release() - } +/// FileInternalInformation and FileFsVolumeInformation queries. +fn identity_requests(file_id: FileId) -> [QueryInfoRequest; 2] { + let query = |info_type, file_info_class, output_buffer_length| QueryInfoRequest { + info_type, + file_info_class, + output_buffer_length, + additional_information: 0, + flags: 0, + file_id, + input_buffer: Vec::new(), + }; + [ + query(InfoType::File, 6, 8), + query(InfoType::Filesystem, 1, 1024), + ] +} - fn release(&mut self) -> io::Result<()> { - if let Some(file_id) = self.id.take() { - let result: io::Result = self - .client - .request(Command::Close, CloseRequest { file_id, flags: 0 }); - if result.is_err() { - self.client.retire(); - } - result?; - } - Ok(()) +fn create_request( + path: &str, + access: u32, + sharing: u32, + disposition: CreateDisposition, + options: u32, +) -> io::Result { + if path.is_empty() || path.contains('\0') || path.encode_utf16().count() > 32767 { + return Err(io::ErrorKind::InvalidInput.into()); } + Ok(CreateRequest { + requested_oplock_level: OplockLevel::None, + impersonation_level: ImpersonationLevel::Impersonation, + desired_access: FileAccessMask::new(access), + file_attributes: 0, + share_access: ShareAccess(sharing), + create_disposition: disposition, + create_options: options, + name: smb2::encode_path(&path.replace('\\', "/")), + create_contexts: Vec::new(), + }) } -impl Drop for File<'_> { - fn drop(&mut self) { - let _ = self.release(); + +fn read_request(file_id: FileId, offset: u64, length: usize) -> ReadRequest { + ReadRequest { + file_id, + offset, + length: length as u32, + minimum_count: 1, + flags: 0, + padding: 0, + channel: 0, + remaining_bytes: 0, + read_channel_info: Vec::new(), } } + fn read_size(params: &NegotiatedParams, credits: u16, requested: usize) -> usize { let budget = if params.dialect != Dialect::Smb2_0_2 && params.capabilities.contains(Capabilities::LARGE_MTU) @@ -559,6 +704,15 @@ impl CommitIo for File<'_> { if output.is_empty() { return Ok(0); } + if let Some(at) = self + .prefetched + .iter() + .position(|(start, length, _)| *start == offset && *length == output.len()) + { + let (_, _, data) = self.prefetched.swap_remove(at); + output[..data.len()].copy_from_slice(&data); + return Ok(data.len()); + } let mut size = 0; let response: io::Result = self.client.request_with(Command::Read, |connection| { @@ -595,40 +749,71 @@ impl CommitIo for File<'_> { output[..response.data.len()].copy_from_slice(&response.data); Ok(response.data.len()) } + /// Queues at most one request's worth of `bytes`; `flush` sends it. fn write_at(&mut self, offset: u64, bytes: &[u8]) -> io::Result { - if bytes.is_empty() { - return Ok(0); - } let size = bytes.len().min(65536); - let response: WriteResponse = self.client.request( - Command::Write, - WriteRequest { - file_id: self.id.unwrap(), + if size > 0 { + self.writes.push((offset, bytes[..size].to_vec())); + } + Ok(size) + } + + /// Sends the queued writes and then the flush in related compound requests of at most + /// sixteen, awaiting each: the server performs a compound in order, and answers the flush + /// once every write before it is durable. + fn flush(&mut self) -> io::Result<()> { + let file_id = self.id.unwrap(); + let writes: Vec<_> = self + .writes + .drain(..) + .map(|(offset, data)| WriteRequest { + file_id, offset, - data: bytes[..size].to_vec(), + data, data_offset: 112, - flags: 1, + flags: 0, channel: 0, remaining_bytes: 0, write_channel_info_offset: 0, write_channel_info_length: 0, - }, - )?; - if response.count as usize > size { - return Err(io::ErrorKind::InvalidData.into()); + }) + .collect(); + let flush = FlushRequest { file_id }; + let mut requests: Vec<(Command, &dyn Pack)> = writes + .iter() + .map(|write| (Command::Write, write as &dyn Pack)) + .collect(); + requests.push((Command::Flush, &flush)); + let mut sizes = writes.iter().map(|write| write.data.len()); + for compound in requests.chunks(16) { + for (response, (command, _)) in + self.client.compound(compound)?.into_iter().zip(compound) + { + let body = response?; + if *command == Command::Flush { + let _: FlushResponse = self.client.unpack(&body)?; + continue; + } + let written: WriteResponse = self.client.unpack(&body)?; + let size = sizes.next().ok_or(io::ErrorKind::InvalidData)?; + if written.count as usize != size { + return Err(if (written.count as usize) < size { + io::ErrorKind::WriteZero + } else { + io::ErrorKind::InvalidData + } + .into()); + } + } } - Ok(response.count as usize) - } - fn flush(&mut self) -> io::Result<()> { - let _: FlushResponse = self.client.request( - Command::Flush, - FlushRequest { - file_id: self.id.unwrap(), - }, - )?; Ok(()) } } +/// The reads `Stamp::check` makes: the header and a probe of the last byte. +fn checked(stamp: &onestore::Stamp) -> [(u64, usize); 2] { + [(0, 1024), (stamp.length.saturating_sub(1), 2)] +} + #[cfg(test)] mod tests; diff --git a/crates/notebook/src/smb/remote.rs b/crates/notebook/src/smb/remote.rs index 16fd2ba06ab25ceb5a2e0c08fb4329eced4fceb4..b1178eb2115de1f5478754686127724f74713299 100644 --- a/crates/notebook/src/smb/remote.rs +++ b/crates/notebook/src/smb/remote.rs @@ -26,15 +26,15 @@ impl Remote for SmbRemote { self.client.read(&self.path, self.limit) } - fn stamp(&mut self) -> io::Result> { - self.client.stamp(&self.path).map(Some) + fn stamp(&mut self) -> io::Result { + self.client.stamp(&self.path) } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.client.commit_transaction(&self.path, transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.client.confirm_snapshot(&self.path, snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.client.confirm(&self.path, base) } } diff --git a/crates/notebook/src/smb/tests.rs b/crates/notebook/src/smb/tests.rs index 54078552e8a67c7b15b3a34a1d308dda2d0a2e57..af3c34d20f8ccbfbe66741337843cd11284932df 100644 --- a/crates/notebook/src/smb/tests.rs +++ b/crates/notebook/src/smb/tests.rs @@ -1,7 +1,8 @@ use super::*; use onestore::{ - RevisionIndex, Store, + ExGuid, RevisionIndex, Store, Transaction, document::{Document, Kind}, + op::{Edit, Op, PageOp}, }; use std::{ fs, @@ -117,11 +118,7 @@ fn create(client: &Client, path: &str, bytes: &[u8]) { }, ) .unwrap(); - let mut file = File { - client, - id: Some(response.file_id), - length: response.end_of_file, - }; + let mut file = File::new(client, &response); let mut offset = 0; while offset < bytes.len() { offset += file.write_at(offset as u64, &bytes[offset..]).unwrap(); @@ -129,6 +126,43 @@ fn create(client: &Client, path: &str, bytes: &[u8]) { file.flush().unwrap(); file.close().unwrap(); } +/// The transaction replacing `range` of the first text in `image` with `with`, as typed now. +fn replaced(image: &[u8], range: std::ops::Range, with: &str) -> Transaction { + let (space, text, _) = text(image); + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, image.to_vec()).unwrap(); + let at = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64 + / 100 + + 116_444_736_000_000_000; + section + .apply( + "Fixture", + &Edit { + at, + ops: vec![Op::Page { + space, + op: PageOp::Text { + text, + range, + with: with.to_owned(), + }, + }], + }, + ) + .unwrap(); + section.seal().unwrap().unwrap() +} + +/// `image` after `transaction`. +fn applied(image: &[u8], transaction: &Transaction) -> Vec { + let mut image = image.to_vec(); + transaction.apply(&mut image).unwrap(); + image +} + fn text(bytes: &[u8]) -> (ExGuid, ExGuid, String) { let store = Store::parse(bytes).unwrap(); assert!(store.checksum_mismatches.is_empty()); @@ -171,21 +205,15 @@ fn live_coordination() { client .open(&path, false) .unwrap() - .coordinate(&path, false) + .coordinate(&path, false, &[]) .unwrap() }) .collect(); - let (sid, oid, before) = text(&source); + let (_, _, before) = text(&source); let replacement = "After café 🦀"; + let everything = 0..before.encode_utf16().count() as u32; writer - .commit_text( - &path, - &source, - sid, - oid, - 0..before.encode_utf16().count() as u32, - replacement, - ) + .commit_transaction(&path, &replaced(&source, everything.clone(), replacement)) .unwrap(); let after = writer.read(&path, 1 << 20).unwrap(); assert_eq!(text(&after).2, replacement); @@ -196,7 +224,7 @@ fn live_coordination() { assert_eq!(snapshot, after); } let error = writer - .commit_text(&path, &source, sid, oid, 0..1, "X") + .commit_transaction(&path, &replaced(&source, 0..1, "X")) .unwrap_err(); assert_eq!(error.state, CommitState::NotCommitted); assert_eq!(error.error.kind(), io::ErrorKind::ResourceBusy); @@ -262,7 +290,7 @@ fn live_coordination() { .unwrap(); drop(connection); guard.close().unwrap(); - let error = stale.coordinate(&path, true).err().unwrap(); + let error = stale.coordinate(&path, true, &[]).err().unwrap(); assert_eq!(error.kind(), io::ErrorKind::ResourceBusy); assert_eq!(writer.read(&path, 1 << 20).unwrap(), source); @@ -270,7 +298,7 @@ fn live_coordination() { let file = retiring .open(&path, true) .unwrap() - .coordinate(&path, true) + .coordinate(&path, true, &[]) .unwrap(); retiring.retire(); assert_eq!( @@ -282,7 +310,7 @@ fn live_coordination() { loop { match writer .open(&path, true) - .and_then(|file| file.coordinate(&path, true)) + .and_then(|file| file.coordinate(&path, true, &[])) { Ok(file) => { file.close().unwrap(); @@ -300,7 +328,7 @@ fn live_coordination() { let mut unfinished = writer .open(&path, true) .unwrap() - .coordinate(&path, true) + .coordinate(&path, true, &[]) .unwrap(); assert_eq!( unfinished @@ -314,14 +342,7 @@ fn live_coordination() { assert_eq!(snapshot.len(), source.len() + 11); assert_eq!(text(&snapshot).2, before); writer - .commit_text( - &path, - &snapshot, - sid, - oid, - 0..before.encode_utf16().count() as u32, - "Recovered café 🦀", - ) + .commit_transaction(&path, &replaced(&snapshot, everything, "Recovered café 🦀")) .unwrap(); let recovered = writer.read(&path, 1 << 20).unwrap(); assert_eq!(text(&recovered).2, "Recovered café 🦀"); @@ -376,19 +397,27 @@ fn live_stamp_and_transaction() { onestore::Stamp::of(&source).unwrap() ); guard.close().unwrap(); - let (sid, oid, _) = text(&source); - let edit = onestore::PreparedEdit::text(&source, sid, oid, 0..0, "Changed ").unwrap(); - let transaction = edit.transaction(); + let transaction = replaced(&source, 0..0, "Changed "); + let edited = applied(&source, &transaction); poller.commit_transaction(&path, &transaction).unwrap(); assert_eq!( poller.stamp(&path).unwrap(), - onestore::Stamp::of(edit.as_bytes()).unwrap() + onestore::Stamp::of(&edited).unwrap() ); - assert_eq!(poller.read(&path, 1 << 20).unwrap(), edit.as_bytes()); + assert_eq!(poller.read(&path, 1 << 20).unwrap(), edited); let stale = poller.commit_transaction(&path, &transaction).unwrap_err(); assert_eq!(stale.state, CommitState::NotCommitted); assert_eq!(stale.error.kind(), io::ErrorKind::ResourceBusy); - assert_eq!(poller.read(&path, 1 << 20).unwrap(), edit.as_bytes()); + assert_eq!(poller.read(&path, 1 << 20).unwrap(), edited); + let base = onestore::Stamp::of(&edited).unwrap(); + poller.confirm(&path, &base).unwrap(); + let confirmed = poller.read(&path, 1 << 20).unwrap(); + assert_eq!(confirmed[..212], edited[..212]); + assert_ne!(confirmed[212..252], edited[212..252]); + assert_eq!(confirmed[252..], edited[252..]); + let stale = poller.confirm(&path, &base).unwrap_err(); + assert_eq!(stale.state, CommitState::NotCommitted); + assert_eq!(stale.error.kind(), io::ErrorKind::ResourceBusy); poller.delete(&path).unwrap(); } @@ -488,7 +517,7 @@ fn live_reader_hold() { let mut file = client .open(&path, false) .unwrap() - .coordinate(&path, false) + .coordinate(&path, false, &[]) .unwrap(); fs::write(output.join("ready"), b"held").unwrap(); let deadline = Instant::now() + Duration::from_secs(300); @@ -580,13 +609,13 @@ fn live_structure() { let mut notebook = Notebook::open_smb(std::sync::Arc::clone(&client), &root, cache.path()).unwrap(); assert_eq!( - notebook.create_section("", "Second", "Author").unwrap(), + notebook.create_section("", "Second", &onestore::PageCreation::new(None, Some(""), "Author").unwrap()).unwrap(), "Second.one" ); assert_eq!(notebook.create_group("", "Archive").unwrap(), "Archive"); assert_eq!( notebook - .create_section("Archive", "Inner", "Author") + .create_section("Archive", "Inner", &onestore::PageCreation::new(None, Some(""), "Author").unwrap()) .unwrap(), "Archive/Inner.one" ); diff --git a/crates/notebook/src/smb/tests/faults.rs b/crates/notebook/src/smb/tests/faults.rs index 99641ee062cb06e9a4dad56f284ca954293b65fc..a9b74ec3144932898298846fec2c3026203a47a8 100644 --- a/crates/notebook/src/smb/tests/faults.rs +++ b/crates/notebook/src/smb/tests/faults.rs @@ -297,7 +297,7 @@ fn live_message_loss() { &source, ) .unwrap(); - let (sid, oid, before) = text(&source); + let (_, _, before) = text(&source); let after = if fixture == "chunked" { "After café 🦀 ".repeat(6000) } else { @@ -318,15 +318,11 @@ fn live_message_loss() { ); std::thread::sleep(Duration::from_millis(5)); } - let expected = onestore::replace_text( + let everything = 0..before.encode_utf16().count() as u32; + let new = snapshot(&applied( &source, - sid, - oid, - 0..before.encode_utf16().count() as u32, - &after, - ) - .unwrap(); - let new = snapshot(&expected); + &replaced(&source, everything.clone(), &after), + )); let baseline_path = format!("fault-{prefix}-{fixture}-baseline.one"); create(&observer, &baseline_path, &source); configure(&output, json!({"phase":format!("{fixture}-setup")})); @@ -340,13 +336,9 @@ fn live_message_loss() { let start = configure(&output, json!({"phase":format!("{fixture}-baseline")})); let began = stamp(); baseline - .commit_text( + .commit_transaction( &baseline_path, - &source, - sid, - oid, - 0..before.encode_utf16().count() as u32, - &after, + &replaced(&source, everything.clone(), &after), ) .unwrap(); let events = records(&output); @@ -397,15 +389,9 @@ fn live_message_loss() { json!({"phase":name, "direction":direction, "cut":command, "status":status, "occurrence":occurrence}), ); let began = stamp(); + let transaction = replaced(&source, everything.clone(), &after); let error = interrupted - .commit_text( - &path, - &source, - sid, - oid, - 0..before.encode_utf16().count() as u32, - &after, - ) + .commit_transaction(&path, &transaction) .unwrap_err(); assert_eq!( interrupted.read(&path, 1 << 20).unwrap_err().kind(), @@ -429,7 +415,7 @@ fn live_message_loss() { loop { match fresh .open(&path, true) - .and_then(|file| file.coordinate(&path, true)) + .and_then(|file| file.coordinate(&path, true, &[])) { Ok(file) => { file.close().unwrap(); @@ -458,14 +444,7 @@ fn live_message_loss() { } if !visible { fresh - .commit_text( - &path, - &saved, - sid, - oid, - 0..before.encode_utf16().count() as u32, - &after, - ) + .commit_transaction(&path, &replaced(&saved, everything.clone(), &after)) .unwrap(); } let saved = fresh.read(&path, 1 << 20).unwrap(); @@ -475,7 +454,7 @@ fn live_message_loss() { ); let end = after.encode_utf16().count() as u32; fresh - .commit_text(&path, &saved, sid, oid, end..end, suffix) + .commit_transaction(&path, &replaced(&saved, end..end, suffix)) .unwrap(); let recovered = fresh.read(&path, 1 << 20).unwrap(); assert_eq!(text(&recovered).2, format!("{after}{suffix}")); diff --git a/crates/notebook/src/sync.rs b/crates/notebook/src/sync.rs index f972d937376b28dcce55dedbbcac584e1c792d5c..e23cca5ce9edd84e1b1f37fabd4cb8ab1a192088 100644 --- a/crates/notebook/src/sync.rs +++ b/crates/notebook/src/sync.rs @@ -1,5 +1,5 @@ use super::*; -use crate::working::{Rebased, Request, Resolve, Sealed}; +use crate::working::{Request, Sealed}; use onestore::{CommitError, CommitState, RevisionIndex, Stamp, Store, Transaction}; use rusqlite::OptionalExtension; use std::{ @@ -12,44 +12,12 @@ use std::{ /// cached readers. pub trait Remote { fn read(&mut self) -> io::Result>; - /// The file's stamp without reading its body, when the remote can tell. While it is the - /// last observed image's, synchronization neither reads nor revalidates the file. - fn stamp(&mut self) -> io::Result> { - Ok(None) - } + /// The file's stamp without reading its body or coordinating with writers. While it is + /// the last observed image's, synchronization neither reads nor revalidates the file. + fn stamp(&mut self) -> io::Result; fn publish(&mut self, transaction: &Transaction) -> std::result::Result<(), CommitError>; - fn confirm(&mut self, snapshot: &[u8]) -> std::result::Result<(), CommitError>; -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(i64)] -pub enum ConflictKind { - /// The edited page or a selected page no longer exists remotely. - TargetUnavailable = 0, - /// The remote section no longer accepts the edit. - UnsupportedEdit = 1, - /// Page order or indentation changed remotely in a way the batch cannot absorb. - StructureChanged = 2, - /// The remote page changed where the local edits changed it too; review is required. - ContentChanged = 3, -} - -impl ConflictKind { - fn from_stored(kind: i64) -> Result { - Ok(match kind { - 0 => Self::TargetUnavailable, - 1 => Self::UnsupportedEdit, - 2 => Self::StructureChanged, - 3 => Self::ContentChanged, - _ => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "Unknown cached conflict kind", - ) - .into()); - } - }) - } + /// Confirms that the file still has `base`'s stamp and is durable (`onestore::confirm`). + fn confirm(&mut self, base: &Stamp) -> std::result::Result<(), CommitError>; } #[derive(Debug, Clone, PartialEq, Eq)] @@ -59,7 +27,6 @@ pub enum EditStatus { AwaitingConfirmation { revision: ExGuid, }, - Conflict(ConflictKind), /// Historical confirmation; later remote edits or restores may remove the effect. Published { revision: ExGuid, @@ -84,15 +51,13 @@ struct State { base: Stamp, /// The observed remote image's stamp while it is not the base. remote: Option, - /// The first batch waiting on a remote answer: sealed, attempted, or in conflict. + /// The first batch waiting on a remote answer to an attempt. blocked: Option, queued: bool, } struct Blocked { batch: i64, - attempted: bool, - conflict: Option, revisions: Option>, } @@ -101,24 +66,14 @@ fn state(connection: &Connection) -> Result { .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "The cache has no base image"))?; let blocked = connection .query_row( - "SELECT id, attempted, conflict, revisions FROM batches - WHERE attempted=1 OR conflict IS NOT NULL ORDER BY id LIMIT 1", + "SELECT id, revisions FROM batches WHERE attempted=1 ORDER BY id LIMIT 1", [], - |row| { - Ok(( - row.get::<_, i64>(0)?, - row.get::<_, bool>(1)?, - row.get::<_, Option>(2)?, - row.get::<_, Option>(3)?, - )) - }, + |row| Ok((row.get::<_, i64>(0)?, row.get::<_, Option>(1)?)), ) .optional()? - .map(|(batch, attempted, conflict, revisions)| { + .map(|(batch, revisions)| { Ok::<_, Error>(Blocked { batch, - attempted, - conflict: conflict.map(ConflictKind::from_stored).transpose()?, revisions: revisions .map(|revisions| decode_revisions(&revisions)) .transpose()?, @@ -186,7 +141,7 @@ impl Replica { /// The last observed remote image. Observation alone does not acknowledge any pending /// edit's remote durability. - pub fn remote_snapshot(&self) -> Result> { + pub(crate) fn remote_snapshot(&self) -> Result> { let connection = self.lock()?; let image = match base::read(&connection, base::Image::Remote)? { Some(image) => Some(image), @@ -197,26 +152,6 @@ impl Replica { })?) } - /// The unpublished batch the remote no longer accepts, if any. - pub fn conflict(&self) -> Result> { - let connection = self.lock()?; - let row: Option<(i64, i64, String)> = connection - .query_row( - "SELECT id, conflict, space FROM batches WHERE conflict IS NOT NULL ORDER BY id LIMIT 1", - [], - |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), - ) - .optional()?; - row.map(|(batch, kind, space)| { - Ok(Conflict { - id: newest(&connection, batch)?, - space: space.parse()?, - kind: ConflictKind::from_stored(kind)?, - }) - }) - .transpose() - } - /// Publishes the oldest unpublished batch, rebasing the queue first when the remote /// changed. Reads the remote image only when its stamp moved; network I/O holds /// synchronization ownership without holding the cache mutex. Uncertain attempts are @@ -225,8 +160,8 @@ impl Replica { let _owner = self.sync_owner()?; let state = state(&*self.lock()?)?; let observed = remote.stamp().map_err(Error::RemoteIo)?; - if let (Some(observed), Some(blocked)) = (&observed, &state.blocked) - && Some(observed) == state.remote.as_ref().or(Some(&state.base)) + if let Some(blocked) = &state.blocked + && observed == *state.remote.as_ref().unwrap_or(&state.base) { // Nothing new to decide: the remote is as it was when the batch blocked. let id = newest(&*self.lock()?, blocked.batch)?; @@ -239,7 +174,7 @@ impl Replica { }); } let image = match observed { - Some(observed) if observed == state.base => None, + observed if observed == state.base => None, _ => { let image = remote.read().map_err(Error::RemoteIo)?; // A read image is compared whole: a stamp stands for it only when read alone. @@ -249,13 +184,7 @@ impl Replica { }; let mut changed = Vec::new(); if let Some(image) = image { - if let Some(Blocked { - batch, - attempted: true, - revisions, - .. - }) = &state.blocked - { + if let Some(Blocked { batch, revisions }) = &state.blocked { let id = newest(&*self.lock()?, *batch)?; let revisions = revisions.clone().unwrap_or_default(); let store = Store::parse(&image)?; @@ -308,7 +237,7 @@ impl Replica { .collect(), ) }; - if let Err(error) = remote.confirm(&image) { + if let Err(error) = remote.confirm(&Stamp::of(&image)?) { if error.state == CommitState::Committed { self.acknowledge(*batch, sealed.as_ref(), receipts.as_ref())?; } @@ -316,49 +245,26 @@ impl Replica { } self.acknowledge(*batch, sealed.as_ref(), receipts.as_ref())?; let revision = self.receipt(id)?; - changed = self.rebase(Some(image), None)?.unwrap_or_default(); + changed = self.rebase(Some(image))?; return Ok(Synced { edit: Some((id, EditStatus::Published { revision })), changed, }); } - match self.rebase(Some(image), None)? { - Ok(spaces) => changed = spaces, - Err(conflict) => { - return Ok(Synced { - edit: Some((conflict.id, EditStatus::Conflict(conflict.kind))), - changed, - }); - } - } + changed = self.rebase(Some(image))?; } else if let Some(blocked) = &state.blocked { if state.remote.is_some() { // The remote is back at the base: what blocked the queue is gone. - let connection = self.lock()?; - base::clear(&connection, base::Image::Remote)?; - if blocked.conflict.is_some() { - connection.execute("UPDATE batches SET conflict=NULL, space=NULL", [])?; - } - } - if blocked.attempted { - let id = newest(&*self.lock()?, blocked.batch)?; - return Ok(Synced { - edit: Some(( - id, - status(&*self.lock()?, id)?.unwrap_or(EditStatus::Pending), - )), - changed, - }); - } - if let Some(kind) = blocked.conflict - && state.remote.is_none() - { - let id = newest(&*self.lock()?, blocked.batch)?; - return Ok(Synced { - edit: Some((id, EditStatus::Conflict(kind))), - changed, - }); + base::clear(&*self.lock()?, base::Image::Remote)?; } + let id = newest(&*self.lock()?, blocked.batch)?; + return Ok(Synced { + edit: Some(( + id, + status(&*self.lock()?, id)?.unwrap_or(EditStatus::Pending), + )), + changed, + }); } else if !state.queued { return Ok(Synced::default()); } @@ -383,7 +289,9 @@ impl Replica { let id = newest(&*self.lock()?, batch)?; let Some(transaction) = transaction else { // Edits that changed nothing are published once the remote's image is durable. - let base = base::read(&*self.lock()?, base::Image::Base)?.unwrap_or_default(); + let base = base::stamp(&*self.lock()?, base::Image::Base)?.ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "The cache holds no base image") + })?; if let Err(error) = remote.confirm(&base) { if error.state == CommitState::Committed { self.acknowledge(batch, None, None)?; @@ -439,22 +347,10 @@ impl Replica { )) } - /// Asks the section thread to replay the queue on `image`; `Err` is the conflict found. - fn rebase( - &self, - image: Option>, - resolve: Option, - ) -> Result, Conflict>> { - Ok( - match self.ask(|reply| Request::Rebase { - image, - resolve, - reply, - })? { - Rebased::Applied { changed } => Ok(changed), - Rebased::Conflict { id, space, kind } => Err(Conflict { id, space, kind }), - }, - ) + /// Asks the section thread to replay the queue on `image`, returning the pages the + /// remote changed. + fn rebase(&self, image: Option>) -> Result> { + self.ask(|reply| Request::Rebase { image, reply }) } fn receipt(&self, id: u64) -> Result { @@ -501,49 +397,6 @@ impl Replica { Ok(()) } - /// Resolves the conflict holding edit `id`: the batch and everything queued after it - /// drop their ops on the conflicted page; `Mine` rewrites the remote page to the local - /// one first. A conflict on another page may follow. - pub fn resolve(&self, id: u64, resolution: Resolution) -> Result<()> { - self.resolve_with(id, resolution, None) - } - - pub(crate) fn resolve_with(&self, id: u64, keep: Resolution, page: Option) -> Result<()> { - let owner = self.sync_owner()?; - let (first, space) = { - let connection = self.lock()?; - let row: Option<(i64, String)> = connection - .query_row( - "SELECT (SELECT min(id) FROM edits WHERE batch=batches.id), space FROM batches - WHERE conflict IS NOT NULL AND id=(SELECT batch FROM edits WHERE id=?1)", - [signed(id)?], - |row| Ok((row.get(0)?, row.get(1)?)), - ) - .optional()?; - let Some((first, space)) = row else { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "The edit is not in conflict", - ) - .into()); - }; - (unsigned(first)?, space.parse()?) - }; - // A conflict on another page is recorded like any other. - let _ = self.rebase( - None, - Some(Resolve { - first, - space, - keep, - page, - }), - )?; - drop(owner); - self.wake_sync(); - Ok(()) - } - /// Retires the uncertain attempt of the batch holding edit `id` after review. The /// queue is first exported to `archive` (a new file), which is the record: no receipt /// is written. `Mine` publishes the batch again against the current remote; `Theirs` @@ -620,10 +473,7 @@ impl Replica { None if !state.queued => state.base, None => return Ok(false), }; - let Some(stamp) = remote.stamp().map_err(Error::RemoteIo)? else { - return Ok(false); - }; - Ok(stamp == expected) + Ok(remote.stamp().map_err(Error::RemoteIo)? == expected) } } @@ -651,26 +501,23 @@ pub(crate) fn status(connection: &Connection, id: u64) -> Result, Option, String)> = connection + let record: Option<(bool, Option, String)> = connection .query_row( - "SELECT batches.attempted, batches.conflict, batches.revisions, edits.edit + "SELECT batches.attempted, batches.revisions, edits.edit FROM edits JOIN batches ON batches.id=edits.batch WHERE edits.id=?1", [id], - |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)), + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), ) .optional()?; Ok(match record { None => None, - Some((true, _, revisions, edit)) => { + Some((true, revisions, edit)) => { let revisions = decode_revisions(revisions.as_deref().unwrap_or("{}"))?; let edit: onestore::op::Edit = serde_json::from_str(&edit) .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; let revision = revision_of(&edit, &revisions)?; Some(EditStatus::AwaitingConfirmation { revision }) } - Some((false, Some(kind), ..)) => { - Some(EditStatus::Conflict(ConflictKind::from_stored(kind)?)) - } - Some((false, None, ..)) => Some(EditStatus::Pending), + Some((false, ..)) => Some(EditStatus::Pending), }) } diff --git a/crates/notebook/src/working.rs b/crates/notebook/src/working.rs index ec937105e69c6ba4a4d9fba33a3483b46e8ed16d..53c2f3fa7b83c31f06bc26a1baff499848736d96 100644 --- a/crates/notebook/src/working.rs +++ b/crates/notebook/src/working.rs @@ -4,12 +4,11 @@ //! and, when the remote changed, to rebase the queue. use crate::{ - ConflictKind, Resolution, Result, base, lock, merge, queue, session::Save, signed, - worker::Signal, + Result, base, lock, merge, queue, worker::Signal, }; use onestore::{ Arena, ExGuid, Section, Transaction, - op::{Edit, Op, OpError}, + op::{Edit, OpError}, page::Page, }; use rusqlite::{Connection, OptionalExtension, TransactionBehavior, params}; @@ -28,15 +27,6 @@ pub(crate) enum Request { edit: Edit, reply: Reply, }, - /// A whole-page save of the old session API: `None` answers a save a later one - /// continuing it absorbed. - Save { - space: ExGuid, - before: Page, - after: Page, - author: String, - reply: Reply>, - }, Page { space: ExGuid, reply: Reply, @@ -44,6 +34,9 @@ pub(crate) enum Request { Pages { reply: Reply>, }, + Conflicts { + reply: Reply)>>, + }, /// Answers once the edits before it are written. Flush { reply: Reply<()>, @@ -53,11 +46,11 @@ pub(crate) enum Request { Seal { reply: Reply>, }, - /// Replays the queue on `image` (the stored remote image, or the base, when `None`). + /// Replays the queue on `image` (the stored remote image, or the base, when `None`); + /// answers with the pages the remote changed. Rebase { image: Option>, - resolve: Option, - reply: Reply, + reply: Reply>, }, /// Rereads the queue after the sync thread replaced it. Reopen { @@ -78,34 +71,14 @@ pub(crate) struct Sealed { pub transaction: Option, } -/// A conflict's resolution: the batch it holds and every later edit drop their ops on -/// `space`; `Mine` first rewrites the rebased page to `page`, the local one unless given. -pub(crate) struct Resolve { - pub first: u64, - pub space: ExGuid, - pub keep: Resolution, - pub page: Option, -} - -pub(crate) enum Rebased { - /// The queue now applies to the image, which is the base; these pages changed. - Applied { changed: Vec }, - /// The batch holding edit `id` no longer applies; nothing changed but the record. - Conflict { - id: u64, - space: ExGuid, - kind: ConflictKind, - }, -} - impl Request { fn fail(self, message: &str) { let error = || io::Error::other(message.to_owned()).into(); match self { Self::Apply { reply, .. } => reply(Err(error())), - Self::Save { reply, .. } => reply(Err(error())), Self::Page { reply, .. } => reply(Err(error())), Self::Pages { reply } => reply(Err(error())), + Self::Conflicts { reply } => reply(Err(error())), Self::Flush { reply } => reply(Err(error())), Self::Seal { reply } => reply(Err(error())), Self::Rebase { reply, .. } => reply(Err(error())), @@ -244,8 +217,7 @@ fn fail(error: crate::Error, backlog: VecDeque, requests: mpsc::Receive enum Job { Rebase { image: Option>, - resolve: Option, - reply: Reply, + reply: Reply>, }, Reopen { reply: Reply<()>, @@ -253,18 +225,14 @@ enum Job { } /// Runs `job`, rereads the section and takes the requests over from the thread that -/// started it; a rebase that found a conflict changed nothing that thread serves. +/// started it; a failed rebase changed nothing that thread serves. fn build(shared: Arc, job: Job, signal: mpsc::Sender) { let answer: Box = match job { Job::Reopen { reply } => Box::new(move || reply(Ok(()))), - Job::Rebase { - image, - resolve, - reply, - } => match rebase(&shared.connection, image, resolve) { - Ok(applied @ Rebased::Applied { .. }) => Box::new(move || reply(Ok(applied))), - other => { - reply(other); + Job::Rebase { image, reply } => match rebase(&shared.connection, image) { + Ok(changed) => Box::new(move || reply(Ok(changed))), + Err(error) => { + reply(Err(error)); let _ = signal.send(Request::Resume); return; } @@ -298,21 +266,7 @@ fn build(shared: Arc, job: Job, signal: mpsc::Sender) { struct Accepted { author: String, edit: Edit, - done: Done, -} - -enum Done { - Apply(Reply), - Save(Reply>), -} - -impl Done { - fn reply(self, written: Result) { - match self { - Self::Apply(reply) => reply(written), - Self::Save(reply) => reply(written.map(|id| Some(Save::Queued(id)))), - } - } + reply: Reply, } struct Working<'a> { @@ -321,8 +275,6 @@ struct Working<'a> { open: Option, /// Spaces the open batch's edits change. touched: BTreeSet, - /// The last whole-page save: its space, the page it was given, and the page as stored. - saved: Option<(ExGuid, Page, Page)>, } impl<'a> Working<'a> { @@ -332,7 +284,6 @@ impl<'a> Working<'a> { section, open, touched, - saved: None, }) } @@ -367,6 +318,9 @@ impl<'a> Working<'a> { reply(self.section.page(space).map_err(Into::into)) } Request::Pages { reply } => reply(self.section.pages().map_err(Into::into)), + Request::Conflicts { reply } => { + reply(self.section.conflicts().map_err(Into::into)) + } Request::Handover(to) => { let mut waiting = held.take().unwrap_or_default(); waiting.extend(burst.drain(..).chain(later.drain(..))); @@ -386,86 +340,9 @@ impl<'a> Working<'a> { edit, reply, } => self - .accept( - author, - edit, - Done::Apply(reply), - &mut accepted, - connection, - worker, - ) + .accept(author, edit, reply, &mut accepted, connection, worker) .err() .unwrap_or(false), - Request::Save { - space, - before, - mut after, - author, - reply, - } => { - // Saves that continue one another are written once. - let mut replies = vec![reply]; - while let Some(Request::Save { - space: next, - before: continued, - author: by, - .. - }) = burst.front() - && (*next, continued, by) == (space, &after, &author) - { - let Some(Request::Save { - after: later, - reply, - .. - }) = burst.pop_front() - else { - unreachable!() - }; - after = later; - replies.push(reply); - } - let last = replies.pop().unwrap(); - for folded in replies { - folded(Ok(None)); - } - match self.save(space, &before, &after) { - Ok(Some(edit)) => match self.accept( - author, - edit, - Done::Save(last), - &mut accepted, - connection, - worker, - ) { - Ok(()) => { - self.saved = self - .section - .page(space) - .ok() - .map(|stored| (space, after, stored)); - false - } - Err(broken) => broken, - }, - Ok(None) => { - self.saved = self - .section - .page(space) - .ok() - .map(|stored| (space, after, stored)); - last(Ok(Some(Save::Unchanged))); - false - } - Err(Stale::Stale) => { - last(Ok(Some(Save::Stale))); - false - } - Err(Stale::Error(error)) => { - last(Err(error)); - false - } - } - } Request::Page { space, reply } => { reply(self.section.page(space).map_err(Into::into)); false @@ -474,6 +351,10 @@ impl<'a> Working<'a> { reply(self.section.pages().map_err(Into::into)); false } + Request::Conflicts { reply } => { + reply(self.section.conflicts().map_err(Into::into)); + false + } request @ (Request::Flush { .. } | Request::Seal { .. }) if !burst.is_empty() => { @@ -502,50 +383,15 @@ impl<'a> Working<'a> { failed } } - Request::Rebase { - image, - mut resolve, - reply, - } => { - let written = self.flush(connection, worker, &mut accepted); - // The page a `Mine` resolution keeps is read here, before the - // rebuild; the root space lists pages and holds none. - let root = self.section.root(); - let kept = match &mut resolve { - Some(Resolve { - keep: Resolution::Mine, - page: page @ None, - space, - .. - }) if *space != root => { - self.section.page(*space).map(|local| *page = Some(local)) - } - _ => Ok(()), - }; - match (written, kept) { - (true, Ok(())) => { - held = self - .rebuild( - shared, - Job::Rebase { - image, - resolve, - reply, - }, - ) - .then(VecDeque::new); - false - } - (_, Err(error)) => { - reply(Err(error.into())); - !written - } - (false, _) => { - reply(Err( - io::Error::other("The queue could not be written").into() - )); - true - } + Request::Rebase { image, reply } => { + if self.flush(connection, worker, &mut accepted) { + held = self + .rebuild(shared, Job::Rebase { image, reply }) + .then(VecDeque::new); + false + } else { + reply(Err(io::Error::other("The queue could not be written").into())); + true } } Request::Reopen { reply } => { @@ -598,7 +444,7 @@ impl<'a> Working<'a> { &mut self, author: String, edit: Edit, - done: Done, + reply: Reply, accepted: &mut Vec, connection: &Mutex, worker: &Worker, @@ -607,7 +453,11 @@ impl<'a> Working<'a> { Ok(()) => { self.touched .extend(queue::spaces(&edit, self.section.root())); - accepted.push(Accepted { author, edit, done }); + accepted.push(Accepted { + author, + edit, + reply, + }); Ok(()) } Err(error) => { @@ -615,40 +465,12 @@ impl<'a> Working<'a> { if broken { self.flush(connection, worker, accepted); } - done.reply(Err(error.into())); + reply(Err(error.into())); Err(broken) } } } - /// The edit that takes the stored page from `before` to `after`. A save continuing - /// the previous one finds the page as that one stored it, which may read back - /// normalized. - fn save( - &mut self, - space: ExGuid, - before: &Page, - after: &Page, - ) -> std::result::Result, Stale> { - let current = self - .section - .page(space) - .map_err(|error| Stale::Error(error.into()))?; - let expected = match &self.saved { - Some((saved, given, stored)) if *saved == space && given == before => stored, - _ => before, - }; - if current != *expected { - return Err(Stale::Stale); - } - let ops = onestore::op::lower_page(¤t, after) - .map_err(|error| Stale::Error(OpError::Unsupported(error.message).into()))?; - Ok((!ops.is_empty()).then(|| Edit { - at: crate::now(), - ops: ops.into_iter().map(|op| Op::Page { space, op }).collect(), - })) - } - /// Writes the accepted edits in one transaction, then answers their senders; false /// when the write failed and the section holds edits the queue lacks. fn flush( @@ -689,15 +511,14 @@ impl<'a> Working<'a> { match written { Ok(ids) => { for (edit, id) in accepted.drain(..).zip(ids) { - edit.done.reply(Ok(id)); + (edit.reply)(Ok(id)); } crate::wake(worker); } Err(error) => { let message = error.to_string(); for edit in accepted.drain(..) { - edit.done - .reply(Err(io::Error::other(message.clone()).into())); + (edit.reply)(Err(io::Error::other(message.clone()).into())); } } } @@ -712,7 +533,7 @@ impl<'a> Working<'a> { { let connection = lock(connection)?; let waiting: bool = connection.query_row( - "SELECT EXISTS(SELECT 1 FROM batches WHERE sealed IS NOT NULL OR conflict IS NOT NULL)", + "SELECT EXISTS(SELECT 1 FROM batches WHERE sealed IS NOT NULL)", [], |row| row.get(0), )?; @@ -746,12 +567,10 @@ impl<'a> Working<'a> { } } -/// Replays every queued edit on `image`, keeping it as the base when all apply. -fn rebase( - connection: &Mutex, - image: Option>, - resolve: Option, -) -> Result { +/// Replays every queued edit on `image`, which becomes the base; each page whose local +/// version the remote could not take gains a conflict page holding it, queued as a new edit. +/// Returns the pages the remote changed. +fn rebase(connection: &Mutex, image: Option>) -> Result> { let (base_image, image, edits) = { let connection = lock(connection)?; if connection.query_row( @@ -777,117 +596,91 @@ fn rebase( (base_image, image, queue::load(&connection, None)?) }; let old_arena = Arena::default(); - let mut old = Section::open(&old_arena, base_image.clone())?; + let mut old = Section::open(&old_arena, base_image)?; let before: BTreeMap = old.revisions().collect(); - let mut after: BTreeMap; + let remote_arena = Arena::default(); + let remote = Section::open(&remote_arena, image.clone())?; + if old.root() != remote.root() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Remote snapshot belongs to another document", + ) + .into()); + } + let after: BTreeMap = remote.revisions().collect(); + // The pages as the queue leaves them, read once a page conflicts: O(section). + let local_arena = Arena::default(); + let mut local = None; // Pages the remote already holds as the local edits leave them: their ops are done. let mut converged = BTreeSet::new(); - let outcome = loop { + let (rewritten, added) = loop { let arena = Arena::default(); let mut new = Section::open(&arena, image.clone())?; - if old.root() != new.root() { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Remote snapshot belongs to another document", - ) - .into()); + let merged = merge::rebase(&mut old, &mut new, &edits, &converged)?; + if !merged.conflicts.is_empty() && local.is_none() { + local = Some(replay(&local_arena, &*lock(connection)?)?.0); } - after = new.revisions().collect(); - let outcome = merge::rebase( - &mut old, - &mut new, - &edits, - &converged, - resolve.as_ref().map(|resolve| merge::Resolving { - first: resolve.first, - space: resolve.space, - mine: resolve.keep == Resolution::Mine, - target: resolve.page.as_ref(), - }), - )?; - if let merge::Outcome::Conflict { space, .. } = &outcome - && !converged.contains(space) - && let Some(local) = local_page(connection, *space)? - && Section::open(&Arena::default(), image.clone())? - .page(*space) - .is_ok_and(|remote| remote == local) - { - converged.insert(*space); + let pages: BTreeMap = merged + .conflicts + .keys() + .filter_map(|space| Some((*space, local.as_ref()?.page(*space).ok()?))) + .collect(); + let settled: Vec = pages + .iter() + .filter(|(space, page)| remote.page(**space).is_ok_and(|remote| remote == **page)) + .map(|(space, _)| *space) + .collect(); + if !settled.is_empty() { + converged.extend(settled); continue; } - break outcome; + let mut added = Vec::new(); + for (space, (author, objects)) in &merged.conflicts { + // A page the queue went on to delete keeps no version of its own. + let (Some(page), Some(local)) = (pages.get(space), local.as_mut()) else { + continue; + }; + let edit = merge::conflict_page( + &mut new, + &mut old, + local, + *space, + page, + author, + objects, + crate::now(), + )?; + added.push((author.clone(), edit)); + } + break (merged.rewritten, added); }; let mut connection = lock(connection)?; let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; - let rebased = match outcome { - merge::Outcome::Conflict { id, space, kind } => { - transaction.execute("UPDATE batches SET conflict=NULL, space=NULL", [])?; - transaction.execute( - "UPDATE batches SET conflict=?1, space=?2 WHERE id=(SELECT batch FROM edits WHERE id=?3)", - params![kind as i64, space.to_string(), signed(id)?], - )?; - if image == base_image { - base::clear(&transaction, base::Image::Remote)?; - } else { - base::write(&transaction, base::Image::Remote, &image)?; - } - // A conflict is reported under its batch's newest edit. - let id = crate::unsigned(transaction.query_row( - "SELECT max(id) FROM edits WHERE batch=(SELECT batch FROM edits WHERE id=?1)", - [signed(id)?], - |row| row.get(0), - )?)?; - Rebased::Conflict { id, space, kind } - } - merge::Outcome::Applied(rewritten) => { - base::write(&transaction, base::Image::Base, &image)?; - base::clear(&transaction, base::Image::Remote)?; - transaction.execute("INSERT INTO batches DEFAULT VALUES", [])?; - let batch = transaction.last_insert_rowid(); - transaction.execute("UPDATE edits SET batch=?1", [batch])?; - transaction.execute("DELETE FROM batches WHERE id<>?1", [batch])?; - for (id, edit) in rewritten { - queue::rewrite(&transaction, id, &edit)?; - } - if transaction - .query_row("SELECT count(*) FROM edits", [], |row| row.get::<_, i64>(0))? - == 0 - { - transaction.execute("DELETE FROM batches", [])?; - } - queue::collect(&transaction)?; - let mut changed: BTreeSet = before - .iter() - .filter(|(space, rid)| after.get(space) != Some(rid)) - .map(|(space, _)| *space) - .chain( - after - .keys() - .filter(|space| !before.contains_key(space)) - .copied(), - ) - .collect(); - changed.extend(resolve.as_ref().map(|resolve| resolve.space)); - changed.extend(converged); - Rebased::Applied { - changed: changed.into_iter().collect(), - } - } - }; + base::write(&transaction, base::Image::Base, &image)?; + base::clear(&transaction, base::Image::Remote)?; + transaction.execute("INSERT INTO batches DEFAULT VALUES", [])?; + let batch = transaction.last_insert_rowid(); + transaction.execute("UPDATE edits SET batch=?1", [batch])?; + transaction.execute("DELETE FROM batches WHERE id<>?1", [batch])?; + for (id, edit) in rewritten { + queue::rewrite(&transaction, id, &edit)?; + } + for (author, edit) in &added { + queue::insert(&transaction, None, batch, author, edit)?; + } + if transaction.query_row("SELECT count(*) FROM edits", [], |row| row.get::<_, i64>(0))? == 0 { + transaction.execute("DELETE FROM batches", [])?; + } + queue::collect(&transaction)?; transaction.commit()?; - Ok(rebased) -} - -/// A page as the queued edits leave it, read from the cache: O(section). -fn local_page(connection: &Mutex, space: ExGuid) -> Result> { - let arena = Arena::default(); - let (section, ..) = replay(&arena, &*lock(connection)?)?; - Ok(section.page(space).ok()) -} - -enum Stale { - Stale, - Error(crate::Error), + let mut changed: BTreeSet = before + .iter() + .filter(|(space, rid)| after.get(space) != Some(rid)) + .map(|(space, _)| *space) + .chain(after.keys().filter(|space| !before.contains_key(space)).copied()) + .collect(); + changed.extend(converged); + Ok(changed.into_iter().collect()) } /// Batches in order with their sealed transactions: `None` while open, `Some(None)` when diff --git a/crates/notebook/tests/assets.rs b/crates/notebook/tests/assets.rs index 16d1f0826eae82e50aa8ab2a995941efe14ed2ee..17a308be3320d6f122ccb1c81dcda68b29b4254f 100644 --- a/crates/notebook/tests/assets.rs +++ b/crates/notebook/tests/assets.rs @@ -15,6 +15,7 @@ use std::{ mod model_ops; #[path = "support/server.rs"] mod server; +use server::{remote_snapshot, snapshot}; const FIXTURE: &str = "../../corpus/native-external-assets/notebook"; @@ -86,8 +87,8 @@ impl notebook::discover::Source for Payload { fn downloaded_media_survives_reopen_and_recovery_with_the_queue_intact() { let directory = tempfile::tempdir().unwrap(); let cache = queued_cache(directory.path()); - let working = cache.snapshot().unwrap(); - let remote = cache.remote_snapshot().unwrap(); + let working = snapshot(&cache); + let remote = remote_snapshot(&cache); let pending = cache.pending().unwrap(); assert_eq!( pending.iter().map(|edit| edit.id).collect::>(), @@ -118,10 +119,10 @@ fn downloaded_media_survives_reopen_and_recovery_with_the_queue_intact() { (2, 1024) ); assert_eq!( - server::pages(&cache.snapshot().unwrap()), + server::pages(&snapshot(&cache)), server::pages(&working) ); - assert_eq!(cache.remote_snapshot().unwrap(), remote); + assert_eq!(remote_snapshot(&cache), remote); assert_eq!(cache.pending().unwrap(), pending); assert_eq!(cache.status(1).unwrap(), uncertain); cache @@ -227,7 +228,7 @@ fn download_network_wait_does_not_block_local_edits() { .unwrap() }); waiting.recv_timeout(Duration::from_secs(5)).unwrap(); - let text = first_text(&cache.snapshot().unwrap()); + let text = first_text(&snapshot(&cache)); let id = model_ops::save(&cache, text, |page| { model_ops::replace_text(page, text, 0..0, "during download ") }) @@ -272,12 +273,18 @@ fn a_native_refresh_removing_the_reference_rejects_an_inflight_download() { struct Native; impl notebook::Remote for Native { fn read(&mut self) -> io::Result> { - fs::read("../../corpus/native-external-assets/native/synthetic.one") + let mut image = fs::read("../../corpus/native-external-assets/native/synthetic.one")?; + // The fixture keeps the old header; a native commit writes a new file version. + image[212] ^= 1; + Ok(image) } fn publish(&mut self, _: &onestore::Transaction) -> Result<(), onestore::CommitError> { panic!("Unexpected publication") } - fn confirm(&mut self, _: &[u8]) -> Result<(), onestore::CommitError> { + fn stamp(&mut self) -> io::Result { + onestore::Stamp::of(&self.read()?).map_err(io::Error::other) + } + fn confirm(&mut self, _: &onestore::Stamp) -> Result<(), onestore::CommitError> { panic!("Unexpected confirmation") } } @@ -301,8 +308,8 @@ fn a_native_refresh_removing_the_reference_rejects_an_inflight_download() { ); assert!(cache.cached_asset(&name, 1024).unwrap().is_none()); assert_eq!( - cache.snapshot().unwrap(), - fs::read("../../corpus/native-external-assets/native/synthetic.one").unwrap() + snapshot(&cache), + notebook::Remote::read(&mut Native).unwrap() ); } diff --git a/crates/notebook/tests/cache.rs b/crates/notebook/tests/cache.rs index 54bdc5d9b4e90da0e5462e9d7694f9ed729b0012..d06e3fc9eaad9cff72a0d930c38326f8f12e04c6 100644 --- a/crates/notebook/tests/cache.rs +++ b/crates/notebook/tests/cache.rs @@ -1,3 +1,5 @@ +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; use notebook::{Error, Replica}; use onestore::{ ExGuid, RevisionIndex, Store, @@ -17,6 +19,7 @@ use std::{ mod model_ops; #[path = "support/server.rs"] mod server; +use server::snapshot; fn target(source: &[u8]) -> (ExGuid, ExGuid, String) { let store = Store::parse(source).unwrap(); @@ -60,7 +63,7 @@ fn cache_reopen_preserves_the_base_and_queued_edits() { let path = dir.path().join("section.sqlite"); let source = onestore::create_section("section.one", "café 🦀", "Fixture").unwrap(); let replica = Replica::create(&path, &source).unwrap(); - assert_eq!(replica.snapshot().unwrap(), source); + assert_eq!(snapshot(&replica), source); assert!(replica.pending().unwrap().is_empty()); let (sid, oid, _) = target(&source); let unchanged = |page: &mut onestore::page::Page| { @@ -73,7 +76,7 @@ fn cache_reopen_preserves_the_base_and_queued_edits() { }) .unwrap() .unwrap(); - assert_eq!(target(&replica.snapshot().unwrap()).2, "café 🐈 日本語"); + assert_eq!(target(&snapshot(&replica)).2, "café 🐈 日本語"); let edits = replica.pending().unwrap(); assert_eq!(edits.len(), 1); assert_eq!( @@ -99,14 +102,14 @@ fn cache_reopen_preserves_the_base_and_queued_edits() { "closing checkpoints and removes the log" ); let replica = Replica::open(&path).unwrap(); - assert_eq!(target(&replica.snapshot().unwrap()).2, "café 🐈 日本語"); + assert_eq!(target(&snapshot(&replica)).2, "café 🐈 日本語"); assert_eq!(replica.pending().unwrap(), edits); let second = replica .apply(model_ops::AUTHOR, typed(sid, oid, "Recovered ")) .unwrap(); assert!(second > first); assert_eq!( - target(&replica.snapshot().unwrap()).2, + target(&snapshot(&replica)).2, "Recovered café 🐈 日本語" ); assert_eq!(replica.pending().unwrap().len(), 2); @@ -160,7 +163,7 @@ fn refused_edits_and_failed_writes_leave_the_queue_as_it_was() { replica.apply("Author", typed(ExGuid::default(), oid, "Elsewhere ")), Err(Error::Rejected(_)) )); - assert_eq!(replica.snapshot().unwrap(), source); + assert_eq!(snapshot(&replica), source); assert!(replica.pending().unwrap().is_empty()); drop(replica); let connection = rusqlite::Connection::open(&path).unwrap(); @@ -168,10 +171,10 @@ fn refused_edits_and_failed_writes_leave_the_queue_as_it_was() { drop(connection); let replica = Replica::open(&path).unwrap(); assert!(replica.apply("Author", typed(sid, oid, "lost? ")).is_err()); - assert_eq!(replica.snapshot().unwrap(), source); + assert_eq!(snapshot(&replica), source); drop(replica); let replica = Replica::open(&path).unwrap(); - assert_eq!(replica.snapshot().unwrap(), source); + assert_eq!(snapshot(&replica), source); assert!(replica.pending().unwrap().is_empty()); } @@ -230,7 +233,7 @@ fn concurrent_recovery_exports_capture_one_complete_acknowledged_queue() { } }); assert_eq!(replica.pending().unwrap().len(), 60); - let content = target(&replica.snapshot().unwrap()).2; + let content = target(&snapshot(&replica)).2; for writer in 0..3 { for edit in 0..20 { assert_eq!(content.matches(&format!("[{writer}:{edit}] ")).count(), 1); @@ -255,7 +258,7 @@ fn ownership_and_foreign_file_rejection_preserve_existing_data() { assert!( matches!(Replica::create(&path, &source), Err(Error::Io(error)) if error.kind() == ErrorKind::AlreadyExists) ); - assert_eq!(replica.snapshot().unwrap(), source); + assert_eq!(snapshot(&replica), source); } drop(replica); let current: u32 = rusqlite::Connection::open(&path) @@ -264,10 +267,10 @@ fn ownership_and_foreign_file_rejection_preserve_existing_data() { .unwrap(); for sql in [ "PRAGMA application_id=0".to_owned(), - // Schema 14 converts; older caches do not. + // Schemas 14 and 15 convert; older caches do not. format!( "PRAGMA application_id=1330529615; PRAGMA user_version={}", - current - 2 + current - 3 ), format!("PRAGMA user_version={}", current + 1), ] { @@ -348,7 +351,7 @@ fn twelve_local_editors_queue_every_edit_once_in_order() { } let ids: BTreeSet<_> = outcomes.into_iter().flatten().collect(); assert_eq!(ids.len(), 240); - let content = target(&replica.snapshot().unwrap()).2; + let content = target(&snapshot(&replica)).2; assert!(content.ends_with("Shared café 🦀")); for writer in 0..12 { for edit in 0..20 { @@ -362,7 +365,7 @@ fn twelve_local_editors_queue_every_edit_once_in_order() { ); drop(replica); let reopened = Replica::open(&path).unwrap(); - assert_eq!(target(&reopened.snapshot().unwrap()).2, content); + assert_eq!(target(&snapshot(&reopened)).2, content); assert_eq!(reopened.pending().unwrap(), pending); } @@ -410,7 +413,7 @@ fn seeded_unicode_edits_and_restarts_match_an_independent_text_model() { } text = expected; assert_eq!( - target(&replica.snapshot().unwrap()).2, + target(&snapshot(&replica)).2, text, "seed 911, step {step}" ); @@ -418,7 +421,7 @@ fn seeded_unicode_edits_and_restarts_match_an_independent_text_model() { let pending = replica.pending().unwrap(); drop(replica); replica = Replica::open(&path).unwrap(); - assert_eq!(target(&replica.snapshot().unwrap()).2, text); + assert_eq!(target(&snapshot(&replica)).2, text); assert_eq!(replica.pending().unwrap(), pending); } } @@ -508,13 +511,10 @@ fn twelve_local_clients_preserve_inserted_identities_and_dependent_edits() { .flat_map(|(ids, _)| ids.iter().copied()) .collect(); assert_eq!(ids.len(), 84); - let snapshot = cache.snapshot().unwrap(); + let image = snapshot(&cache); drop(cache); let cache = Replica::open(&path).unwrap(); - assert_eq!( - server::pages(&cache.snapshot().unwrap()), - server::pages(&snapshot) - ); + assert_eq!(server::pages(&snapshot(&cache)), server::pages(&image)); assert_eq!( cache .pending() @@ -524,7 +524,7 @@ fn twelve_local_clients_preserve_inserted_identities_and_dependent_edits() { .collect::>(), ids ); - let store = Store::parse(&snapshot).unwrap(); + let store = Store::parse(&image).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let doc = Document::parse(&index).unwrap(); let s = &doc.spaces[&sid]; @@ -559,7 +559,7 @@ fn unrecognized_persisted_ops_are_rejected_without_dropping_fields() { let path = directory.path().join("unknown.sqlite"); let first = onestore::create_section("unknown.one", "Original", "Author").unwrap(); let second = onestore::PageCreation::new(None, Some("Second"), "Author").unwrap(); - let source = onestore::PreparedEdit::create_page(&first, &second) + let source = ops::section_op(&first, SectionOp::Create(second.clone())) .unwrap() .as_bytes() .to_vec(); diff --git a/crates/notebook/tests/changes.rs b/crates/notebook/tests/changes.rs index 1131d39958c722393ffcedb30c3b651d8d264c9b..4d86c90b6653093506a1e59c47208b0bc498f8c3 100644 --- a/crates/notebook/tests/changes.rs +++ b/crates/notebook/tests/changes.rs @@ -24,9 +24,9 @@ fn external_structure_changes_are_reported_by_identity() { let mut other = Notebook::open(&root, temporary.path().join("other")).unwrap(); assert_eq!(watcher.refresh().unwrap(), []); - other.create_section("", "Second", "Author").unwrap(); + other.create_section("", "Second", &onestore::PageCreation::new(None, Some(""), "Author").unwrap()).unwrap(); other.create_group("", "Group").unwrap(); - other.create_section("Group", "Inner", "Author").unwrap(); + other.create_section("Group", "Inner", &onestore::PageCreation::new(None, Some(""), "Author").unwrap()).unwrap(); assert_eq!( watcher.refresh().unwrap(), [ diff --git a/crates/notebook/tests/copy.rs b/crates/notebook/tests/copy.rs index 09d26a61193b5a1c9cf9e8a2e587798103c8dccf..8961a6f3a073da4a3838b5404fcd6f07d6dde706 100644 --- a/crates/notebook/tests/copy.rs +++ b/crates/notebook/tests/copy.rs @@ -1,7 +1,4 @@ -use notebook::{ - EditStatus, - session::{Event, Section}, -}; +use notebook::session::{Event, Section}; use onestore::{ RevisionIndex, Store, document::Document, @@ -197,13 +194,7 @@ fn pages_copy_into_another_section_with_their_content_and_fresh_identities() { ] ); let deadline = Instant::now() + Duration::from_secs(120); - while section - .queue() - .unwrap() - .iter() - .any(|edit| !matches!(edit.status, EditStatus::Published { .. })) - || !section.queue().unwrap().is_empty() - { + while !section.pending().unwrap().is_empty() { for event in section.events() { if let Event::Failed(message) = event { panic!("{message}"); @@ -212,7 +203,7 @@ fn pages_copy_into_another_section_with_their_content_and_fresh_identities() { assert!( Instant::now() < deadline, "publication did not finish: {:?}", - section.queue().unwrap() + section.pending().unwrap() ); std::thread::sleep(Duration::from_millis(50)); } @@ -233,7 +224,7 @@ fn pages_copy_into_another_section_with_their_content_and_fresh_identities() { } section.delete_pages(&[expected.last().unwrap().0]).unwrap(); let deadline = Instant::now() + Duration::from_secs(60); - while !section.queue().unwrap().is_empty() { + while !section.pending().unwrap().is_empty() { assert!(Instant::now() < deadline); std::thread::sleep(Duration::from_millis(50)); } diff --git a/crates/notebook/tests/migrate.rs b/crates/notebook/tests/migrate.rs index 4f929c388c58e8b9c04aa9c3246fbb1b1a762697..83af0972f9e8d3fa1b5cd6ca70b2dbc7dec78064 100644 --- a/crates/notebook/tests/migrate.rs +++ b/crates/notebook/tests/migrate.rs @@ -1,8 +1,12 @@ //! Converting schema-14 caches: whole-page edits become ops, verified page by page against -//! the old working image, with a recovery archive first and nothing changed on a mismatch. +//! the old working image, with a recovery archive first and nothing changed on a mismatch; +//! and schema-15 caches, whose batches lose the review conflict. -use notebook::{ConflictKind, EditStatus, Replica, Resolution}; -use onestore::{ExGuid, PageCreation, PageEdit, PreparedEdit, page::Page}; +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; + +use notebook::{EditStatus, Replica}; +use onestore::{ExGuid, PageCreation, PageEdit, op::SectionOp, page::Page}; use rusqlite::{Connection, params}; use serde_json::json; use std::path::Path; @@ -48,17 +52,17 @@ fn v14(path: &Path, base: &[u8], queue: &[Queued], working: Option<&[u8]>) -> Ve let (space, operation, next) = match entry { Queued::Page(space, after) => { let before = model_ops::page_of(&image, *space); - let next = PreparedEdit::page(&image, *space, after, "Author").unwrap(); + let next = ops::saved(&image, *space, after).unwrap(); ( *space, json!({"Page": {"before": before, "after": after, "author": "Author"}}), - next.as_bytes().to_vec(), + next.as_slice().to_vec(), ) } Queued::Create(creation) => ( creation.space(), json!({"CreatePage": creation}), - PreparedEdit::create_page(&image, creation) + ops::section_op(&image, SectionOp::Create(creation.clone())) .unwrap() .as_bytes() .to_vec(), @@ -71,7 +75,7 @@ fn v14(path: &Path, base: &[u8], queue: &[Queued], working: Option<&[u8]>) -> Ve ( root(&image), json!({"Pages": {"edits": batch, "observed": observed}}), - PreparedEdit::pages(&image, batch) + ops::section_op(&image, SectionOp::Pages(batch.to_vec())) .unwrap() .as_bytes() .to_vec(), @@ -80,7 +84,7 @@ fn v14(path: &Path, base: &[u8], queue: &[Queued], working: Option<&[u8]>) -> Ve Queued::Delete(spaces) => ( root(&image), json!({"DeletePages": spaces}), - PreparedEdit::delete_pages_permanently(&image, spaces) + ops::section_op(&image, SectionOp::Delete(spaces.to_vec())) .unwrap() .as_bytes() .to_vec(), @@ -187,9 +191,9 @@ fn a_schema_14_queue_converts_to_ops_that_reach_its_working_pages_and_publish() Queued::Page( a, appended( - PreparedEdit::page(SOURCE, a, &first, "Author") + ops::saved(SOURCE, a, &first) .unwrap() - .as_bytes(), + .as_slice(), a, a_text, " two", @@ -216,12 +220,12 @@ fn a_schema_14_queue_converts_to_ops_that_reach_its_working_pages_and_publish() } }) ); - let local = pages(&cache.snapshot().unwrap()); + let local = pages(&snapshot(&cache)); assert_eq!(local, pages(&working)); drop(cache); // A converted cache opens as it is. let cache = Replica::open(&path).unwrap(); - assert_eq!(pages(&cache.snapshot().unwrap()), local); + assert_eq!(pages(&snapshot(&cache)), local); let next = cache .apply( "Author", @@ -245,7 +249,7 @@ fn a_schema_14_queue_converts_to_ops_that_reach_its_working_pages_and_publish() Some((id, EditStatus::Published { .. })) if id == next )); let published = pages(&server.durable); - assert_eq!(published, pages(&cache.snapshot().unwrap())); + assert_eq!(published, pages(&snapshot(&cache))); assert!( published .iter() @@ -255,7 +259,7 @@ fn a_schema_14_queue_converts_to_ops_that_reach_its_working_pages_and_publish() } #[test] -fn an_uncertain_attempt_and_a_conflict_keep_their_states() { +fn an_uncertain_attempt_keeps_its_state_and_a_conflict_becomes_a_conflict_page() { let body = body_pages(SOURCE); let (a, a_text) = body[0]; let directory = tempfile::tempdir().unwrap(); @@ -263,9 +267,9 @@ fn an_uncertain_attempt_and_a_conflict_keep_their_states() { // The oldest edit was attempted: its schema-14 evidence names the revision it published. let path = directory.path().join("attempted.sqlite"); let after = appended(SOURCE, a, a_text, " attempted"); - let published = PreparedEdit::page(SOURCE, a, &after, "Author").unwrap(); + let published = ops::saved(SOURCE, a, &after).unwrap(); let revision = - onestore::RevisionIndex::parse(&onestore::Store::parse(published.as_bytes()).unwrap()) + onestore::RevisionIndex::parse(&onestore::Store::parse(published.as_slice()).unwrap()) .unwrap() .active(a) .unwrap(); @@ -286,7 +290,7 @@ fn an_uncertain_attempt_and_a_conflict_keep_their_states() { Some((2, awaiting)) ); assert_eq!(unchanged.publications, 0); - let mut landed = Server::new(published.as_bytes()); + let mut landed = Server::new(published.as_slice()); assert_eq!( cache.sync_once(&mut landed).unwrap().edit, Some((2, EditStatus::Published { revision })) @@ -294,17 +298,17 @@ fn an_uncertain_attempt_and_a_conflict_keep_their_states() { assert_eq!(landed.publications, 0); drop(cache); - // The oldest edit was in conflict: the conversion lowered it onto the remote page the - // conflict was recorded against, and keeping mine publishes it. + // The oldest edit was in conflict: the conversion keeps the remote page the conflict was + // recorded against and queues the local version as its conflict page. let path = directory.path().join("conflicted.sqlite"); - let remote = PreparedEdit::page(SOURCE, a, &appended(SOURCE, a, a_text, " remote"), "Native") + let remote = ops::saved(SOURCE, a, &appended(SOURCE, a, a_text, " remote")) .unwrap() - .as_bytes() + .as_slice() .to_vec(); let local = appended(SOURCE, a, a_text, " local"); - let working = PreparedEdit::page(SOURCE, a, &local, "Author") + let working = ops::saved(SOURCE, a, &local) .unwrap() - .as_bytes() + .as_slice() .to_vec(); { // Schema 14 stored the remote as the base and kept the local working image. @@ -324,27 +328,26 @@ fn an_uncertain_attempt_and_a_conflict_keep_their_states() { .unwrap(); } let cache = Replica::open(&path).unwrap(); + assert_eq!(cache.status(2).unwrap(), Some(EditStatus::Pending)); assert_eq!( - cache.status(2).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) - ); - assert_eq!( - model_ops::page_of(&cache.snapshot().unwrap(), a), - model_ops::page_of(&working, a) + model_ops::page_of(&snapshot(&cache), a), + model_ops::page_of(&remote, a) ); let mut server = Server::new(&remote); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((2, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - cache.resolve(2, Resolution::Mine).unwrap(); assert!(matches!( cache.sync_once(&mut server).unwrap().edit, Some((2, EditStatus::Published { .. })) )); assert_eq!( model_ops::page_of(&server.durable, a), - model_ops::page_of(&working, a) + model_ops::page_of(&remote, a) + ); + assert_eq!( + conflicts(&server.durable), + [( + a, + vec![("Author".to_owned(), page_texts(&model_ops::page_of(&working, a)))] + )] ); } @@ -355,9 +358,9 @@ fn a_page_the_conversion_cannot_reproduce_leaves_the_cache_untouched() { let directory = tempfile::tempdir().unwrap(); let path = directory.path().join("cache.sqlite"); // The working image holds a page the queued edit does not reach. - let stray = PreparedEdit::page(SOURCE, a, &appended(SOURCE, a, a_text, " stray"), "Author") + let stray = ops::saved(SOURCE, a, &appended(SOURCE, a, a_text, " stray")) .unwrap() - .as_bytes() + .as_slice() .to_vec(); v14( &path, @@ -382,3 +385,75 @@ fn a_page_the_conversion_cannot_reproduce_leaves_the_cache_untouched() { assert!(Replica::open(&path).is_err()); assert_eq!(std::fs::read(&path).unwrap(), before); } + +/// A schema-15 cache upgrades in place: its queue, receipts and recovery archives stay, and +/// a conflict it recorded is queued work the next sync keeps as a conflict page. +#[test] +fn a_schema_15_cache_upgrades_in_place_and_its_conflict_becomes_a_conflict_page() { + let (space, text) = body_pages(SOURCE)[0]; + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("cache.sqlite"); + let cache = Replica::create(&path, SOURCE).unwrap(); + let id = model_ops::save(&cache, text, |page| { + model_ops::replace_text(page, text, 0..0, "Local ") + }) + .unwrap() + .unwrap(); + let pending = cache.pending().unwrap(); + let archive = directory.path().join("before.sqlite"); + drop(cache); + { + // Schema 15 recorded the review conflict and its page on the batch. + let connection = Connection::open(&path).unwrap(); + connection + .execute_batch(&format!( + "PRAGMA foreign_keys=OFF; + BEGIN; + CREATE TABLE old ( + id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(id>0), + sealed TEXT, + revisions TEXT, + attempted INTEGER NOT NULL DEFAULT 0 CHECK(attempted IN (0,1)), + conflict INTEGER CHECK(conflict BETWEEN 0 AND 3), + space TEXT, + CHECK((sealed IS NULL) = (revisions IS NULL)), + CHECK(attempted=0 OR sealed IS NOT NULL), + CHECK((conflict IS NULL) = (space IS NULL)) + ) STRICT; + INSERT INTO old SELECT id, sealed, revisions, attempted, 3, '{space}' FROM batches; + DROP TABLE batches; + ALTER TABLE old RENAME TO batches; + PRAGMA user_version=15; + COMMIT;" + )) + .unwrap(); + } + let cache = Replica::open(&path).unwrap(); + assert_eq!(cache.pending().unwrap(), pending); + assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); + cache.export_recovery(&archive).unwrap(); + drop(cache); + let connection = Connection::open(&path).unwrap(); + let version: u32 = connection + .pragma_query_value(None, "user_version", |row| row.get(0)) + .unwrap(); + let columns: Vec = connection + .prepare("SELECT name FROM pragma_table_info('batches')") + .unwrap() + .query_map([], |row| row.get(0)) + .unwrap() + .collect::>() + .unwrap(); + assert_eq!(version, 16); + assert_eq!(columns, ["id", "sealed", "revisions", "attempted"]); + drop(connection); + assert_eq!(notebook::Recovery::open(&archive).unwrap().pending().unwrap(), pending); + let cache = Replica::open(&path).unwrap(); + let remote = typed(SOURCE, space, text, 0..0, "Remote "); + let mut server = Server::new(&remote); + assert!(matches!( + cache.sync_once(&mut server).unwrap().edit, + Some((_, EditStatus::Published { .. })) + )); + assert!(conflicted(&server.durable, space)); +} diff --git a/crates/notebook/tests/protected.rs b/crates/notebook/tests/protected.rs index fc929e2d4425a6f7d974fe03537a03b2f3ba4757..926222b59e1fe7bece32bb17de561b5a64cbd82a 100644 --- a/crates/notebook/tests/protected.rs +++ b/crates/notebook/tests/protected.rs @@ -28,8 +28,8 @@ fn a_locked_section_unlocks_for_reading() { )); } -/// An unlocked page is edited and saved under the section's key, then reads back with the -/// same password. `ONESTORE_PROTECTED_EXPORT` names a directory receiving the notebook for +/// An unlocked page is edited as ops stored under the section's key, then reads back with +/// the same password. `ONESTORE_PROTECTED_EXPORT` names a directory receiving the notebook for /// a cold reopen in OneNote. #[test] fn an_unlocked_page_is_saved_under_the_section_key() { @@ -59,6 +59,7 @@ fn an_unlocked_page_is_saved_under_the_section_key() { .clone(); let mut unlocked = notebook.unlock(§ion, password).unwrap(); let (space, mut page) = unlocked.pages[0].clone(); + let before = page.clone(); let paragraphs = page .objects .iter_mut() @@ -95,20 +96,28 @@ fn an_unlocked_page_is_saved_under_the_section_key() { format, )) .unwrap(); + let edit = onestore::op::Edit { + at: 134_000_000_000_000_000, + ops: onestore::op::lower_page(&before, &page) + .unwrap() + .into_iter() + .map(|op| onestore::op::Op::Page { space, op }) + .collect(), + }; assert!(matches!( - notebook.save_unlocked(§ion, "wrong", &mut unlocked, space, &page, "Rust"), + notebook.apply_unlocked(§ion, "wrong", &mut unlocked, "Rust", &edit), Err(notebook::Error::Protected( onestore::protected::Error::PasswordMismatch )) )); let mut stale = notebook.unlock(§ion, password).unwrap(); notebook - .save_unlocked(§ion, password, &mut unlocked, space, &page, "Rust") + .apply_unlocked(§ion, password, &mut unlocked, "Rust", &edit) .unwrap(); - // A view read before that save no longer matches the file. + // A view read before that edit no longer matches the file. assert!( notebook - .save_unlocked(§ion, password, &mut stale, space, &page, "Rust") + .apply_unlocked(§ion, password, &mut stale, "Rust", &edit) .is_err() ); let (_, stored) = notebook.unlock(§ion, password).unwrap().pages.remove(0); diff --git a/crates/notebook/tests/session.rs b/crates/notebook/tests/session.rs index 1f3800abc52273f37826eb2687a9ee06b76c0fdb..a58a6215634697375500e5554820c527fe3b2c61 100644 --- a/crates/notebook/tests/session.rs +++ b/crates/notebook/tests/session.rs @@ -1,8 +1,14 @@ +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; use notebook::{ - ConflictKind, EditStatus, Recovery, Resolution, - session::{Event, Notebook, QueuedEdit, Save, Section}, + EditStatus, Recovery, Resolution, + session::{Event, Notebook, Section}, +}; +use onestore::{ + ExGuid, + op::{Edit, Op, PageOp}, + page::Page, }; -use onestore::{ExGuid, PreparedEdit, page::Page}; use std::{ path::Path, sync::{ @@ -35,6 +41,21 @@ fn edited(page: &Page, text: &str) -> Page { after } +/// Replaces `range` of the page's first body text as the editor does: the edit's id once +/// it is durable. +fn typed(section: &Section, space: ExGuid, page: &Page, range: std::ops::Range, text: &str) -> u64 { + let op = PageOp::Text { + text: first_text(page), + range, + with: text.into(), + }; + let edit = Edit { + at: model_ops::now(), + ops: vec![Op::Page { space, op }], + }; + section.replica().apply("Editor", edit).unwrap() +} + fn open(file: &Path, cache: &Path) -> (Section, Arc) { let notified = Arc::new(AtomicUsize::new(0)); let counter = Arc::clone(¬ified); @@ -105,14 +126,8 @@ fn a_section_opens_through_its_replica_and_a_save_reaches_the_file_and_survives_ assert_eq!(pages.len(), 1); let space = pages[0].0; let before = section.page(space).unwrap(); - assert_eq!( - section.save(space, &before, &before, "Editor").unwrap(), - Save::Unchanged - ); let after = edited(&before, "Saved "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "Saved "); assert_same(section.page(space).unwrap(), &after); published(§ion, id); // The worker reports the step after writing its receipt. @@ -167,9 +182,7 @@ fn saves_wait_for_an_unreachable_file_and_publish_after_relaunch() { let permissions = std::fs::metadata(&file).unwrap().permissions(); std::fs::set_permissions(&file, std::fs::Permissions::from_mode(0o444)).unwrap(); let after = edited(&before, "Offline "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "Offline "); wait(§ion, |event| matches!(event, Event::Unreachable(_))); section.close().unwrap(); assert_eq!( @@ -198,7 +211,7 @@ fn saves_wait_for_an_unreachable_file_and_publish_after_relaunch() { } #[test] -fn an_external_change_refreshes_the_page_and_stales_a_save_from_the_old_model() { +fn an_external_change_reloads_the_page_and_later_edits_apply_to_it() { let directory = tempfile::tempdir().unwrap(); let file = directory.path().join("notes.one"); let cache = directory.path().join("cache"); @@ -214,7 +227,7 @@ fn an_external_change_refreshes_the_page_and_stales_a_save_from_the_old_model() let deadline = Instant::now() + Duration::from_secs(20); loop { let bytes = onestore::read_file(&file).unwrap(); - match PreparedEdit::page(&bytes, space, &native, "Native") + match ops::save(&bytes, space, &native) .unwrap() .commit_file(&file) { @@ -227,29 +240,18 @@ fn an_external_change_refreshes_the_page_and_stales_a_save_from_the_old_model() } } section.wake(); - let deadline = Instant::now() + Duration::from_secs(20); - while section.page(space).unwrap().title == before.title { - assert!(Instant::now() < deadline, "the refresh did not arrive"); - wait(§ion, |event| matches!(event, Event::Refreshed)); - } + wait(§ion, |event| matches!(event, Event::Changed(spaces) if spaces.contains(&space))); assert_same(section.page(space).unwrap(), &native); - let native = section.page(space).unwrap(); - let stale = edited(&before, "Local "); - assert_eq!( - section.save(space, &before, &stale, "Editor").unwrap(), - Save::Stale - ); + // An edit made on the page shown before the change applies to the page as it is now. + let id = typed(§ion, space, &before, 0..0, "Local "); let after = edited(&native, "Local "); - let Save::Queued(id) = section.save(space, &native, &after, "Editor").unwrap() else { - panic!() - }; published(§ion, id); assert_same(stored_page(&file, space), &after); section.close().unwrap(); } #[test] -fn queued_saves_continue_each_other_detect_stale_pages_and_finish_before_close() { +fn edits_apply_in_order_without_waiting_and_finish_before_close() { let directory = tempfile::tempdir().unwrap(); let file = directory.path().join("notes.one"); let cache = directory.path().join("cache"); @@ -260,36 +262,44 @@ fn queued_saves_continue_each_other_detect_stale_pages_and_finish_before_close() .unwrap(); let (section, _) = open(&file, &cache); let space = section.pages().unwrap()[0].0; - let mut before = section.page(space).unwrap(); + let original = section.page(space).unwrap(); + let text = first_text(&original); + let apply = |word: &str| { + let op = PageOp::Text { + text, + range: 0..0, + with: word.into(), + }; + let edit = Edit { + at: model_ops::now(), + ops: vec![Op::Page { space, op }], + }; + section.apply("Editor", edit).unwrap(); + }; + let mut expected = original.clone(); for word in ["one ", "two ", "three ", "four ", "five "] { - let after = edited(&before, word); - section - .queue_save(space, before, after.clone(), "Editor") - .unwrap(); - before = after; + apply(word); + expected = edited(&expected, word); } let deadline = Instant::now() + Duration::from_secs(20); loop { let mut stored = section.page(space).unwrap(); - stored.title = before.title.clone(); - if stored == before { + stored.title = expected.title.clone(); + if stored == expected { break; } - assert!(Instant::now() < deadline, "the saves did not arrive"); - for (_, save) in section.saved() { - assert!(matches!(save, Ok(Save::Queued(_))), "{save:?}"); - } + assert!(Instant::now() < deadline, "the edits did not arrive"); std::thread::sleep(Duration::from_millis(20)); } while !section.pending().unwrap().is_empty() { - assert!(Instant::now() < deadline, "the saves were not published"); + assert!(Instant::now() < deadline, "the edits were not published"); std::thread::sleep(Duration::from_millis(20)); } let native = edited(§ion.page(space).unwrap(), "Native "); let deadline = Instant::now() + Duration::from_secs(20); loop { let bytes = onestore::read_file(&file).unwrap(); - match PreparedEdit::page(&bytes, space, &native, "Native") + match ops::save(&bytes, space, &native) .unwrap() .commit_file(&file) { @@ -302,29 +312,11 @@ fn queued_saves_continue_each_other_detect_stale_pages_and_finish_before_close() } } section.wake(); - while !section.page(space).unwrap().objects.eq(&native.objects) { - assert!(Instant::now() < deadline, "the refresh did not arrive"); - wait(§ion, |event| matches!(event, Event::Refreshed)); - } - let stale = edited(&before, "Local "); - section.queue_save(space, before, stale, "Editor").unwrap(); - // Outcomes of the earlier saves may still be arriving. - while !section - .saved() - .into_iter() - .any(|saved| saved == (space, Ok(Save::Stale))) - { - assert!(Instant::now() < deadline, "the stale save was not reported"); - std::thread::sleep(Duration::from_millis(20)); - } - let native = section.page(space).unwrap(); - let after = edited(&native, "Local "); - section - .queue_save(space, native, after.clone(), "Editor") - .unwrap(); + wait(§ion, |event| matches!(event, Event::Changed(spaces) if spaces.contains(&space))); + apply("Local "); section.close().unwrap(); let (section, _) = open(&file, &cache); - assert_same(section.page(space).unwrap(), &after); + assert_same(section.page(space).unwrap(), &edited(&native, "Local ")); section.close().unwrap(); } @@ -426,9 +418,7 @@ fn an_absent_remote_does_not_prevent_local_relaunch_or_further_saves() { let space = section.pages().unwrap()[0].0; let before = section.page(space).unwrap(); let after = edited(&before, "First "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "First "); wait( §ion, |event| matches!(event, Event::Unreachable(error) if error.kind() == std::io::ErrorKind::NotFound), @@ -442,9 +432,7 @@ fn an_absent_remote_does_not_prevent_local_relaunch_or_further_saves() { assert_eq!(section.status(id).unwrap(), Some(EditStatus::Pending)); let before = section.page(space).unwrap(); let after = edited(&before, "Second "); - let Save::Queued(next) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let next = typed(§ion, space, &before, 0..0, "Second "); assert!(!file.exists()); let restored = directory.path().join("restored.one"); std::fs::write(&restored, &source).unwrap(); @@ -470,9 +458,7 @@ fn resuming_against_another_document_preserves_both_remote_and_pending_edit() { let space = section.pages().unwrap()[0].0; let before = section.page(space).unwrap(); let after = edited(&before, "Local "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "Local "); wait(§ion, |event| matches!(event, Event::Unreachable(_))); section.close().unwrap(); @@ -499,10 +485,7 @@ fn offline_save_process() { let section = Section::resume(root.join("absent.one"), replica, || {}).unwrap(); let space = section.pages().unwrap()[0].0; let before = section.page(space).unwrap(); - let after = edited(&before, "Durable "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "Durable "); std::fs::write(root.join("acknowledged"), id.to_string()).unwrap(); // Terminate without running Session or SQLite destructors after acknowledgement. std::process::exit(0); @@ -562,9 +545,7 @@ fn smb_connection_failure_keeps_the_session_locally_editable_and_retries() { let space = section.pages().unwrap()[0].0; let before = section.page(space).unwrap(); let after = edited(&before, "Offline SMB "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "Offline SMB "); section.wake(); wait( §ion, @@ -611,9 +592,7 @@ fn live_smb_session_save_publishes_and_reopens() { let space = section.pages().unwrap()[0].0; let before = section.page(space).unwrap(); let after = edited(&before, "Session SMB "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "Session SMB "); published(§ion, id); let remote = client.read("session.one", 1024 * 1024).unwrap(); assert_same(model_ops::page_of(&remote, space), &after); @@ -661,9 +640,7 @@ fn dropping_during_connection_keeps_cache_owned_until_the_worker_finishes() { let space = section.pages().unwrap()[0].0; let before = section.page(space).unwrap(); let after = edited(&before, "Saved during connection "); - let Save::Queued(id) = section.save(space, &before, &after, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..0, "Saved during connection "); let (dropped, finished) = mpsc::channel(); let owner = std::thread::spawn(move || { drop(section); @@ -692,14 +669,14 @@ fn dropping_during_connection_keeps_cache_owned_until_the_worker_finishes() { }; assert_eq!(replica.status(id).unwrap(), Some(EditStatus::Pending)); assert_same( - model_ops::page_of(&replica.snapshot().unwrap(), space), + model_ops::page_of(&server::snapshot(&replica), space), &after, ); assert!(connecting.try_recv().is_err()); } #[test] -fn a_conflicting_save_is_reviewed_against_the_remote_page_and_archived_for_recovery() { +fn a_conflicting_save_keeps_the_native_page_and_a_conflict_page_the_session_deletes() { let directory = tempfile::tempdir().unwrap(); let file = directory.path().join("notes.one"); let cache = directory.path().join("cache"); @@ -717,9 +694,7 @@ fn a_conflicting_save_is_reviewed_against_the_remote_page_and_archived_for_recov std::fs::set_permissions(&file, std::fs::Permissions::from_mode(0o444)).unwrap(); let mut local = before.clone(); model_ops::replace_text(&mut local, text, 0..8, "Local"); - let Save::Queued(id) = section.save(space, &before, &local, "Editor").unwrap() else { - panic!() - }; + let id = typed(§ion, space, &before, 0..8, "Local"); wait(§ion, |event| matches!(event, Event::Unreachable(_))); std::fs::set_permissions(&file, permissions).unwrap(); let mut native = before.clone(); @@ -727,7 +702,7 @@ fn a_conflicting_save_is_reviewed_against_the_remote_page_and_archived_for_recov let deadline = Instant::now() + Duration::from_secs(20); loop { let bytes = onestore::read_file(&file).unwrap(); - match PreparedEdit::page(&bytes, space, &native, "Native") + match ops::save(&bytes, space, &native) .unwrap() .commit_file(&file) { @@ -740,40 +715,38 @@ fn a_conflicting_save_is_reviewed_against_the_remote_page_and_archived_for_recov } } section.wake(); - wait( - §ion, - |event| matches!(event, Event::Attempt { id: n, status: EditStatus::Conflict(ConflictKind::ContentChanged) } if *n == id), - ); - let conflicts = section.conflicts().unwrap(); + wait(§ion, |event| { + matches!(event, Event::Attempt { id: n, status: EditStatus::Published { .. } } if *n > id) + }); + assert!(matches!( + section.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + // As OneNote does, the native version stays the page and the local one is kept beside it. + assert_same(section.page(space).unwrap(), &native); + assert_same(stored_page(&file, space), &native); + let listed = section.conflicts().unwrap(); + assert_eq!(listed.len(), 1); + assert_eq!(listed[0].0, space); + let conflict = &listed[0].1[0]; + assert_eq!(conflict.user, "Editor"); + let kept = section.page(conflict.space).unwrap(); assert_eq!( - conflicts, - [( - QueuedEdit { - id, - space, - status: EditStatus::Conflict(ConflictKind::ContentChanged) - }, - ConflictKind::ContentChanged - )] + model_ops::paragraph_with(&kept, first_text(&kept)) + .and_then(|paragraph| paragraph.text()) + .map(|text| text.text.text().to_owned()), + Some("Local".to_owned()) ); - assert_same(section.page(space).unwrap(), &local); - let remote = section.remote_page(space).unwrap(); - assert_same(remote.clone(), &native); - let archive = directory.path().join("review.sqlite"); - section.export_recovery(&archive).unwrap(); - let recovery = Recovery::open(&archive).unwrap(); - assert_eq!(recovery.pending().unwrap().len(), 1); assert_eq!( - recovery.status(id).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) + server::conflicts(&onestore::read_file(&file).unwrap())[0].1, + [("Editor".to_owned(), vec!["Local".to_owned()])] ); - let mut reviewed = remote.clone(); - model_ops::replace_text(&mut reviewed, text, 0..6, "Native and local"); - section.review(id, &reviewed).unwrap(); - assert_eq!(section.status(id).unwrap(), Some(EditStatus::Pending)); - published(§ion, id); - assert_same(stored_page(&file, space), &reviewed); - assert!(section.queue().unwrap().is_empty()); + // Merged by hand, the conflict page is deleted. + let deleted = section.delete_pages(&[conflict.space]).unwrap(); + published(§ion, deleted); + assert!(section.conflicts().unwrap().is_empty()); + assert!(server::conflicts(&onestore::read_file(&file).unwrap()).is_empty()); + assert!(section.pending().unwrap().is_empty()); section.close().unwrap(); } @@ -819,7 +792,7 @@ fn an_uncertain_attempt_is_released_after_restart_by_review() { ); assert_eq!( section - .queue() + .pending() .unwrap() .iter() .map(|edit| edit.id) @@ -850,7 +823,7 @@ fn an_uncertain_attempt_is_released_after_restart_by_review() { recovery.status(id).unwrap(), Some(EditStatus::AwaitingConfirmation { .. }) )); - assert!(section.queue().unwrap().is_empty()); + assert!(section.pending().unwrap().is_empty()); assert!( section .release( diff --git a/crates/notebook/tests/smb_queue.rs b/crates/notebook/tests/smb_queue.rs index c817c1a575b6689295ee1876334c876ad1d97995..4282840cdde547b398df649e169b3f53cb5ea192 100644 --- a/crates/notebook/tests/smb_queue.rs +++ b/crates/notebook/tests/smb_queue.rs @@ -17,6 +17,10 @@ use std::{ time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; +#[path = "support/server.rs"] +mod server; +use server::snapshot; + fn client() -> Client { Client::connect( &std::env::var("ONESTORE_SMB_LAB").unwrap(), @@ -49,15 +53,15 @@ impl Remote for Counted { self.reads += 1; self.remote.read() } - fn stamp(&mut self) -> io::Result> { + fn stamp(&mut self) -> io::Result { self.stamps += 1; self.remote.stamp() } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.remote.publish(transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.remote.confirm(snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.remote.confirm(base) } } @@ -140,7 +144,7 @@ fn live_keystrokes_publish_reading_only_the_header_and_a_native_change_merges() assert_eq!(remote.stamps, 20); assert_eq!( lab.read(&path, 1 << 24).unwrap(), - replica.snapshot().unwrap() + snapshot(&replica) ); for _ in 0..5 { assert_eq!(replica.sync_once(&mut remote).unwrap().edit, None); @@ -173,7 +177,7 @@ fn live_keystrokes_publish_reading_only_the_header_and_a_native_change_merges() assert!(merged.ends_with(" local"), "{merged}"); assert_eq!( lab.read(&path, 1 << 24).unwrap(), - replica.snapshot().unwrap() + snapshot(&replica) ); lab.delete(&path).unwrap(); } @@ -244,6 +248,6 @@ fn live_session_applies_edits_through_the_embedded_client_and_reopens() { section.close().unwrap(); let replica = Replica::open(&cache).unwrap(); assert_eq!(text_of(&replica, space, text), "0123456789Session"); - assert_eq!(replica.snapshot().unwrap(), published); + assert_eq!(snapshot(&replica), published); lab.delete(&path).unwrap(); } diff --git a/crates/notebook/tests/structure.rs b/crates/notebook/tests/structure.rs index 6274848d901a6ee49db5374bd193c0b9b0f6ae3d..79d684d23c9c67749c548746b0fc927000947265 100644 --- a/crates/notebook/tests/structure.rs +++ b/crates/notebook/tests/structure.rs @@ -53,13 +53,13 @@ fn sections_and_groups_are_created_renamed_coloured_ordered_and_deleted() { onestore::place_file(root.join("First.one"), toc_id, "First.one").unwrap(); let mut notebook = Notebook::open(&root, temporary.path().join("cache")).unwrap(); assert_eq!( - notebook.create_section("", "Second", "Author").unwrap(), + notebook.create_section("", "Second", &onestore::PageCreation::new(None, Some(""), "Author").unwrap()).unwrap(), "Second.one" ); assert_eq!(notebook.create_group("", "Archive").unwrap(), "Archive"); assert_eq!( notebook - .create_section("Archive", "Inner", "Author") + .create_section("Archive", "Inner", &onestore::PageCreation::new(None, Some(""), "Author").unwrap()) .unwrap(), "Archive/Inner.one" ); diff --git a/crates/notebook/tests/support/model_ops.rs b/crates/notebook/tests/support/model_ops.rs index 8e49e7c98c7fee9df4bc671a8be943a38214af54..5110dbd392412cac020f6986fdbcac9d542de9f5 100644 --- a/crates/notebook/tests/support/model_ops.rs +++ b/crates/notebook/tests/support/model_ops.rs @@ -287,17 +287,16 @@ pub fn save( text: ExGuid, edit: impl FnOnce(&mut Page), ) -> Result, notebook::Error> { - let (space, _) = locate(&cache.snapshot()?, text); - save_page(cache, space, edit) -} - -/// Resolves a conflict by taking the remote page, then queues `edit` of it. -pub fn review( - cache: &Replica, - id: u64, - text: ExGuid, - edit: impl FnOnce(&mut Page), -) -> Result, notebook::Error> { - cache.resolve(id, notebook::Resolution::Theirs)?; - save(cache, text, edit) + let mut space = None; + for (listed, ..) in cache.pages()? { + if paragraph_with(&cache.page(listed)?, text).is_some() { + space = Some(listed); + break; + } + } + save_page( + cache, + space.expect("text object belongs to an active page"), + edit, + ) } diff --git a/crates/notebook/tests/support/model_schedule.rs b/crates/notebook/tests/support/model_schedule.rs index b877ea01705cee00e27cdf0528ed010bb8ad0fa7..e019ef53a86ed6ceff186058d1d520819453e56c 100644 --- a/crates/notebook/tests/support/model_schedule.rs +++ b/crates/notebook/tests/support/model_schedule.rs @@ -1,16 +1,20 @@ //! A deterministic multi-actor schedule over page-model saves: several replicas edit one -//! page offline, publish through a fault-injecting remote, reopen, and review conflicts. +//! page offline, publish through a fault-injecting remote, reopen, and delete the conflict +//! pages the merges leave. use crate::model_ops::{self, AUTHOR}; -use crate::server::{Fault, Server}; -use notebook::{EditStatus, Remote, Replica, Resolution}; +use crate::server::{Fault, Server, remote_snapshot, snapshot}; +use notebook::{EditStatus, Remote, Replica}; use onestore::{ - CommitError, ExGuid, PreparedEdit, RevisionIndex, Store, Transaction, + CommitError, ExGuid, RevisionIndex, Stamp, Store, Transaction, document::Document, page::{Outline, Page, PageObject}, }; use std::{io, sync::LazyLock}; +#[path = "../../../onestore/tests/support/ops.rs"] +mod ops; + static SOURCE: LazyLock<(Vec, ExGuid)> = LazyLock::new(|| { let source = onestore::create_section("model-schedule.one", "Original 🦀 é 0", "Author").unwrap(); @@ -23,9 +27,9 @@ static SOURCE: LazyLock<(Vec, ExGuid)> = LazyLock::new(|| { for at in 1..8 { anchor = model_ops::insert_after(&mut page, anchor, &format!("Original 🦀 é {at}")).1; } - let source = PreparedEdit::page(&source, space, &page, "Author") + let source = ops::saved(&source, space, &page) .unwrap() - .as_bytes() + .as_slice() .to_vec(); (source, space) }); @@ -139,6 +143,10 @@ impl Remote for Session<'_> { self.server.read() } + fn stamp(&mut self) -> io::Result { + self.server.stamp() + } + fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { assert!(!self.retired, "a retired attempt was replayed"); self.publications += 1; @@ -146,8 +154,8 @@ impl Remote for Session<'_> { self.server.publish(transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.server.confirm(snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.server.confirm(base) } } @@ -165,11 +173,17 @@ fn statuses(cache: &Replica) -> Vec<(u64, Option)> { .collect() } -/// Fails unless the durable remote holds `revision` in the edited page's space. +/// Fails unless the durable remote holds `revision`: in the edited page's space, or, for +/// the edit adding a conflict page, in the space it changed first. fn durable(server: &Server, revision: ExGuid) { let store = Store::parse(&server.durable).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); - assert!(index.spaces[&SOURCE.1].revisions.contains_key(&revision)); + assert!( + index + .spaces + .values() + .any(|space| space.revisions.contains_key(&revision)) + ); } pub fn run(input: &[u8]) { @@ -181,10 +195,10 @@ pub fn run(input: &[u8]) { let actor = usize::from(step[0]) % replicas.len(); let path = directory.path().join(format!("{actor}.sqlite")); let cache = replicas[actor].get_or_insert_with(|| Replica::create(&path, source).unwrap()); - let snapshot = cache.snapshot().unwrap(); + let image = snapshot(cache); let before = local(cache); let pending = cache.pending().unwrap(); - let rows_now = rows(&snapshot); + let rows_now = rows(&image); match step[1] % 8 { 0..=2 => { let row = rows_now[usize::from(step[2]) % rows_now.len()].clone(); @@ -266,12 +280,17 @@ pub fn run(input: &[u8]) { }; server.fault = Fault::None; let next = cache.pending().unwrap(); - // Edits leave the queue oldest first, each with a durable receipt. - let left = pending.len() - next.len(); - assert!( - next.iter() - .all(|edit| pending[left..].iter().any(|kept| kept.id == edit.id)) - ); + // Edits leave the queue oldest first, each with a durable receipt; a rebase + // queues the conflict pages it makes after them. + let queued = |edit: ¬ebook::PendingEdit| pending.iter().any(|kept| kept.id == edit.id); + let left = pending.len() - next.iter().filter(|edit| queued(edit)).count(); + assert!(next.iter().all(|edit| { + pending[left..].iter().any(|kept| kept.id == edit.id) + || matches!( + &edit.edit.ops[..], + [onestore::op::Op::Section(onestore::op::SectionOp::Conflict { .. })] + ) + })); for edit in &pending[..left] { let Some(EditStatus::Published { revision }) = cache.status(edit.id).unwrap() else { @@ -283,7 +302,7 @@ pub fn run(input: &[u8]) { assert_eq!( local(cache), shape(&model_ops::page_of( - &cache.remote_snapshot().unwrap(), + &remote_snapshot(cache), *space )) ); @@ -301,31 +320,41 @@ pub fn run(input: &[u8]) { &char::from(b'a' + step[3] % 26).to_string(), ); let visible = server.visible.clone(); - PreparedEdit::page(&visible, *space, &page, "Remote") + ops::save(&visible, *space, &page) .unwrap() .commit(&mut server) .unwrap(); } } 6 => { - if let Some(conflict) = cache.conflict().unwrap() { - let keep = if step[2] & 1 == 0 { - Resolution::Mine - } else { - Resolution::Theirs - }; - let remote = model_ops::page_of(&cache.remote_snapshot().unwrap(), *space); - cache.resolve(conflict.id, keep).unwrap(); - match cache.conflict().unwrap() { - Some(next) => assert_ne!(next.space, conflict.space), - None => assert_eq!( - local(cache), - match keep { - Resolution::Mine => before.clone(), - Resolution::Theirs => shape(&remote), - } - ), - } + // The writer merged a conflict page's version by hand and deletes it. + let listed = cache.conflicts().unwrap(); + if let Some(conflict) = listed + .iter() + .find(|(page, _)| page == space) + .map(|(_, pages)| pages[usize::from(step[2]) % pages.len()].space) + { + let delete = onestore::op::Op::Section(onestore::op::SectionOp::Delete(vec![ + conflict, + ])); + cache + .apply( + AUTHOR, + onestore::op::Edit { + at: model_ops::now(), + ops: vec![delete], + }, + ) + .unwrap(); + assert!( + cache + .conflicts() + .unwrap() + .iter() + .flat_map(|(_, pages)| pages) + .all(|page| page.space != conflict) + ); + assert_eq!(local(cache), before); let (result, publications) = { let mut session = Session { retired: false, @@ -356,6 +385,6 @@ pub fn run(input: &[u8]) { } } rows(&server.durable); - rows(&replicas[actor].as_ref().unwrap().snapshot().unwrap()); + rows(&snapshot(replicas[actor].as_ref().unwrap())); } } diff --git a/crates/notebook/tests/support/page_schedule.rs b/crates/notebook/tests/support/page_schedule.rs index 76f6ac49a40e1b22c5e5a63696e05718da051c53..b01d6af2d69bb66f20d70eaa47627df58cb3851b 100644 --- a/crates/notebook/tests/support/page_schedule.rs +++ b/crates/notebook/tests/support/page_schedule.rs @@ -1,8 +1,12 @@ -use crate::{disk, model_ops}; -use notebook::{EditStatus, Remote, Replica, Resolution}; +use crate::{ + disk, model_ops, + server::{remote_snapshot, snapshot}, +}; +use notebook::{Remote, Replica}; use onestore::{ - CommitError, ExGuid, PageEdit, PreparedEdit, RevisionIndex, Store, Transaction, + CommitError, ExGuid, PageEdit, RevisionIndex, Stamp, Store, Transaction, document::{Document, Format, Kind, Layout}, + op::SectionOp, page::{ Outline, Page, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, text::new_id, @@ -10,6 +14,9 @@ use onestore::{ }; use std::{io, sync::LazyLock}; +#[path = "../../../onestore/tests/support/ops.rs"] +mod ops; + const BODY: &str = "Body 🦋 é"; #[path = "../../../onestore/tests/support/current.rs"] @@ -19,7 +26,7 @@ static SOURCE: LazyLock> = LazyLock::new(|| { let mut source = onestore::create_section("page-schedule.one", "Original", "Author").unwrap(); for _ in 0..3 { let page = onestore::PageCreation::new(None, Some("Same title"), "Author").unwrap(); - source = PreparedEdit::create_page(&source, &page) + source = ops::section_op(&source, SectionOp::Create(page.clone())) .unwrap() .as_bytes() .to_vec(); @@ -107,6 +114,10 @@ impl Remote for Session<'_> { Ok(self.disk.visible.clone()) } + fn stamp(&mut self) -> io::Result { + Stamp::of(&self.disk.visible).map_err(io::Error::other) + } + fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { let mut image = self.disk.visible.clone(); transaction.apply(&mut image).unwrap(); @@ -121,8 +132,8 @@ impl Remote for Session<'_> { result } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - onestore::confirm_snapshot(self.disk, snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + onestore::confirm(self.disk, base) } } @@ -152,8 +163,8 @@ pub fn run(input: &[u8]) { let actor = usize::from(step[0]) % replicas.len(); let path = directory.path().join(format!("{actor}.sqlite")); let cache = replicas[actor].get_or_insert_with(|| Replica::create(&path, &SOURCE).unwrap()); - let snapshot = cache.snapshot().unwrap(); - let listed = pages(&snapshot); + let image = snapshot(cache); + let listed = pages(&image); let pending = cache.pending().unwrap(); match step[1] % 8 { 0 | 1 => { @@ -183,7 +194,7 @@ pub fn run(input: &[u8]) { .collect(); replicas[actor] = None; let cache = Replica::open(&path).unwrap(); - assert_eq!(pages(&cache.snapshot().unwrap()), listed); + assert_eq!(pages(&snapshot(&cache)), listed); assert_eq!(cache.pending().unwrap(), pending); assert_eq!( pending @@ -200,24 +211,9 @@ pub fn run(input: &[u8]) { disk.fail_at = (step[2] != 0).then_some(usize::from(u16::from_le_bytes([step[2], step[3]]))); disk.random = u64::from(step[4]) + 1; - let prior = pending - .first() - .and_then(|edit| cache.status(edit.id).unwrap()); let result = cache.sync_once(&mut Session { disk: &mut disk }); - if matches!(prior, Some(EditStatus::AwaitingConfirmation { .. })) { - assert!( - result.is_err() - || !matches!( - result.as_ref().unwrap().edit, - Some((_, EditStatus::Conflict(_))) - ) - ); - } if result.is_ok() && cache.pending().unwrap().is_empty() { - assert_eq!( - pages(&cache.snapshot().unwrap()), - pages(&cache.remote_snapshot().unwrap()) - ); + assert_eq!(pages(&snapshot(cache)), pages(&remote_snapshot(cache))); } } 5 => { @@ -244,36 +240,34 @@ pub fn run(input: &[u8]) { } }) .collect(); - let expected = PreparedEdit::pages(&snapshot, &edits); + let expected = ops::section_op(&image, SectionOp::Pages(edits.to_vec())); match section_op(cache, onestore::op::SectionOp::Pages(edits)) { Ok(_) => assert_eq!( - pages(&cache.snapshot().unwrap()), + pages(&snapshot(cache)), pages(expected.unwrap().as_bytes()) ), Err(_) => { assert!(expected.is_err()); - assert_eq!(pages(&cache.snapshot().unwrap()), listed); + assert_eq!(pages(&snapshot(cache)), listed); assert_eq!(cache.pending().unwrap(), pending); } } } 7 => { - if let Some(conflict) = cache.conflict().unwrap() { - // Taking theirs for the page list would drop the pages this actor made. - let root = listed.iter().all(|(sid, ..)| *sid != conflict.space); - let keep = if root || step[2] & 1 == 0 { - Resolution::Mine - } else { - Resolution::Theirs - }; - if cache.resolve(conflict.id, keep).is_err() { - assert_eq!(cache.pending().unwrap(), pending); - } + // The actor merged a conflict page's version by hand and deletes it. + if let Some(conflict) = cache + .conflicts() + .unwrap() + .first() + .map(|(_, pages)| pages[usize::from(step[2]) % pages.len()].space) + { + section_op(cache, onestore::op::SectionOp::Delete(vec![conflict])).unwrap(); + assert_eq!(pages(&snapshot(cache)), listed); } } _ => unreachable!(), } - let local = pages(&replicas[actor].as_ref().unwrap().snapshot().unwrap()); + let local = pages(&snapshot(replicas[actor].as_ref().unwrap())); for page in &owned[actor] { assert!(local.iter().any(|(sid, oid, _)| (*sid, *oid) == *page)); } diff --git a/crates/notebook/tests/support/server.rs b/crates/notebook/tests/support/server.rs index 199421ab5489b423a522c6ab13d7ebf61608df59..a9b329189d4df13e19d7bd2c055e04fef8bce051 100644 --- a/crates/notebook/tests/support/server.rs +++ b/crates/notebook/tests/support/server.rs @@ -75,8 +75,8 @@ impl Remote for Server { fn read(&mut self) -> io::Result> { Ok(self.visible.clone()) } - fn stamp(&mut self) -> io::Result> { - Ok(Stamp::of(&self.visible).ok()) + fn stamp(&mut self) -> io::Result { + Stamp::of(&self.visible).map_err(io::Error::other) } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.publications += 1; @@ -99,13 +99,13 @@ impl Remote for Server { _ => Ok(()), } } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { self.confirmations += 1; if matches!(self.fault, Fault::Confirm) { self.fault = Fault::None; return Err(failure(CommitState::Unknown)); } - onestore::confirm_snapshot(self, snapshot)?; + onestore::confirm(self, base)?; if matches!(self.fault, Fault::ConfirmCommitted) { self.fault = Fault::None; return Err(failure(CommitState::Committed)); @@ -165,3 +165,100 @@ pub fn section_op(cache: ¬ebook::Replica, op: onestore::op::SectionOp) -> u64 ) .unwrap() } + +/// The image the queue leaves, as a recovery archive of the cache records it. +pub fn snapshot(cache: ¬ebook::Replica) -> Vec { + archived(cache, |recovery| recovery.snapshot()) +} + +/// The remote image the cache last observed, as a recovery archive records it. +pub fn remote_snapshot(cache: ¬ebook::Replica) -> Vec { + archived(cache, |recovery| recovery.remote_snapshot()) +} + +fn archived( + cache: ¬ebook::Replica, + image: impl FnOnce(¬ebook::Recovery) -> Result, notebook::Error>, +) -> Vec { + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("recovery.sqlite"); + cache.export_recovery(&path).unwrap(); + image(¬ebook::Recovery::open(&path).unwrap()).unwrap() +} + +/// `image` after another writer replaces `range` of the text object `text` with `with`. +pub fn typed( + image: &[u8], + space: ExGuid, + text: ExGuid, + range: std::ops::Range, + with: &str, +) -> Vec { + let op = onestore::op::PageOp::Text { + text, + range, + with: with.into(), + }; + edited(image, vec![onestore::op::Op::Page { space, op }]).unwrap() +} + +/// `image` after another writer's `ops`, sealed as one revision per space they change. +pub fn edited(image: &[u8], ops: Vec) -> Result, onestore::op::OpError> { + let arena = onestore::Arena::default(); + let mut section = + onestore::Section::open(&arena, image.to_vec()).map_err(onestore::op::OpError::Failed)?; + let edit = onestore::op::Edit { + at: 134_000_000_000_000_000, + ops, + }; + section.apply("Other writer", &edit)?; + section.seal().map_err(onestore::op::OpError::Failed)?; + Ok(section.image()) +} + +/// A conflict page as the tests compare it: whose version it is and its texts. +pub type Version = (String, Vec); + +/// Each page's conflict pages in list order. +pub fn conflicts(image: &[u8]) -> Vec<(ExGuid, Vec)> { + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, image.to_vec()).unwrap(); + section + .conflicts() + .unwrap() + .into_iter() + .map(|(page, conflicts)| { + let conflicts = conflicts + .into_iter() + .map(|conflict| { + let page = section.page(conflict.space).unwrap(); + (conflict.user, page_texts(&page)) + }) + .collect(); + (page, conflicts) + }) + .collect() +} + +/// The texts of a page's outlines and title, in page order. +pub fn page_texts(page: &onestore::page::Page) -> Vec { + use onestore::page::PageObject; + page.objects + .iter() + .flat_map(|object| match object { + PageObject::Outline(outline) => outline.paragraphs.clone(), + PageObject::Title(title) => title + .outlines + .iter() + .flat_map(|outline| outline.paragraphs.clone()) + .collect(), + _ => Vec::new(), + }) + .filter_map(|paragraph| Some(paragraph.text()?.text.text().to_owned())) + .collect() +} + +/// Whether `image` lists a conflict page under page `space`. +pub fn conflicted(image: &[u8], space: ExGuid) -> bool { + conflicts(image).iter().any(|(page, _)| *page == space) +} diff --git a/crates/notebook/tests/sync.rs b/crates/notebook/tests/sync.rs index 581864cfcea245531af577d10627ad55a54e1988..bfc7d5dfa34d8b4f6a5b2d3ce866f079f6f922bf 100644 --- a/crates/notebook/tests/sync.rs +++ b/crates/notebook/tests/sync.rs @@ -1,6 +1,6 @@ -//! Replica protocol: durable receipts, retired attempts, conflict resolution and contention. +//! Replica protocol: durable receipts, retired attempts, conflict pages and contention. -use notebook::{ConflictKind, EditStatus, Error, Remote, Replica, Resolution}; +use notebook::{EditStatus, Error, Remote, Replica}; use onestore::{CommitError, CommitState, ExGuid, Transaction}; use std::{io, ops::Range}; @@ -29,14 +29,14 @@ fn an_unchanged_stamp_publishes_and_settles_without_reading_the_remote() { self.reads += 1; self.server.read() } - fn stamp(&mut self) -> io::Result> { + fn stamp(&mut self) -> io::Result { self.server.stamp() } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.server.publish(transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.server.confirm(snapshot) + fn confirm(&mut self, base: &onestore::Stamp) -> Result<(), CommitError> { + self.server.confirm(base) } } let directory = tempfile::tempdir().unwrap(); @@ -56,11 +56,11 @@ fn an_unchanged_stamp_publishes_and_settles_without_reading_the_remote() { assert_eq!(remote.reads, 0); // Another writer's commit moves the header, so the next step reads the file. let native = - onestore::replace_text(&remote.server.visible, sid, object, 0..0, "Native ").unwrap(); + typed(&remote.server.visible, sid, object, 0..0, "Native "); remote.server.visible.clone_from(&native); assert_eq!(cache.sync_once(&mut remote).unwrap().edit, None); assert_eq!(remote.reads, 1); - assert_eq!(cache.snapshot().unwrap(), native); + assert_eq!(snapshot(&cache), native); } #[test] @@ -89,14 +89,13 @@ fn recovery_archive_preserves_the_queue_uncertainty_and_receipts_without_becomin let created = onestore::PageCreation::new(None, Some("Recovery 🦀"), "Fixture").unwrap(); section_op(&cache, onestore::op::SectionOp::Create(created)); save(&cache, object, 0..0, "Last ").unwrap(); - let working = cache.snapshot().unwrap(); - let remote = cache.remote_snapshot().unwrap(); + let working = snapshot(&cache); + let remote = remote_snapshot(&cache); let pending = cache.pending().unwrap(); let uncertain = cache.status(attempted).unwrap(); let source_file = std::fs::read(&path).unwrap(); let summary = RecoverySummary { queued_edits: 4, - conflicts: 0, uncertain_edits: 1, published_receipts: 1, base_bytes: remote.len() as u64, @@ -173,28 +172,21 @@ fn recovery_archive_preserves_the_queue_uncertainty_and_receipts_without_becomin } #[test] -fn recovery_archive_retains_conflict_images_and_rejects_foreign_or_future_archives() { +fn recovery_archive_retains_the_queue_and_rejects_foreign_or_future_archives() { use notebook::Recovery; let directory = tempfile::tempdir().unwrap(); let source = onestore::create_section("recovery.one", "Original", "Fixture").unwrap(); - let (space, object, _) = text(&source); + let (_, object, _) = text(&source); let cache = Replica::create(directory.path().join("live.sqlite"), &source).unwrap(); let id = save(&cache, object, 0..8, "Local").unwrap(); - let changed = onestore::replace_text(&source, space, object, 0..8, "Remote").unwrap(); - let mut server = Server::new(&changed); - let outcome = cache.sync_once(&mut server).unwrap().edit.unwrap(); - assert_eq!( - outcome, - (id, EditStatus::Conflict(ConflictKind::ContentChanged)) - ); - let path = directory.path().join("conflict.sqlite"); + let path = directory.path().join("queued.sqlite"); cache.export_recovery(&path).unwrap(); let archive = Recovery::open(&path).unwrap(); assert_eq!(text(&archive.snapshot().unwrap()).2, "Local"); - assert_eq!(archive.remote_snapshot().unwrap(), changed); - assert_eq!(archive.status(id).unwrap(), Some(outcome.1.clone())); - assert_eq!(archive.summary().unwrap().conflicts, 1); + assert_eq!(archive.remote_snapshot().unwrap(), source); + assert_eq!(archive.status(id).unwrap(), Some(EditStatus::Pending)); + assert_eq!(archive.summary().unwrap().queued_edits, 1); assert_eq!(archive.summary().unwrap().uncertain_edits, 0); drop(archive); for sql in [ @@ -208,8 +200,8 @@ fn recovery_archive_retains_conflict_images_and_rejects_foreign_or_future_archiv assert!(Recovery::open(&path).is_err()); assert_eq!(std::fs::read(&path).unwrap(), before); } - assert_eq!(cache.status(id).unwrap(), Some(outcome.1.clone())); - assert_eq!(text(&cache.snapshot().unwrap()).2, "Local"); + assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); + assert_eq!(text(&snapshot(&cache)).2, "Local"); } #[test] @@ -220,13 +212,13 @@ fn disjoint_remote_changes_merge_and_persist_the_remote_receipt() { let (sid, oid, _) = text(&source); let cache = Replica::create(&path, &source).unwrap(); let id = save(&cache, oid, 2..4, "🐈").unwrap(); - let remote = onestore::replace_text(&source, sid, oid, 0..6, "Xab🦀cYd").unwrap(); + let remote = typed(&source, sid, oid, 0..6, "Xab🦀cYd"); let mut server = Server::new(&remote); let outcome = cache.sync_once(&mut server).unwrap().edit.unwrap(); assert_eq!(outcome.0, id); assert!(matches!(outcome.1, EditStatus::Published { .. })); assert_eq!(text(&server.durable).2, "Xab🐈cYd"); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); assert!(cache.pending().unwrap().is_empty()); drop(cache); let cache = Replica::open(&path).unwrap(); @@ -238,31 +230,38 @@ fn disjoint_remote_changes_merge_and_persist_the_remote_receipt() { assert!(next > id); } +/// As OneNote 2010 does, the remote's version stays the page and the local one becomes a +/// conflict page under it, published with the rest of the queue. #[test] -fn overlapping_changes_preserve_both_images_and_survive_restart() { +fn overlapping_changes_keep_both_versions_and_survive_restart() { let dir = tempfile::tempdir().unwrap(); let path = dir.path().join("cache.sqlite"); let source = onestore::create_section("sync.one", "abc", "Fixture").unwrap(); let (sid, oid, _) = text(&source); let cache = Replica::create(&path, &source).unwrap(); let id = save(&cache, oid, 1..2, "L").unwrap(); - let local = cache.snapshot().unwrap(); - let remote = onestore::replace_text(&source, sid, oid, 1..2, "R").unwrap(); + let remote = typed(&source, sid, oid, 1..2, "R"); let mut server = Server::new(&remote); - assert_eq!( + assert!(matches!( cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) + Some((newest, EditStatus::Published { .. })) if newest > id + )); + assert_eq!(server.publications, 1); + assert_eq!(content(&server.durable, oid), "aRc"); + assert_eq!( + conflicts(&server.durable), + [(sid, vec![(model_ops::AUTHOR.to_owned(), vec!["aLc".to_owned()])])] ); - assert_eq!(server.publications, 0); + assert_eq!(snapshot(&cache), server.durable); + assert!(cache.pending().unwrap().is_empty()); drop(cache); let cache = Replica::open(&path).unwrap(); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); - assert_eq!(cache.remote_snapshot().unwrap(), remote); - assert_eq!(cache.pending().unwrap().len(), 1); - assert_eq!( + assert!(matches!( cache.status(id).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) - ); + Some(EditStatus::Published { .. }) + )); + assert_eq!(cache.conflicts().unwrap()[0].0, sid); + assert_eq!(cache.sync_once(&mut server).unwrap().edit, None); } #[test] @@ -452,16 +451,19 @@ fn twelve_local_editors_progress_during_remote_reads_publication_and_confirmatio } impl Remote for Paused { fn read(&mut self) -> io::Result> { + self.server.read() + } + fn stamp(&mut self) -> io::Result { self.wait("read"); - self.server.read() + self.server.stamp() } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.wait("publish"); self.server.publish(transaction) } - fn confirm(&mut self, source: &[u8]) -> Result<(), CommitError> { + fn confirm(&mut self, base: &onestore::Stamp) -> Result<(), CommitError> { self.wait("confirm"); - self.server.confirm(source) + self.server.confirm(base) } } for phase in ["read", "publish", "confirm"] { @@ -505,9 +507,6 @@ fn twelve_local_editors_progress_during_remote_reads_publication_and_confirmatio assert!( matches!(cache.sync_once(&mut Server::new(&source)),Err(Error::Io(error)) if error.kind()==io::ErrorKind::WouldBlock) ); - assert!( - matches!(cache.resolve(first, Resolution::Theirs), Err(Error::Io(error)) if error.kind() == io::ErrorKind::WouldBlock) - ); let started = std::time::Instant::now(); let handles: Vec<_> = (0..12) .map(|writer| { @@ -550,7 +549,7 @@ fn twelve_local_editors_progress_during_remote_reads_publication_and_confirmatio cache.pending().unwrap().len(), if phase == "read" { 0 } else { 12 } ); - let expected = text(&cache.snapshot().unwrap()).2; + let expected = text(&snapshot(&cache)).2; for writer in 0..12 { assert_eq!(expected.matches(&format!("[{writer}] ")).count(), 1); } @@ -562,7 +561,7 @@ fn twelve_local_editors_progress_during_remote_reads_publication_and_confirmatio } assert_eq!(server.publications, if phase == "read" { 1 } else { 2 }); assert_eq!(text(&server.durable).2, expected); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); } } @@ -578,12 +577,12 @@ fn unrelated_remote_files_never_replace_a_local_cache() { if pending { save(&cache, oid, 0..0, "Local ").unwrap(); } - let before = pages(&cache.snapshot().unwrap()); + let before = pages(&snapshot(&cache)); assert!( matches!(cache.sync_once(&mut server),Err(Error::Io(error)) if error.kind()==io::ErrorKind::InvalidInput) ); - assert_eq!(pages(&cache.snapshot().unwrap()), before); - assert_eq!(cache.remote_snapshot().unwrap(), source); + assert_eq!(pages(&snapshot(&cache)), before); + assert_eq!(remote_snapshot(&cache), source); assert_eq!(server.publications, 0); } } @@ -600,165 +599,76 @@ fn content(bytes: &[u8], text: ExGuid) -> String { .to_owned() } +/// A conflict never holds the queue: later edits of the page apply to the remote's version. #[test] -fn a_conflict_holds_later_edits_until_resolved_either_way_and_survives_reopen() { - for resolution in [Resolution::Mine, Resolution::Theirs] { - let dir = tempfile::tempdir().unwrap(); - let path = dir.path().join("cache.sqlite"); - let source = onestore::create_section("resolve.one", "abc", "Fixture").unwrap(); - let (sid, oid, _) = text(&source); - let cache = Replica::create(&path, &source).unwrap(); - let first = save(&cache, oid, 1..2, "L").unwrap(); - let remote = onestore::replace_text(&source, sid, oid, 0..3, "aRcZ").unwrap(); - let mut server = Server::new(&remote); - let conflict = EditStatus::Conflict(ConflictKind::ContentChanged); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((first, conflict.clone())) - ); - let mut dependent = None; - for n in 0..12 { - dependent = save(&cache, oid, 0..0, &format!("[{n}] ")); - } - let dependent = dependent.unwrap(); - assert_ne!(dependent, first); - assert_eq!(cache.status(dependent).unwrap(), Some(conflict.clone())); - let local = content(&cache.snapshot().unwrap(), oid); - assert!(local.ends_with("[0] aLc"), "{local}"); - let pending = cache.pending().unwrap(); - assert_eq!(pending.len(), 13); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((dependent, conflict.clone())), - "an unchanged remote leaves the conflict as it was" - ); - assert_eq!(server.publications, 0); - drop(cache); - let cache = Replica::open(&path).unwrap(); - assert_eq!(cache.pending().unwrap(), pending); - assert_eq!(content(&cache.snapshot().unwrap(), oid), local); - assert_eq!( - cache.conflict().unwrap(), - Some(notebook::Conflict { - id: dependent, - space: sid, - kind: ConflictKind::ContentChanged - }) - ); - cache.resolve(first, resolution).unwrap(); - assert_eq!(cache.conflict().unwrap(), None); - assert_eq!(cache.remote_snapshot().unwrap(), remote); - let expected = match resolution { - Resolution::Mine => local.clone(), - Resolution::Theirs => "aRcZ".to_owned(), - }; - assert_eq!(content(&cache.snapshot().unwrap(), oid), expected); - drop(cache); - let cache = Replica::open(&path).unwrap(); - match resolution { - Resolution::Mine => { - assert!(matches!( - cache.sync_once(&mut server).unwrap().edit, - Some((_, EditStatus::Published { .. })) - )); - assert_eq!(server.publications, 1); - for edit in &pending { - assert!(matches!( - cache.status(edit.id).unwrap(), - None | Some(EditStatus::Published { .. }) - )); - } - assert!(matches!( - cache.status(first).unwrap(), - Some(EditStatus::Published { .. }) - )); - } - Resolution::Theirs => { - // The emptied edits are acknowledged once the remote image is flushed. - assert!(matches!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Published { .. })) if id == dependent - )); - assert_eq!((server.publications, server.confirmations), (0, 1)); - assert!(matches!( - cache.status(first).unwrap(), - Some(EditStatus::Published { .. }) - )); - } - } - assert_eq!(content(&server.durable, oid), expected); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&server.durable)); - assert!(cache.pending().unwrap().is_empty()); +fn edits_after_a_conflict_apply_to_the_remote_version_and_survive_reopen() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("cache.sqlite"); + let source = onestore::create_section("resolve.one", "abc", "Fixture").unwrap(); + let (sid, oid, _) = text(&source); + let cache = Replica::create(&path, &source).unwrap(); + save(&cache, oid, 1..2, "L").unwrap(); + let remote = typed(&source, sid, oid, 0..3, "aRcZ"); + let mut server = Server::new(&remote); + assert!(matches!( + cache.sync_once(&mut server).unwrap().edit, + Some((_, EditStatus::Published { .. })) + )); + assert_eq!(content(&snapshot(&cache), oid), "aRcZ"); + let mut expected = "aRcZ".to_owned(); + for n in 0..12 { + save(&cache, oid, 0..0, &format!("[{n}] ")).unwrap(); + expected.insert_str(0, &format!("[{n}] ")); } -} - -#[test] -fn resolution_refuses_edits_that_are_not_in_conflict() { - let dir = tempfile::tempdir().unwrap(); - let source = onestore::create_section("resolve.one", "abc", "Fixture").unwrap(); - let (sid, oid, _) = text(&source); - let cache = Replica::create(dir.path().join("cache.sqlite"), &source).unwrap(); - let id = save(&cache, oid, 1..2, "L").unwrap(); - let refused = |cache: &Replica, id| matches!(cache.resolve(id, Resolution::Mine), Err(Error::Io(error)) if error.kind() == io::ErrorKind::InvalidInput); - assert!(refused(&cache, id)); - let remote = onestore::replace_text(&source, sid, oid, 0..3, "🦀Rc").unwrap(); - let mut server = Server::new(&remote); - assert!(matches!( - cache.sync_once(&mut server).unwrap().edit, - Some((_, EditStatus::Conflict(_))) - )); let pending = cache.pending().unwrap(); - assert!(refused(&cache, id + 1)); + drop(cache); + let cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), pending); - cache.resolve(id, Resolution::Mine).unwrap(); - server.fault = Fault::UnknownBefore; - assert!( - matches!(cache.sync_once(&mut server), Err(Error::Remote(error)) if error.state == CommitState::Unknown) + assert!(matches!( + cache.sync_once(&mut server).unwrap().edit, + Some((id, EditStatus::Published { .. })) if id == pending.last().unwrap().id + )); + assert_eq!(server.publications, 2); + assert_eq!(content(&server.durable, oid), expected); + assert_eq!( + conflicts(&server.durable), + [(sid, vec![(model_ops::AUTHOR.to_owned(), vec!["aLc".to_owned()])])] ); - let attempted = cache.status(id).unwrap(); - assert!(matches!( - attempted, - Some(EditStatus::AwaitingConfirmation { .. }) - )); - assert!(refused(&cache, id)); - assert_eq!(cache.status(id).unwrap(), attempted); - assert_eq!(server.publications, 1); + assert!(cache.pending().unwrap().is_empty()); } +/// A rebase that cannot be stored leaves the queue, and every page, as it was. #[test] -fn a_failed_resolution_leaves_the_conflict_as_it_was() { +fn a_failed_rebase_leaves_the_queue_as_it_was() { let dir = tempfile::tempdir().unwrap(); let path = dir.path().join("cache.sqlite"); let source = onestore::create_section("resolve.one", "abc", "Fixture").unwrap(); let (sid, oid, _) = text(&source); let cache = Replica::create(&path, &source).unwrap(); let id = save(&cache, oid, 1..2, "L").unwrap(); - let remote = onestore::replace_text(&source, sid, oid, 0..3, "XaRc").unwrap(); + let remote = typed(&source, sid, oid, 0..3, "XaRc"); let mut server = Server::new(&remote); - cache.sync_once(&mut server).unwrap(); - let local = pages(&cache.snapshot().unwrap()); + let local = pages(&snapshot(&cache)); let pending = cache.pending().unwrap(); - let status = cache.status(id).unwrap(); drop(cache); let connection = rusqlite::Connection::open(&path).unwrap(); - connection.execute_batch("CREATE TRIGGER fail_clear BEFORE DELETE ON batches BEGIN SELECT RAISE(ABORT, 'test resolution failure'); END;").unwrap(); + connection.execute_batch("CREATE TRIGGER fail_clear BEFORE DELETE ON batches BEGIN SELECT RAISE(ABORT, 'test rebase failure'); END;").unwrap(); drop(connection); let cache = Replica::open(&path).unwrap(); assert!(matches!( - cache.resolve(id, Resolution::Mine), + cache.sync_once(&mut server), Err(Error::Database(_)) )); drop(cache); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), pending); - assert_eq!(cache.status(id).unwrap(), status); - assert_eq!(pages(&cache.snapshot().unwrap()), local); - assert_eq!(cache.remote_snapshot().unwrap(), remote); + assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); + assert_eq!(pages(&snapshot(&cache)), local); assert_eq!(server.publications, 0); } #[test] -fn seeded_conflicts_resolve_to_the_remote_text_and_later_edits_publish_once() { +fn seeded_conflicts_keep_the_remote_text_and_later_edits_publish_once() { let dir = tempfile::tempdir().unwrap(); let original: Vec<_> = "abcdefghij🦀klmnop".chars().collect(); let mut seed = 911_u64; @@ -779,70 +689,71 @@ fn seeded_conflicts_resolve_to_the_remote_text_and_later_edits_publish_once() { .unwrap(); let (sid, oid, _) = text(&source); let cache = Replica::create(dir.path().join(format!("{case}.sqlite")), &source).unwrap(); - let id = save(&cache, oid, start..end, "λ🦊μ").unwrap(); + save(&cache, oid, start..end, "λ🦊μ").unwrap(); save(&cache, oid, start + 1..start + 3, "🐕").unwrap(); + let local = content(&snapshot(&cache), oid); let mut remote_text = original.clone(); remote_text.splice(at..at + 1, "Ω🐈π".chars()); let remote_text = prefix.clone() + &remote_text.iter().collect::() + &suffix; - let remote = onestore::replace_text( + let remote = typed( &source, sid, oid, 0..original.iter().map(|ch| ch.len_utf16() as u32).sum(), &remote_text, - ) - .unwrap(); + ); let mut server = Server::new(&remote); assert!( matches!( cache.sync_once(&mut server).unwrap().edit, - Some((_, EditStatus::Conflict(ConflictKind::ContentChanged))) + Some((_, EditStatus::Published { .. })) ), "case {case}, seed {seed}" ); + assert_eq!(content(&server.durable, oid), remote_text, "case {case}, seed {seed}"); + assert_eq!( + conflicts(&server.durable), + [(sid, vec![(model_ops::AUTHOR.to_owned(), vec![local])])], + "case {case}, seed {seed}" + ); let at_remote = start + prefix.len() as u32; - let reviewed = model_ops::review(&cache, id, oid, |page| { - model_ops::replace_text(page, oid, at_remote..at_remote + 4, "λ🐕μ") - }) - .unwrap() - .unwrap(); + let later = save(&cache, oid, at_remote..at_remote + 4, "λ🐕μ").unwrap(); assert!( - matches!(cache.sync_once(&mut server).unwrap().edit, Some((actual, EditStatus::Published { .. })) if actual == reviewed), + matches!(cache.sync_once(&mut server).unwrap().edit, Some((actual, EditStatus::Published { .. })) if actual == later), "case {case}, seed {seed}" ); let mut expected = original.clone(); expected.splice(at..at + 1, "λ🐕μ".chars()); let expected = prefix + &expected.iter().collect::() + &suffix; - assert_eq!( - text(&server.durable).2, - expected, - "case {case}, seed {seed}" - ); - assert_eq!(server.publications, 1); + assert_eq!(content(&server.durable, oid), expected, "case {case}, seed {seed}"); + assert_eq!(server.publications, 2); assert!(cache.pending().unwrap().is_empty()); } } #[test] -fn remote_changes_after_resolution_are_never_overwritten() { - struct ChangedAfterRead { +fn remote_changes_after_a_conflict_are_never_overwritten() { + struct ChangedAfterStamp { server: Server, change: Option>, } - impl Remote for ChangedAfterRead { + impl Remote for ChangedAfterStamp { fn read(&mut self) -> io::Result> { - let snapshot = self.server.read()?; + self.server.read() + } + fn stamp(&mut self) -> io::Result { + let stamp = self.server.stamp()?; if let Some(changed) = self.change.take() { self.server.visible = changed.clone(); self.server.durable = changed; } - Ok(snapshot) + Ok(stamp) } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.server.publish(transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.server.confirm(snapshot) + fn confirm(&mut self, base: &onestore::Stamp) -> Result<(), CommitError> { + self.server.confirm(base) } } for after_read in [false, true] { @@ -850,50 +761,41 @@ fn remote_changes_after_resolution_are_never_overwritten() { let source = onestore::create_section("resolve.one", "abc", "Fixture").unwrap(); let (sid, oid, _) = text(&source); let cache = Replica::create(dir.path().join("cache.sqlite"), &source).unwrap(); - let id = save(&cache, oid, 1..2, "L").unwrap(); - let remote = onestore::replace_text(&source, sid, oid, 1..2, "R").unwrap(); - let mut remote = ChangedAfterRead { + save(&cache, oid, 1..2, "L").unwrap(); + let remote = typed(&source, sid, oid, 1..2, "R"); + let mut remote = ChangedAfterStamp { server: Server::new(&remote), change: None, }; assert!(matches!( cache.sync_once(&mut remote).unwrap().edit, - Some((_, EditStatus::Conflict(_))) + Some((_, EditStatus::Published { .. })) )); - let id = model_ops::review(&cache, id, oid, |page| { - model_ops::replace_text(page, oid, 1..2, "L") - }) - .unwrap() - .unwrap(); - let local = content(&cache.snapshot().unwrap(), oid); - assert_eq!(local, "aLc"); - let changed = onestore::replace_text(&remote.server.visible, sid, oid, 1..2, "Q").unwrap(); + // The local version again, typed over the remote's. + let id = save(&cache, oid, 1..2, "L").unwrap(); + assert_eq!(content(&snapshot(&cache), oid), "aLc"); + let changed = typed(&remote.server.visible, sid, oid, 1..2, "Q"); if after_read { remote.change = Some(changed.clone()); + assert!( + matches!(cache.sync_once(&mut remote), Err(Error::Remote(error)) if error.state == CommitState::NotCommitted) + ); + assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); } else { remote.server.visible = changed.clone(); remote.server.durable = changed.clone(); } - if after_read { - assert!( - matches!(cache.sync_once(&mut remote), Err(Error::Remote(error)) if error.state == CommitState::NotCommitted) - ); - assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); - } - assert_eq!( - cache.sync_once(&mut remote).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - assert_eq!(remote.server.durable, changed); - assert_eq!(remote.server.visible, changed); - assert_eq!(remote.server.publications, usize::from(after_read)); - assert_eq!(content(&cache.snapshot().unwrap(), oid), local); - assert!(cache.pending().unwrap()[0].edit.ops.is_empty()); assert!(matches!( - &cache.pending().unwrap()[1].edit.ops[..], - [onestore::op::Op::Page { op: onestore::op::PageOp::Text { range, with, .. }, .. }] - if *range == (1..2) && with == "L" + cache.sync_once(&mut remote).unwrap().edit, + Some((_, EditStatus::Published { .. })) )); + assert_eq!(content(&remote.server.durable, oid), "aQc"); + let versions = [(model_ops::AUTHOR.to_owned(), vec!["aLc".to_owned()])]; + assert_eq!( + conflicts(&remote.server.durable), + [(sid, [versions.clone(), versions].concat())] + ); + assert!(cache.pending().unwrap().is_empty()); } } @@ -908,7 +810,7 @@ fn remote_restore_retains_historical_receipts_without_replaying_them() { let published = save(&cache, object, 0..0, "Published ").unwrap(); let (_, receipt) = cache.sync_once(&mut server).unwrap().edit.unwrap(); assert!(matches!(receipt, EditStatus::Published { .. })); - let published_image = cache.snapshot().unwrap(); + let published_image = snapshot(&cache); cache .export_recovery(directory.path().join("published.sqlite")) .unwrap(); @@ -918,8 +820,8 @@ fn remote_restore_retains_historical_receipts_without_replaying_them() { server.durable.clone_from(&source); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.sync_once(&mut server).unwrap().edit, None); - assert_eq!(cache.snapshot().unwrap(), source); - assert_eq!(cache.remote_snapshot().unwrap(), source); + assert_eq!(snapshot(&cache), source); + assert_eq!(remote_snapshot(&cache), source); assert_eq!(cache.status(published).unwrap(), Some(receipt.clone())); assert_eq!(server.publications, 1); assert!(cache.pending().unwrap().is_empty()); @@ -929,7 +831,7 @@ fn remote_restore_retains_historical_receipts_without_replaying_them() { assert_eq!(text(&archive.snapshot().unwrap()).2, "Published Original"); drop(cache); let cache = Replica::open(path).unwrap(); - assert_eq!(text(&cache.snapshot().unwrap()).2, "Original"); + assert_eq!(text(&snapshot(&cache)).2, "Original"); assert_eq!(cache.status(published).unwrap(), Some(receipt.clone())); } @@ -945,14 +847,14 @@ fn remote_restore_rebases_unsent_work_but_never_replays_an_uncertain_attempt() { let mut server = Server::new(&source); let published = save(&cache, object, 0..0, "Published ").unwrap(); let (_, receipt) = cache.sync_once(&mut server).unwrap().edit.unwrap(); - let end = u32::try_from(text(&cache.snapshot().unwrap()).2.encode_utf16().count()).unwrap(); + let end = u32::try_from(text(&snapshot(&cache)).2.encode_utf16().count()).unwrap(); let queued = save(&cache, object, end..end, " Local").unwrap(); if uncertain { server.fault = fault; assert!(cache.sync_once(&mut server).is_err()); } let prior_status = cache.status(queued).unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let pending = cache.pending().unwrap(); cache .export_recovery(directory.path().join("before-restore.sqlite")) @@ -969,7 +871,7 @@ fn remote_restore_rebases_unsent_work_but_never_replays_an_uncertain_attempt() { assert_eq!(Some(status.clone()), prior_status); assert_eq!(server.publications, attempts); assert_eq!(server.visible, source); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); assert_eq!(cache.pending().unwrap(), pending); } else { assert!(matches!(status, EditStatus::Published { .. })); @@ -989,8 +891,10 @@ fn remote_restore_rebases_unsent_work_but_never_replays_an_uncertain_attempt() { } } +/// A remote restored to an older image conflicts with local work on what it undid: the +/// restored page stays and the local version becomes its conflict page. #[test] -fn restore_conflicts_keep_dependent_work_until_mine_publishes_it() { +fn a_remote_restore_keeps_local_work_on_a_conflict_page() { let directory = tempfile::tempdir().unwrap(); let path = directory.path().join("cache.sqlite"); let source = onestore::create_section("restore.one", "Original", "Fixture").unwrap(); @@ -1002,52 +906,26 @@ fn restore_conflicts_keep_dependent_work_until_mine_publishes_it() { let head = save(&cache, object, 0..9, "Revised").unwrap(); server.visible.clone_from(&source); server.durable.clone_from(&source); - let conflict = EditStatus::Conflict(ConflictKind::ContentChanged); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((head, conflict.clone())) - ); - let end = u32::try_from(text(&cache.snapshot().unwrap()).2.encode_utf16().count()).unwrap(); - let dependent = save(&cache, object, end..end, " Later").unwrap(); - assert_ne!(head, dependent); - let local = text(&cache.snapshot().unwrap()).2; - assert_eq!(local, "Revised Original Later"); - let pending = cache.pending().unwrap(); - assert_eq!(pending.len(), 2); - cache - .export_recovery(directory.path().join("conflicted.sqlite")) - .unwrap(); - drop(cache); - let cache = Replica::open(&path).unwrap(); - assert_eq!(cache.status(head).unwrap(), Some(conflict.clone())); - assert_eq!(cache.status(dependent).unwrap(), Some(conflict.clone())); - assert_eq!(cache.pending().unwrap(), pending); - let changed = onestore::replace_text(&source, space, object, 8..8, " Remote").unwrap(); - server.visible.clone_from(&changed); - server.durable.clone_from(&changed); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((dependent, conflict.clone())) - ); - assert_eq!(cache.remote_snapshot().unwrap(), changed); - assert_eq!(server.publications, 1); - cache.resolve(head, Resolution::Mine).unwrap(); - drop(cache); - let cache = Replica::open(&path).unwrap(); assert!(matches!( cache.sync_once(&mut server).unwrap().edit, Some((_, EditStatus::Published { .. })) )); - assert_eq!(text(&server.durable).2, local); + assert_eq!(content(&server.durable, object), "Original"); + assert_eq!( + conflicts(&server.durable), + [( + space, + vec![(model_ops::AUTHOR.to_owned(), vec!["Revised Original".to_owned()])] + )] + ); + drop(cache); + let cache = Replica::open(&path).unwrap(); assert!(matches!( cache.status(head).unwrap(), Some(EditStatus::Published { .. }) )); - assert_eq!(cache.status(published).unwrap(), Some(receipt.clone())); - let archive = notebook::Recovery::open(directory.path().join("conflicted.sqlite")).unwrap(); - assert_eq!(text(&archive.snapshot().unwrap()).2, local); - assert_eq!(archive.pending().unwrap(), pending); - assert_eq!(archive.status(head).unwrap(), Some(conflict.clone())); + assert_eq!(cache.status(published).unwrap(), Some(receipt)); + assert!(cache.pending().unwrap().is_empty()); } #[test] diff --git a/crates/notebook/tests/sync/tree.rs b/crates/notebook/tests/sync/tree.rs index 41aac56061497585288e7811cef9426685ea691c..ba1f76c27c65ac8704f72946e5dc63327df0fb6b 100644 --- a/crates/notebook/tests/sync/tree.rs +++ b/crates/notebook/tests/sync/tree.rs @@ -34,9 +34,9 @@ fn fixture() -> (Vec, ExGuid, ExGuid, [ExGuid; 4], [ExGuid; 4]) { for at in 1..4 { anchor = model_ops::insert_after(&mut page, anchor, &format!("Sibling {at}")).1; } - let source = PreparedEdit::page(&source, space, &page, "Author") + let source = ops::saved(&source, space, &page) .unwrap() - .as_bytes() + .as_slice() .to_vec(); let stored = page_of(&source, space); let siblings = &outline_of(&stored, outline).paragraphs; @@ -87,12 +87,12 @@ fn move_preserves_remote_content_and_dependent_edits_through_reopen() { }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let queue = cache.pending().unwrap(); drop(cache); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), queue); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); let mut server = Server::new(&remote_with(&source, space, |page| { insert_child(page, paragraphs[0], "New remote child"); restyle(page, texts[1], 0..7, |format| format.bold = Some(true)); @@ -185,14 +185,16 @@ fn native_empty_child_list_normalization_merges_with_a_local_deletion() { } } +/// Deleting what the remote changed keeps the remote's content, and the local version on a +/// conflict page; the edits that do not depend on the deletion merge. #[test] -fn deletion_requires_review_of_remote_content_and_preserves_later_work() { +fn deletion_of_remote_content_keeps_a_conflict_page_and_merges_later_work() { let (source, space, outline, paragraphs, texts) = fixture(); let mut page = page_of(&source, space); insert_child(&mut page, paragraphs[0], "Descendant"); - let source = PreparedEdit::page(&source, space, &page, "Author") + let source = ops::saved(&source, space, &page) .unwrap() - .as_bytes() + .as_slice() .to_vec(); let child = outline_of(&page_of(&source, space), outline).paragraphs[1] .text() @@ -215,24 +217,20 @@ fn deletion_requires_review_of_remote_content_and_preserves_later_work() { replace_text(page, texts[1], 0..0, "Local "); }; let id = save(&cache, texts[1], change).unwrap().unwrap(); - let queue = cache.pending().unwrap(); - let local = cache.snapshot().unwrap(); let mut server = Server::new(&remote_with(&source, space, remote)); + assert!(conflicted(&cache, &mut server, space)); assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) + text_of(&page_of(&server.durable, space), texts[1]), + "Local Sibling 1" ); - assert_eq!(server.publications, 0); - assert_eq!(cache.pending().unwrap(), queue); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); drop(cache); let cache = Replica::open(&path).unwrap(); - assert_eq!( - cache.status(id).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) - ); - review(&cache, id, texts[1], change).unwrap(); published(&cache, &mut server, id); + // The writer deletes the paragraph again, remote changes and all. + let again = save(&cache, texts[1], |page| delete_paragraph(page, texts[0])) + .unwrap() + .unwrap(); + published(&cache, &mut server, again); assert_eq!( order(&server.durable, space, outline), paragraphs[1..].to_vec() @@ -247,7 +245,7 @@ fn deletion_requires_review_of_remote_content_and_preserves_later_work() { #[test] fn sibling_and_ancestry_changes_have_explicit_merge_or_conflict() { let (source, space, outline, paragraphs, texts) = fixture(); - // The published order of the fixture's paragraphs, or a conflict. + // The published order of the fixture's paragraphs, or a conflict page. let cases: [(Change, Option<&[usize]>); 5] = [ ( Box::new(move |page| { @@ -279,14 +277,16 @@ fn sibling_and_ancestry_changes_have_explicit_merge_or_conflict() { .unwrap() .unwrap(); let mut server = Server::new(&remote_with(&source, space, remote)); - let result = cache.sync_once(&mut server).unwrap().edit.unwrap(); - assert_eq!(result.0, id); + let conflict = conflicted(&cache, &mut server, space); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); let Some(expected) = expected else { - assert_eq!(result.1, EditStatus::Conflict(ConflictKind::ContentChanged)); - assert_eq!(server.publications, 0); + assert!(conflict); continue; }; - assert!(matches!(result.1, EditStatus::Published { .. })); + assert!(!conflict); let published: Vec = order(&server.durable, space, outline) .into_iter() .filter(|id| paragraphs.contains(id)) @@ -352,7 +352,7 @@ fn cell_mut(page: &mut Page, id: ExGuid) -> &mut TableCell { } /// Moves a cell's sole paragraph to the end of another cell, or deletes it, leaving the -/// cell an empty paragraph as the writers do. +/// cell a new empty paragraph, as an edit that empties a cell must. fn empty_cell(page: &mut Page, cell: ExGuid, destination: Option) { let emptied = cell_mut(page, cell); let mut replacement = model_ops::fresh_paragraph(&emptied.paragraphs[0], ""); @@ -419,7 +419,7 @@ fn emptied_cell_replacement_is_durable_and_cannot_be_silently_omitted_on_replay( empty_cell(page, cell, move_out.then_some(neighbour)); }) .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let queue = cache.pending().unwrap(); { let page = page_of(&local, space); @@ -431,7 +431,7 @@ fn emptied_cell_replacement_is_durable_and_cannot_be_silently_omitted_on_replay( drop(cache); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), queue); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); let mut server = Server::new(&if competing { remote_with(source, space, |page| { let target = cell_mut(page, cell); @@ -574,28 +574,20 @@ fn native_tree_and_layout_changes_reconcile_without_discarding_unreviewed_conten let path = directory.path().join("native.sqlite"); let cache = Replica::create(&path, BEFORE).unwrap(); let id = save(&cache, dependent, save_both).unwrap().unwrap(); - let local = cache.snapshot().unwrap(); - let queue = cache.pending().unwrap(); - let merged = match cache.sync_once(&mut server).unwrap().edit.unwrap() { - (actual, EditStatus::Published { .. }) => { - assert_eq!(actual, id, "{name}"); - true + let merged = !conflicted(&cache, &mut server, space); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + if !merged { + // The dependent text merged; the writer makes the change again. + drop(cache); + let cache = Replica::open(&path).unwrap(); + // Unless the remote made the same change. + if let Some(again) = save(&cache, dependent, &change).unwrap() { + published(&cache, &mut server, again); } - result => { - assert_eq!( - result, - (id, EditStatus::Conflict(ConflictKind::ContentChanged)), - "{name}" - ); - assert_eq!(cache.pending().unwrap(), queue); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); - drop(cache); - let cache = Replica::open(&path).unwrap(); - review(&cache, id, dependent, save_both).unwrap(); - published(&cache, &mut server, id); - false - } - }; + } let durable = page_of(&server.durable, space); assert!( text_of(&durable, dependent).starts_with("Offline "), diff --git a/crates/notebook/tests/sync_formatting.rs b/crates/notebook/tests/sync_formatting.rs index dca00aa6e1511bafceb8e6c0f5b63620c83d7fbf..2810b47732944500353c35c1ac95d4d2275082e7 100644 --- a/crates/notebook/tests/sync_formatting.rs +++ b/crates/notebook/tests/sync_formatting.rs @@ -1,21 +1,23 @@ //! Formatting and insertion as page-model differences: what reconciles with an independent -//! remote change, what conflicts, and what a reviewed conflict publishes. +//! remote change, what keeps a conflict page, and what the edit made again publishes. -use notebook::{ConflictKind, EditStatus, Replica}; +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; + +use notebook::{EditStatus, Replica}; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Format}, page::{Page, PageObject}, }; #[path = "support/server.rs"] mod server; -use server::{Fault, Server, pages}; +use server::{Fault, Server, conflicted, pages, snapshot}; #[path = "support/model_ops.rs"] mod model_ops; use model_ops::{ - AUTHOR, insert_after, insert_outline, page_of, paragraph_with, replace_text, restyle, review, - save, + AUTHOR, insert_after, insert_outline, page_of, paragraph_with, replace_text, restyle, save, }; const OUTLINES: &[u8] = @@ -80,9 +82,9 @@ fn remote_with(space: ExGuid, change: impl FnOnce(&mut Page)) -> Server { let mut page = page_of(OUTLINES, space); change(&mut page); Server::new( - PreparedEdit::page(OUTLINES, space, &page, "Native author") + ops::saved(OUTLINES, space, &page) .unwrap() - .as_bytes(), + .as_slice(), ) } @@ -112,7 +114,7 @@ fn offline_formatting_merges_with_a_remote_edit_to_another_paragraph() { .all(|format| format.bold == Some(true)) ); assert_eq!(text_of(&published, ids[3]), "Remote Trailing sibling"); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); } #[test] @@ -127,7 +129,7 @@ fn offline_formatting_conflicts_only_with_remote_changes_it_overlaps() { }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let mut server = remote_with(space, |page| { if typed { replace_text(page, ids[0], 0..0, "Remote "); @@ -150,18 +152,18 @@ fn offline_formatting_conflicts_only_with_remote_changes_it_overlaps() { ); } } else { - assert_eq!( - outcome, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - assert_eq!(server.publications, 0); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert!(matches!(outcome, Some((_, EditStatus::Published { .. })))); + assert!(conflicted(&server.durable, space)); + let remote = page_of(&server.durable, space); + assert!(formats(&remote, ids[0]).iter().all(|format| format.bold != Some(true))); + assert_eq!(pages(&snapshot(&cache)), pages(&server.durable)); + assert_ne!(pages(&local), pages(&server.durable)); } } } #[test] -fn competing_formatting_of_one_paragraph_conflicts_until_reviewed() { +fn competing_formatting_of_one_paragraph_keeps_a_conflict_page_until_made_again() { let (space, page) = page_titled(OUTLINES, PAGE); let ids = plain(&page); let directory = tempfile::tempdir().unwrap(); @@ -172,24 +174,22 @@ fn competing_formatting_of_one_paragraph_conflicts_until_reviewed() { }) .unwrap() .unwrap(); - let pending = cache.pending().unwrap(); let mut server = remote_with(space, |page| { restyle(page, ids[0], 0..3, |format| format.italic = Some(true)); }); - for _ in 0..2 { - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - } - assert_eq!(cache.pending().unwrap(), pending); + assert!(matches!( + cache.sync_once(&mut server).unwrap().edit, + Some((_, EditStatus::Published { .. })) + )); + assert!(conflicted(&server.durable, space)); + assert_eq!(cache.sync_once(&mut server).unwrap().edit, None); drop(cache); let cache = Replica::open(&path).unwrap(); - assert_eq!( + assert!(matches!( cache.status(id).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) - ); - review(&cache, id, ids[0], |page| { + Some(EditStatus::Published { .. }) + )); + save(&cache, ids[0], |page| { restyle(page, ids[0], 0..6, |format| format.font_size = Some(14.0)); }) .unwrap(); @@ -287,7 +287,7 @@ fn seeded_formatting_reconciles_exactly_when_the_remote_left_the_paragraph_alone }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let mut server = remote_with(space, |page| { for (step, &at) in remote.iter().enumerate() { if step % 2 == 0 { @@ -313,17 +313,10 @@ fn seeded_formatting_reconciles_exactly_when_the_remote_left_the_paragraph_alone } if underlined[target] && start == 0 { conflicts += 1; - assert_eq!( - outcome.1, - EditStatus::Conflict(ConflictKind::ContentChanged), - "seed {seed}" - ); - assert_eq!(server.publications, 0, "seed {seed}"); - assert_eq!( - pages(&cache.snapshot().unwrap()), - pages(&local), - "seed {seed}" - ); + assert!(matches!(outcome.1, EditStatus::Published { .. }), "seed {seed}"); + assert!(conflicted(&server.durable, space), "seed {seed}"); + assert_eq!(page_of(&server.durable, space), remote_page, "seed {seed}"); + assert_ne!(pages(&snapshot(&cache)), pages(&local), "seed {seed}"); continue; } published += 1; @@ -345,7 +338,7 @@ fn seeded_formatting_reconciles_exactly_when_the_remote_left_the_paragraph_alone "seed {seed}" ); } - assert_eq!(cache.snapshot().unwrap(), server.durable, "seed {seed}"); + assert_eq!(snapshot(&cache), server.durable, "seed {seed}"); } assert!( published >= 16, @@ -366,11 +359,11 @@ fn offline_insertions_survive_reopen_and_carry_their_formatting() { }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let pending = cache.pending().unwrap(); drop(cache); cache = Replica::open(&path).unwrap(); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); assert_eq!(cache.pending().unwrap(), pending); let mut server = remote_with(space, |page| replace_text(page, ids[3], 0..0, "Remote ")); assert!( @@ -399,7 +392,7 @@ fn offline_insertions_survive_reopen_and_carry_their_formatting() { for (offset, format) in styles.iter().enumerate() { assert_eq!(format.bold == Some(true), (8..17).contains(&offset)); } - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); } #[test] @@ -415,7 +408,7 @@ fn uncertain_formatting_attempts_keep_the_original_attempt_and_never_replay() { }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let mut server = Server::new(OUTLINES); server.fault = fault; assert!(cache.sync_once(&mut server).is_err()); @@ -433,7 +426,7 @@ fn uncertain_formatting_attempts_keep_the_original_attempt_and_never_replay() { } assert_eq!(server.publications, 1); assert_eq!(server.durable, OUTLINES); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); continue; } assert!(matches!( @@ -474,7 +467,7 @@ fn a_format_the_remote_already_carries_is_confirmed_without_republishing() { ); assert_eq!((server.publications, server.confirmations), (0, 2)); assert!(cache.pending().unwrap().is_empty()); - let published = page_of(&cache.snapshot().unwrap(), space); + let published = page_of(&snapshot(&cache), space); assert_eq!(published, page_of(&server.durable, space)); assert!( formats(&published, ids[0]) @@ -552,7 +545,7 @@ fn paragraph_formatting_survives_reopen_rebase_and_a_lost_publication_reply() { let id = save(&cache, ids[0], |page| restyle(page, ids[0], 0..6, apply)) .unwrap() .unwrap(); - let expected = formats(&page_of(&cache.snapshot().unwrap(), space), ids[0]); + let expected = formats(&page_of(&snapshot(&cache), space), ids[0]); drop(cache); let cache = Replica::open(directory.path().join("cache.sqlite")).unwrap(); let mut server = remote_with(space, |page| replace_text(page, ids[3], 0..0, "Remote ")); diff --git a/crates/notebook/tests/sync_model.rs b/crates/notebook/tests/sync_model.rs index 5132e5f0cb78f260ebda6bb0a5091b5e7e9331ad..1de8ceb7ce71feadc18569948778bcc3f8e0b6c2 100644 --- a/crates/notebook/tests/sync_model.rs +++ b/crates/notebook/tests/sync_model.rs @@ -1,8 +1,11 @@ //! Page-model saves: one intent per page until it is attempted, reconciliation against the //! remote page, reviewed conflicts, and independence between pages. -use notebook::{ConflictKind, EditStatus, Replica}; -use onestore::{ExGuid, PreparedEdit, RevisionIndex, Store, document::Document}; +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; + +use notebook::{EditStatus, Replica}; +use onestore::{ExGuid, RevisionIndex, Store, document::Document}; #[path = "support/server.rs"] mod server; @@ -80,7 +83,7 @@ fn a_saved_page_model_publishes_and_reads_back() { .unwrap() .unwrap(); assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); - assert_eq!(page_of(&cache.snapshot().unwrap(), space), after); + assert_eq!(page_of(&snapshot(&cache), space), after); let pending = cache.pending().unwrap(); assert_eq!(pending.len(), 1); assert_eq!(pending[0].author, "Model author"); @@ -113,7 +116,7 @@ fn saves_before_an_attempt_publish_together() { let id = model_ops::save_as(&cache, space, &after, "Model author") .unwrap() .unwrap(); - let mut later = page_of(&cache.snapshot().unwrap(), space); + let mut later = page_of(&snapshot(&cache), space); append(&mut later, " two"); let second = model_ops::save_as(&cache, space, &later, "Model author") .unwrap() @@ -133,7 +136,7 @@ fn saves_before_an_attempt_publish_together() { first_text(&page_of(&server.durable, space)), first_text(&later) ); - let mut third = page_of(&cache.snapshot().unwrap(), space); + let mut third = page_of(&snapshot(&cache), space); append(&mut third, " three"); let next = model_ops::save_as(&cache, space, &third, "Model author") .unwrap() @@ -157,7 +160,7 @@ fn an_attempted_save_is_never_rewritten() { cache.status(id).unwrap(), Some(EditStatus::AwaitingConfirmation { .. }) )); - let mut more = page_of(&cache.snapshot().unwrap(), space); + let mut more = page_of(&snapshot(&cache), space); append(&mut more, " more"); let next = model_ops::save_as(&cache, space, &more, "Model author") .unwrap() @@ -165,7 +168,7 @@ fn an_attempted_save_is_never_rewritten() { assert!(next > id); let pending = cache.pending().unwrap(); assert_eq!(pending.iter().map(|p| p.id).collect::>(), [id, next]); - assert_eq!(page_of(&cache.snapshot().unwrap(), space), more); + assert_eq!(page_of(&snapshot(&cache), space), more); assert!(matches!( cache.status(id).unwrap(), Some(EditStatus::AwaitingConfirmation { .. }) @@ -173,8 +176,10 @@ fn an_attempted_save_is_never_rewritten() { assert_eq!(cache.status(next).unwrap(), Some(EditStatus::Pending)); } +/// A remote change to the saved text keeps the remote's page and the local one as its +/// conflict page; the writer's change made again publishes over the remote's. #[test] -fn a_remote_change_to_the_saved_page_conflicts_until_reviewed() { +fn a_remote_change_to_the_saved_page_keeps_a_conflict_page() { let (space, mut after) = page_titled(OUTLINES, "Move leaf down"); let directory = tempfile::tempdir().unwrap(); let cache = Replica::create(directory.path().join("cache.sqlite"), OUTLINES).unwrap(); @@ -184,49 +189,37 @@ fn a_remote_change_to_the_saved_page_conflicts_until_reviewed() { .unwrap(); let mut remote_page = page_of(OUTLINES, space); append(&mut remote_page, " remote"); - let remote = PreparedEdit::page(OUTLINES, space, &remote_page, "Native author").unwrap(); - let mut server = Server::new(remote.as_bytes()); - assert_eq!( + let remote = ops::saved(OUTLINES, space, &remote_page).unwrap(); + let mut server = Server::new(remote.as_slice()); + assert!(matches!( cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) + Some((_, EditStatus::Published { .. })) + )); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + assert_eq!(server.publications, 1); + let published = page_of(&server.durable, space); + assert_eq!(first_text(&published), first_text(&remote_page)); + assert_eq!( + conflicts(&server.durable)[0].1[0].0, + "Model author", + "the conflict page names whose version it is" ); - assert_eq!(server.publications, 0); - assert!(cache.pending().unwrap().len() == 1); - let observed = cache.remote_snapshot().unwrap(); - assert_eq!(observed, remote.as_bytes()); - let mut reviewed = page_of(&observed, space); - append(&mut reviewed, " local"); - assert!(cache.resolve(id + 1, notebook::Resolution::Theirs).is_err()); - let text = model_ops::paragraph_with(&reviewed, { - let outline = reviewed - .objects - .iter() - .find_map(|object| match object { - PageObject::Outline(outline) => Some(outline), - _ => None, - }) - .unwrap(); - outline.paragraphs[0].text().unwrap().id - }) - .unwrap() - .text() - .unwrap() - .id; - let reviewed_id = model_ops::review(&cache, id, text, |page| append(page, " local")) + let text = published + .objects + .iter() + .find_map(|object| match object { + PageObject::Outline(outline) => outline.paragraphs[0].text().map(|text| text.id), + _ => None, + }) + .unwrap(); + let again = model_ops::save(&cache, text, |page| append(page, " local")) .unwrap() .unwrap(); - // The conflicted edit stays, emptied, and publishes with the reviewed one. - assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); - assert_eq!( - cache.status(reviewed_id).unwrap(), - Some(EditStatus::Pending) - ); assert!( - matches!(cache.sync_once(&mut server).unwrap().edit, Some((n, EditStatus::Published { .. })) if n == reviewed_id) - ); - assert_eq!( - first_text(&page_of(&server.durable, space)), - first_text(&reviewed) + matches!(cache.sync_once(&mut server).unwrap().edit, Some((n, EditStatus::Published { .. })) if n == again) ); assert!(first_text(&page_of(&server.durable, space)).ends_with(" remote local")); } @@ -242,14 +235,14 @@ fn remote_changes_to_other_pages_do_not_conflict() { .unwrap() .unwrap(); append(&mut other_page, " elsewhere"); - let remote = PreparedEdit::page(OUTLINES, other, &other_page, "Native author").unwrap(); - let mut server = Server::new(remote.as_bytes()); + let remote = ops::saved(OUTLINES, other, &other_page).unwrap(); + let mut server = Server::new(remote.as_slice()); assert!( matches!(cache.sync_once(&mut server).unwrap().edit, Some((n, EditStatus::Published { .. })) if n == id) ); assert_eq!(page_of(&server.durable, space), after); assert_eq!(page_of(&server.durable, other), other_page); - assert_eq!(page_of(&cache.snapshot().unwrap(), other), other_page); + assert_eq!(page_of(&snapshot(&cache), other), other_page); } #[test] @@ -267,7 +260,7 @@ fn pending_saves_survive_reopening_the_cache() { let reopened = Replica::open(&path).unwrap(); assert_eq!(reopened.pending().unwrap(), pending); assert_eq!(reopened.status(id).unwrap(), Some(EditStatus::Pending)); - assert_eq!(page_of(&reopened.snapshot().unwrap(), space), after); + assert_eq!(page_of(&snapshot(&reopened), space), after); } /// Edits paragraph `index` of the first body outline: replaces its text range with `replacement`. @@ -309,11 +302,7 @@ fn texts(page: &Page) -> Vec { fn remote_with(space: ExGuid, change: impl FnOnce(&mut Page)) -> Server { let mut page = page_of(OUTLINES, space); change(&mut page); - Server::new( - PreparedEdit::page(OUTLINES, space, &page, "Native author") - .unwrap() - .as_bytes(), - ) + Server::new(ops::saved(OUTLINES, space, &page).unwrap().as_slice()) } #[test] @@ -366,11 +355,16 @@ fn concurrent_edits_to_one_paragraph_merge_unless_their_ranges_overlap() { .unwrap() .unwrap(); let mut server = remote_with(space, |page| edit_paragraph(page, 0, 0..6, "Remote")); - assert_eq!( + assert!(matches!( cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - assert_eq!(server.publications, 0); + Some((_, EditStatus::Published { .. })) + )); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + assert!(conflicted(&server.durable, space)); + assert_eq!(server.publications, 1); } #[test] @@ -428,7 +422,7 @@ fn a_local_insertion_merges_with_a_remote_deletion_elsewhere() { } #[test] -fn a_remote_deletion_of_the_edited_paragraph_conflicts() { +fn a_remote_deletion_of_the_edited_paragraph_keeps_a_conflict_page() { let (space, mut after) = page_titled(OUTLINES, "Move leaf down"); let directory = tempfile::tempdir().unwrap(); let cache = Replica::create(directory.path().join("cache.sqlite"), OUTLINES).unwrap(); @@ -447,10 +441,15 @@ fn a_remote_deletion_of_the_edited_paragraph_conflicts() { .unwrap(); outline.paragraphs.pop(); }); - assert_eq!( + assert!(matches!( cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); + Some((_, EditStatus::Published { .. })) + )); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + assert!(conflicted(&server.durable, space)); } #[test] @@ -515,10 +514,15 @@ fn outline_moves_merge_with_remote_text_edits_but_not_with_remote_moves() { }; outline.layout.x = Some(50.0); }); - assert_eq!( + assert!(matches!( cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); + Some((_, EditStatus::Published { .. })) + )); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + assert!(conflicted(&server.durable, space)); } #[test] diff --git a/crates/notebook/tests/sync_outline.rs b/crates/notebook/tests/sync_outline.rs index f136c756bf1812076e9fc045271e37caacc0d554..79b2085fb9e9750bb53c2cb233be5c9187d2e528 100644 --- a/crates/notebook/tests/sync_outline.rs +++ b/crates/notebook/tests/sync_outline.rs @@ -1,9 +1,12 @@ //! Outline layout through page-model saves: reconciliation against the remote page, //! review of competing layout, twelve offline writers, and the native outline fixtures. -use notebook::{ConflictKind, EditStatus, Replica}; +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; + +use notebook::{EditStatus, Replica}; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::Document, page::{Outline, Page, PageObject, PageParagraph}, }; @@ -11,12 +14,12 @@ use serde_json::json; #[path = "support/server.rs"] mod server; -use server::{Fault, Server, pages}; +use server::{Fault, Server, conflicts, pages, snapshot}; #[path = "support/model_ops.rs"] mod model_ops; use model_ops::{ delete_paragraph, insert_outline, outlines_mut, page_of, paragraph_with, replace_text, restyle, - review, save, + save, }; #[path = "support/model_schedule.rs"] mod model_schedule; @@ -99,12 +102,30 @@ pub(crate) fn text_of(page: &Page, text: ExGuid) -> String { pub(crate) fn remote_with(source: &[u8], space: ExGuid, change: impl FnOnce(&mut Page)) -> Vec { let mut page = page_of(source, space); change(&mut page); - PreparedEdit::page(source, space, &page, "Native author") + ops::saved(source, space, &page) .unwrap() - .as_bytes() + .as_slice() .to_vec() } +/// Runs one synchronization step, which publishes; true when the remote's version of +/// `space` stayed and the local one became a conflict page under it. +pub(crate) fn conflicted(cache: &Replica, server: &mut Server, space: ExGuid) -> bool { + let count = |image: &[u8]| { + conflicts(image) + .into_iter() + .filter(|(page, _)| *page == space) + .map(|(_, pages)| pages.len()) + .sum::() + }; + let before = count(&server.durable); + assert!(matches!( + cache.sync_once(server).unwrap().edit, + Some((_, EditStatus::Published { .. })) + )); + count(&server.durable) > before +} + /// Publishes the batch holding `id`, unless an earlier step already did. pub(crate) fn published(cache: &Replica, server: &mut Server, id: u64) { if !matches!( @@ -149,13 +170,13 @@ fn layout_and_dependent_text_survive_reopen_and_a_remote_format_of_the_same_page let path = directory.path().join("cache.sqlite"); let cache = Replica::create(&path, &source).unwrap(); let id = save(&cache, text_id, &change).unwrap().unwrap(); - let expected = page_of(&cache.snapshot().unwrap(), space); + let expected = page_of(&snapshot(&cache), space); let pending = cache.pending().unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); drop(cache); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), pending); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); let mut server = Server::new(&remote_with(&source, space, |page| { restyle(page, text_id, 0..8, |format| format.bold = Some(true)); })); @@ -208,9 +229,9 @@ fn an_uncertain_move_confirms_from_the_remote_revision_and_adopts_the_remote_tit let (source, space, outline, _) = fixture(); let mut page = page_of(&source, space); insert_outline(&mut page, 144.0, 36.0, "Second"); - let source = PreparedEdit::page(&source, space, &page, "Author") + let source = ops::saved(&source, space, &page) .unwrap() - .as_bytes() + .as_slice() .to_vec(); let second = text_with(&page_of(&source, space), "Second"); let directory = tempfile::tempdir().unwrap(); @@ -242,8 +263,9 @@ fn an_uncertain_move_confirms_from_the_remote_revision_and_adopts_the_remote_tit assert_eq!((server.publications, server.confirmations), (1, 1)); } +/// Competing layout keeps the remote's on the page and the local one on its conflict page. #[test] -fn competing_layout_conflicts_until_the_local_page_is_kept() { +fn competing_layout_keeps_the_local_one_on_a_conflict_page() { let (source, space, outline, text_id) = fixture(); let cases: [(Change, Change); 2] = [ (Box::new(moved(180.0, 216.0)), Box::new(moved(288.0, 216.0))), @@ -256,46 +278,40 @@ fn competing_layout_conflicts_until_the_local_page_is_kept() { let directory = tempfile::tempdir().unwrap(); let path = directory.path().join("cache.sqlite"); let cache = Replica::create(&path, &source).unwrap(); - let id = save(&cache, text_id, |page| { + save(&cache, text_id, |page| { local_change(outline_mut(page, outline)) }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); - let mut server = Server::new(&remote_with(&source, space, |page| { + let local = page_of(&snapshot(&cache), space); + let remote = remote_with(&source, space, |page| { remote_change(outline_mut(page, outline)); - })); + }); + let mut server = Server::new(&remote); + assert!(conflicted(&cache, &mut server, space)); + assert_eq!(server.publications, 1); + let durable = page_of(&server.durable, space); assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) + outline_of(&durable, outline).layout, + outline_of(&page_of(&remote, space), outline).layout ); - assert_eq!(server.publications, 0); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, server.durable.clone()).unwrap(); + let conflict = section.conflicts().unwrap()[0].1[0].space; + let kept = section.page(conflict).unwrap(); + assert_eq!(body_outlines(&kept)[0].layout, outline_of(&local, outline).layout); let dependent = save(&cache, text_id, |page| { replace_text(page, text_id, 0..0, "Local ") }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); drop(cache); let cache = Replica::open(&path).unwrap(); + published(&cache, &mut server, dependent); assert_eq!( - cache.status(id).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) + text_of(&page_of(&server.durable, space), text_id), + "Local Original 🦀 é" ); - cache.resolve(id, notebook::Resolution::Mine).unwrap(); - assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); - drop(cache); - let cache = Replica::open(&path).unwrap(); - for id in [id, dependent] { - published(&cache, &mut server, id); - } - let durable = page_of(&server.durable, space); - assert_eq!( - outline_of(&durable, outline).layout, - outline_of(&page_of(&local, space), outline).layout - ); - assert_eq!(text_of(&durable, text_id), "Local Original 🦀 é"); } } @@ -346,15 +362,14 @@ fn twelve_offline_writers_preserve_all_layout_intents_through_review_and_restart paragraph_mut(page, text_id).collapsed = (actor + round) % 2 != 0; }; let id = save(&cache, text_id, change).unwrap().unwrap(); - let expected = page_of(&cache.snapshot().unwrap(), space); + let expected = page_of(&snapshot(&cache), space); drop(cache); let cache = Replica::open(&path).unwrap(); - if cache.sync_once(&mut server).unwrap().edit - == Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - { - review(&cache, id, text_id, change).unwrap(); + if conflicted(&cache, &mut server, space) { + // The writer makes the change again over the remote's version. + let again = save(&cache, text_id, change).unwrap().unwrap(); reviewed += 1; - published(&cache, &mut server, id); + published(&cache, &mut server, again); } assert!(matches!( cache.status(id).unwrap(), @@ -388,7 +403,7 @@ fn an_uncertain_layout_attempt_confirms_by_revision_or_by_an_equal_remote_page() }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let mut server = Server::new(&source); server.fault = fault; assert!(cache.sync_once(&mut server).is_err()); @@ -416,7 +431,7 @@ fn an_uncertain_layout_attempt_confirms_by_revision_or_by_an_equal_remote_page() cache.sync_once(&mut server).unwrap().edit, Some((id, status.clone())) ); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); server.visible = remote_with(&source, space, |page| { resized(144.0, true)(outline_mut(page, outline)); }); @@ -483,7 +498,7 @@ pub(crate) fn native_pages(source: &[u8]) -> Vec<(String, ExGuid, ExGuid, ExGuid } #[test] -fn native_moves_deletions_and_layout_changes_merge_or_require_review() { +fn native_moves_deletions_and_layout_changes_merge_or_keep_a_conflict_page() { let mut server = Server::new(NATIVE); let mut records = Vec::new(); for (name, space, outline, target, dependent) in native_pages(BEFORE) { @@ -496,28 +511,20 @@ fn native_moves_deletions_and_layout_changes_merge_or_require_review() { let path = directory.path().join("cache.sqlite"); let cache = Replica::create(&path, BEFORE).unwrap(); let id = save(&cache, dependent, save_both).unwrap().unwrap(); - let local = cache.snapshot().unwrap(); - let queue = cache.pending().unwrap(); - let merged = match cache.sync_once(&mut server).unwrap().edit.unwrap() { - (actual, EditStatus::Published { .. }) => { - assert_eq!(actual, id, "{name}"); - true + let merged = !conflicted(&cache, &mut server, space); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + if !merged { + // The dependent text merged; the writer makes the change again. + drop(cache); + let cache = Replica::open(&path).unwrap(); + // Unless the remote made the same change. + if let Some(again) = save(&cache, dependent, &change).unwrap() { + published(&cache, &mut server, again); } - result => { - assert_eq!( - result, - (id, EditStatus::Conflict(ConflictKind::ContentChanged)), - "{name}" - ); - assert_eq!(cache.pending().unwrap(), queue); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); - drop(cache); - let cache = Replica::open(&path).unwrap(); - review(&cache, id, dependent, save_both).unwrap(); - published(&cache, &mut server, id); - false - } - }; + } let durable = page_of(&server.durable, space); assert!( text_of(&durable, dependent).starts_with("Local "), @@ -564,7 +571,7 @@ fn native_moves_deletions_and_layout_changes_merge_or_require_review() { } #[test] -fn a_new_native_wrap_reservation_requires_review_before_width_replacement() { +fn a_new_native_wrap_reservation_keeps_the_local_width_on_a_conflict_page() { let (_, space, outline, _, text_id) = native_pages(BEFORE) .into_iter() .find(|(name, ..)| name == "Move outline") @@ -584,15 +591,17 @@ fn a_new_native_wrap_reservation_requires_review_before_width_replacement() { .unwrap() .unwrap(); let mut server = Server::new(NATIVE); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - review(&cache, id, text_id, |page| { + assert!(conflicted(&cache, &mut server, space)); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + let again = save(&cache, text_id, |page| { resized(144.0, true)(outline_mut(page, outline)) }) + .unwrap() .unwrap(); - published(&cache, &mut server, id); + published(&cache, &mut server, again); let layout = outline_of(&page_of(&server.durable, space), outline) .layout .clone(); diff --git a/crates/notebook/tests/sync_page.rs b/crates/notebook/tests/sync_page.rs index 2c434a6a1b628b0499c9ac12b530f23506aa542b..1fa47119d629b644e5e0c5ef2c259a0ad5e18e6f 100644 --- a/crates/notebook/tests/sync_page.rs +++ b/crates/notebook/tests/sync_page.rs @@ -1,7 +1,10 @@ -//! Page creation reconciliation: dependent body saves, uncertain publication, anchor review, +//! Page creation reconciliation: dependent body saves, uncertain publication, lost anchors, //! and deterministic multi-actor schedules. -use notebook::{ConflictKind, EditStatus, Recovery, Replica, Resolution}; +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; + +use notebook::{EditStatus, Recovery, Replica}; use onestore::op::SectionOp; use onestore::{ ExGuid, PageCreation, RevisionIndex, Store, @@ -67,11 +70,11 @@ fn offline_page_creation_rebases_with_dependent_edits_and_duplicate_titles() { assert!( matches!(&original[0].edit.ops[..], [onestore::op::Op::Section(SectionOp::Create(retained))] if retained == &page) ); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); drop(cache); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), original); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); assert!(matches!( cache.sync_once(&mut server).unwrap().edit, Some((published, EditStatus::Published { .. })) if published == dependent @@ -120,7 +123,7 @@ fn uncertain_page_publication_retains_both_revisions_and_never_replays() { let cache = Replica::create(&path, &source).unwrap(); let page = PageCreation::new(None, Some("Created"), "Author").unwrap(); let id = section_op(&cache, SectionOp::Create(page.clone())); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let mut server = Server::new(&source); server.fault = fault; let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { @@ -155,12 +158,12 @@ fn uncertain_page_publication_retains_both_revisions_and_never_replays() { assert_eq!(server.confirmations, usize::from(visible)); if visible { assert!(matches!(result.1, EditStatus::Published { .. })); - assert!(cache.snapshot().unwrap() == observed); + assert!(snapshot(&cache) == observed); assert!(observed[..212] == server.durable[..212]); assert!(observed[252..] == server.durable[252..]); } else { assert_eq!(result, (id, attempted.clone())); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); } } } @@ -235,8 +238,10 @@ fn surviving_page_revision_alone_does_not_confirm_section_publication() { assert_eq!(server.publications, 1); } +/// A page created before a page the remote moved goes before the next page the remote left +/// in place, its content under the identities it was made with. #[test] -fn changed_page_anchor_conflicts_until_kept_without_regenerating_dependent_identities() { +fn a_moved_page_anchor_places_the_page_without_regenerating_dependent_identities() { let source = include_bytes!("../../../corpus/page-lifecycle/03-renamed/notebook/Lifecycle.one"); let remote = include_bytes!("../../../corpus/page-lifecycle/04-nested/notebook/Lifecycle.one"); let store = Store::parse(source).unwrap(); @@ -252,30 +257,29 @@ fn changed_page_anchor_conflicts_until_kept_without_regenerating_dependent_ident let save = model_ops::save_as(&cache, page.space(), &created, "Author") .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); - let pending = cache.pending().unwrap(); + let local = snapshot(&cache); let mut server = Server::new(remote); - assert_eq!( + assert!(matches!( cache.sync_once(&mut server).unwrap().edit, - Some((save, EditStatus::Conflict(ConflictKind::StructureChanged))) - ); - cache.resolve(id, Resolution::Mine).unwrap(); - assert_eq!(cache.pending().unwrap()[1], pending[1]); + Some((published, EditStatus::Published { .. })) if published == save + )); assert!(matches!( - &cache.pending().unwrap()[0].edit.ops[..], - [onestore::op::Op::Section(SectionOp::Create(reviewed))] if *reviewed == page.reposition(None).unwrap() + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) )); + assert_eq!(server.publications, 1); assert_eq!( - model_ops::page_of(&cache.snapshot().unwrap(), page.space()), + model_ops::page_of(&server.durable, page.space()), model_ops::page_of(&local, page.space()) ); - drop(cache); - let cache = Replica::open(&path).unwrap(); - assert!(matches!( - cache.sync_once(&mut server).unwrap().edit, - Some((_, EditStatus::Published { .. })) - )); - assert_eq!(server.publications, 1); + let arena = onestore::Arena::default(); + let listed = onestore::Section::open(&arena, server.durable.clone()) + .unwrap() + .pages() + .unwrap(); + // The remote made `child` and `grandchild` subpages; `Renamed` stayed. + let at = listed.iter().position(|listed| listed.0 == pages[6].0).unwrap(); + assert_eq!(listed[at - 1].0, page.space()); let store = Store::parse(&server.durable).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); @@ -285,8 +289,9 @@ fn changed_page_anchor_conflicts_until_kept_without_regenerating_dependent_ident Kind::RichText { text, .. } if text == "Retained body")); } +/// Edits of a page the remote removed put the local version back where it stood. #[test] -fn keeping_mine_puts_back_a_page_the_remote_removed_where_it_stood() { +fn a_page_the_remote_removed_comes_back_where_it_stood() { let source = include_bytes!("../../../corpus/page-lifecycle/04-nested/notebook/Lifecycle.one"); let order = |image: &[u8]| -> Vec<(ExGuid, String, u32)> { let arena = onestore::Arena::default(); @@ -319,73 +324,61 @@ fn keeping_mine_puts_back_a_page_the_remote_removed_where_it_stood() { _ => None, }) .unwrap(); - for keep in [Resolution::Mine, Resolution::Theirs] { - let directory = tempfile::tempdir().unwrap(); - let cache = Replica::create(directory.path().join("pages.sqlite"), source).unwrap(); - let id = model_ops::save(&cache, text, |page| { - model_ops::replace_text(page, text, 0..0, "Kept ") - }) + let directory = tempfile::tempdir().unwrap(); + let cache = Replica::create(directory.path().join("pages.sqlite"), source).unwrap(); + model_ops::save(&cache, text, |page| { + model_ops::replace_text(page, text, 0..0, "Kept ") + }) + .unwrap() + .unwrap(); + let local = cache.page(space).unwrap(); + let removed = ops::section_op(source, SectionOp::Delete([space].to_vec())) .unwrap() - .unwrap(); - let local = cache.page(space).unwrap(); - let removed = onestore::PreparedEdit::delete_pages_permanently(source, &[space]) - .unwrap() - .as_bytes() - .to_vec(); - let mut server = Server::new(&removed); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::TargetUnavailable))) - ); - assert_eq!( - server.publications, 0, - "a removed page is not edited in place" - ); - cache.resolve(id, keep).unwrap(); - assert!(matches!( - cache.sync_once(&mut server).unwrap().edit, - Some((_, EditStatus::Published { .. })) - )); - let published = order(&server.durable); - assert!(published.iter().all(|(page, ..)| *page != space)); - match keep { - Resolution::Mine => { - assert_eq!(published.len(), pages.len()); - let (restored, title, restored_level) = published[at].clone(); - assert_eq!( - (title.as_str(), restored_level), - (pages[at].1.as_str(), level) - ); - let page = model_ops::page_of(&server.durable, restored); - let texts = |page: &onestore::page::Page| -> Vec { - page.objects - .iter() - .filter_map(|object| match object { - onestore::page::PageObject::Outline(outline) => Some(outline), - _ => None, - }) - .flat_map(|outline| &outline.paragraphs) - .filter_map(|p| p.text().map(|t| t.text.text().to_owned())) - .collect() - }; - assert_eq!(texts(&page), texts(&local)); - assert!(texts(&page)[0].starts_with("Kept ")); - let others: Vec<_> = published - .iter() - .map(|(page, ..)| *page) - .filter(|page| *page != restored) - .collect(); - let before: Vec<_> = pages - .iter() - .map(|(page, ..)| *page) - .filter(|page| *page != space) - .collect(); - assert_eq!(others, before); - } - Resolution::Theirs => assert_eq!(published, order(&removed)), - } - assert_eq!(pages_of(&cache), published); - } + .as_bytes() + .to_vec(); + let mut server = Server::new(&removed); + assert!(matches!( + cache.sync_once(&mut server).unwrap().edit, + Some((_, EditStatus::Published { .. })) + )); + assert_eq!( + server.publications, 1, + "a removed page is not edited in place" + ); + let published = order(&server.durable); + assert!(published.iter().all(|(page, ..)| *page != space)); + assert_eq!(published.len(), pages.len()); + let (restored, title, restored_level) = published[at].clone(); + assert_eq!( + (title.as_str(), restored_level), + (pages[at].1.as_str(), level) + ); + let page = model_ops::page_of(&server.durable, restored); + let texts = |page: &onestore::page::Page| -> Vec { + page.objects + .iter() + .filter_map(|object| match object { + onestore::page::PageObject::Outline(outline) => Some(outline), + _ => None, + }) + .flat_map(|outline| &outline.paragraphs) + .filter_map(|p| p.text().map(|t| t.text.text().to_owned())) + .collect() + }; + assert_eq!(texts(&page), texts(&local)); + assert!(texts(&page)[0].starts_with("Kept ")); + let others: Vec<_> = published + .iter() + .map(|(page, ..)| *page) + .filter(|page| *page != restored) + .collect(); + let before: Vec<_> = pages + .iter() + .map(|(page, ..)| *page) + .filter(|page| *page != space) + .collect(); + assert_eq!(others, before); + assert_eq!(pages_of(&cache), published); } fn pages_of(cache: &Replica) -> Vec<(ExGuid, String, u32)> { diff --git a/crates/notebook/tests/sync_pages.rs b/crates/notebook/tests/sync_pages.rs index 35d493870769e00b9ba018ab948e7150ad985f51..009f731609957044662bd133a4ef454a2d213550 100644 --- a/crates/notebook/tests/sync_pages.rs +++ b/crates/notebook/tests/sync_pages.rs @@ -1,10 +1,14 @@ //! Page batch reconciliation: atomic batches with dependent body saves, competing moves and -//! indentation, uncertain batches, and native page movement fixtures. +//! indentation (a page the remote moved keeps the remote's placement), uncertain batches, and +//! native page movement fixtures. -use notebook::{ConflictKind, EditStatus, Recovery, Replica, Resolution}; +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; + +use notebook::{EditStatus, Recovery, Replica}; use onestore::op::{Op, SectionOp}; use onestore::{ - ExGuid, PageEdit, PagePosition, PreparedEdit, RevisionIndex, Store, + ExGuid, PageEdit, PagePosition, RevisionIndex, Store, document::{Document, Format, Kind, Layout}, page::{ Outline, Page, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, @@ -135,12 +139,12 @@ fn atomic_page_batches_survive_reopen_with_dependent_text() { assert!( matches!(&queue[0].edit.ops[..], [Op::Section(SectionOp::Pages(batch))] if *batch == edits) ); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); drop(cache); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), queue); assert_eq!( - server::pages(&cache.snapshot().unwrap()), + server::pages(&snapshot(&cache)), server::pages(&local) ); let mut server = Server::new(SOURCE); @@ -164,7 +168,7 @@ fn atomic_page_batches_survive_reopen_with_dependent_text() { } #[test] -fn indentation_only_edits_preserve_remote_page_movement() { +fn indenting_a_page_the_remote_moved_keeps_the_remote_placement() { let directory = tempfile::tempdir().unwrap(); let cache = Replica::create(directory.path().join("pages.sqlite"), SOURCE).unwrap(); let before = order(SOURCE); @@ -174,86 +178,56 @@ fn indentation_only_edits_preserve_remote_page_movement() { SectionOp::Pages([PageEdit::set_level(sid, 3).unwrap()].to_vec()), ); let mut server = Server::new(SOURCE); - PreparedEdit::pages( - SOURCE, - &[PageEdit::move_to(sid, Some(before[7].0), 2).unwrap()], - ) + ops::section_op(SOURCE, SectionOp::Pages([PageEdit::move_to(sid, Some(before[7].0), 2).unwrap()].to_vec())) .unwrap() .commit(&mut server) .unwrap(); - let mut expected = order(&server.durable); - expected.iter_mut().find(|p| p.0 == sid).unwrap().1 = 3; + let expected = order(&server.durable); assert!(matches!(cache.sync_once(&mut server).unwrap().edit, Some((published, EditStatus::Published { .. })) if published == id)); assert_eq!(order(&server.durable), expected); } +/// A page both sides moved stays where the remote put it, as OneNote 2010 keeps it +/// (corpus/conflict-page/native-pages). #[test] -fn competing_page_moves_conflict_until_mine_is_kept_with_its_identities() { +fn competing_page_moves_keep_the_remote_placement() { let directory = tempfile::tempdir().unwrap(); let cache = Replica::create(directory.path().join("pages.sqlite"), SOURCE).unwrap(); let pages = order(SOURCE); let sid = pages[6].0; - let original = PageEdit::move_to(sid, None, 1).unwrap(); - let id = section_op(&cache, SectionOp::Pages(vec![original.clone()])); + let id = section_op(&cache, SectionOp::Pages(vec![PageEdit::move_to(sid, None, 1).unwrap()])); let mut server = Server::new(SOURCE); - PreparedEdit::pages( - SOURCE, - &[PageEdit::move_to(sid, Some(pages[0].0), 1).unwrap()], - ) + ops::section_op(SOURCE, SectionOp::Pages([PageEdit::move_to(sid, Some(pages[0].0), 1).unwrap()].to_vec())) .unwrap() .commit(&mut server) .unwrap(); - let remote = server.visible.clone(); - let local = cache.snapshot().unwrap(); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::StructureChanged))) - ); - assert_eq!(server.publications, 0); - assert_eq!( - server::pages(&cache.snapshot().unwrap()), - server::pages(&local) - ); - // Keeping mine moves the page last again, from where the remote put it. - cache.resolve(id, Resolution::Mine).unwrap(); - assert!(matches!(&cache.pending().unwrap()[0].edit.ops[..], - [Op::Section(SectionOp::Pages(batch))] if *batch == [original.clone()])); - let expected = PreparedEdit::pages(&remote, &[original]).unwrap(); + let expected = order(&server.visible); assert!(matches!(cache.sync_once(&mut server).unwrap().edit, Some((published, EditStatus::Published { .. })) if published == id)); - assert_eq!(order(&server.durable), order(expected.as_bytes())); + assert_eq!(order(&server.durable), expected); } #[test] -fn one_competing_level_prevents_publication_of_the_whole_batch() { +fn a_competing_level_keeps_the_remote_level_and_the_rest_of_the_batch() { let directory = tempfile::tempdir().unwrap(); let cache = Replica::create(directory.path().join("pages.sqlite"), SOURCE).unwrap(); let pages = order(SOURCE); - let id = section_op( - &cache, - SectionOp::Pages(vec![ - PageEdit::set_level(pages[4].0, 3).unwrap(), - PageEdit::set_level(pages[5].0, 2).unwrap(), - ]), - ); - let local = cache.snapshot().unwrap(); + let edits = vec![ + PageEdit::set_level(pages[4].0, 3).unwrap(), + PageEdit::set_level(pages[5].0, 2).unwrap(), + ]; + let id = section_op(&cache, SectionOp::Pages(edits.clone())); let mut server = Server::new(SOURCE); - PreparedEdit::pages(SOURCE, &[PageEdit::set_level(pages[5].0, 1).unwrap()]) + ops::section_op(SOURCE, SectionOp::Pages([PageEdit::set_level(pages[5].0, 1).unwrap()].to_vec())) .unwrap() .commit(&mut server) .unwrap(); - let remote = server.durable.clone(); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::StructureChanged))) - ); - assert_eq!(server.publications, 0); - assert_eq!(server.durable, remote); - assert_eq!( - server::pages(&cache.snapshot().unwrap()), - server::pages(&local) - ); + let expected = + ops::section_op(&server.durable.clone(), SectionOp::Pages(edits[..1].to_vec())).unwrap(); + assert!(matches!(cache.sync_once(&mut server).unwrap().edit, + Some((published, EditStatus::Published { .. })) if published == id)); + assert_eq!(order(&server.durable), order(expected.as_bytes())); } #[test] @@ -273,7 +247,7 @@ fn uncertain_page_batches_survive_recovery_and_do_not_replay() { PageEdit::set_level(pages[5].0, 2).unwrap(), ]; let id = section_op(&cache, SectionOp::Pages(edits.to_vec())); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let queue = cache.pending().unwrap(); let mut server = Server::new(SOURCE); server.fault = fault; @@ -293,7 +267,6 @@ fn uncertain_page_batches_survive_recovery_and_do_not_replay() { server::pages(&recovery.snapshot().unwrap()), server::pages(&local) ); - assert!(cache.resolve(id, Resolution::Mine).is_err()); let result = cache.sync_once(&mut server).unwrap().edit.unwrap(); assert_eq!(server.publications, 1); if matches!(fault, Fault::UnknownAfter | Fault::PanicAfter) { @@ -319,9 +292,23 @@ fn an_unchanged_section_revision_cannot_confirm_a_changed_page() { let copy = *view.nodes.iter().find(|(_, node)| matches!(&node.kind, Kind::Metadata { title: Some(title), .. } if title == "child")) .unwrap().0; - let source = - onestore::replace_property_bytes(SOURCE, root, copy, 0x14001dff, &3_u32.to_le_bytes()) - .unwrap(); + // The section's copy of that page's metadata says level 3, stored where it lies. + let revision = index.resolve_active(root).unwrap(); + let onestore::ObjectData::Properties(data) = revision.objects[©].data else { + panic!() + }; + let properties = onestore::PropertySets::parse(data).unwrap(); + let onestore::Value::Bytes(level) = properties.sets[0] + .iter() + .find(|property| property.id == 0x14001dff) + .unwrap() + .value + else { + panic!() + }; + let at = level.as_ptr().addr() - SOURCE.as_ptr().addr(); + let mut source = SOURCE.to_vec(); + source[at..at + 4].copy_from_slice(&3_u32.to_le_bytes()); let directory = tempfile::tempdir().unwrap(); let path = directory.path().join("pages.sqlite"); let cache = Replica::create(&path, &source).unwrap(); @@ -368,7 +355,7 @@ fn an_unchanged_section_revision_cannot_confirm_a_changed_page() { } #[test] -fn an_explicit_anchor_is_retained_even_when_originally_in_place() { +fn an_explicit_anchor_yields_to_a_remote_move() { let directory = tempfile::tempdir().unwrap(); let cache = Replica::create(directory.path().join("pages.sqlite"), SOURCE).unwrap(); let pages = order(SOURCE); @@ -380,18 +367,18 @@ fn an_explicit_anchor_is_retained_even_when_originally_in_place() { ]), ); let mut server = Server::new(SOURCE); - PreparedEdit::pages( - SOURCE, - &[PageEdit::move_to(pages[6].0, Some(pages[0].0), 1).unwrap()], - ) + ops::section_op(SOURCE, SectionOp::Pages([PageEdit::move_to(pages[6].0, Some(pages[0].0), 1).unwrap()].to_vec())) .unwrap() .commit(&mut server) .unwrap(); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::StructureChanged))) - ); - assert_eq!(server.publications, 0); + let expected = ops::section_op( + &server.durable.clone(), + SectionOp::Pages(vec![PageEdit::set_level(pages[4].0, 3).unwrap()]), + ) + .unwrap(); + assert!(matches!(cache.sync_once(&mut server).unwrap().edit, + Some((published, EditStatus::Published { .. })) if published == id)); + assert_eq!(order(&server.durable), order(expected.as_bytes())); } #[test] @@ -404,7 +391,7 @@ fn convergent_page_indentation_requires_confirmation_without_republishing() { SectionOp::Pages([PageEdit::set_level(sid, 1).unwrap()].to_vec()), ); let mut server = Server::new(SOURCE); - PreparedEdit::pages(SOURCE, &[PageEdit::set_level(sid, 1).unwrap()]) + ops::section_op(SOURCE, SectionOp::Pages([PageEdit::set_level(sid, 1).unwrap()].to_vec())) .unwrap() .commit(&mut server) .unwrap(); @@ -418,7 +405,7 @@ fn convergent_page_indentation_requires_confirmation_without_republishing() { assert_eq!(server.confirmations, 2); } #[test] -fn native_page_changes_reconcile_with_atomic_offline_batches_and_review() { +fn native_page_changes_reconcile_with_atomic_offline_batches() { let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../corpus/page-lifecycle/movement"); let provenance: serde_json::Value = @@ -455,48 +442,36 @@ fn native_page_changes_reconcile_with_atomic_offline_batches_and_review() { .unwrap(); let native = std::fs::read(root.join(phase).join("notebook/Lifecycle.one")).unwrap(); let mut server = Server::new(&native); - let local = cache.snapshot().unwrap(); - let expected_conflict = if mode == "indent" { - matches!( - phase.as_str(), - "04-subpage-move" | "07-promoted-root" | "08-collapsed-group-move" - ) - } else { - !matches!( - phase.as_str(), - "02-promoted-parent" | "03-selected-group-move" | "06-promoted-level-two" - ) + // The page list takes the edits of the pages OneNote left in their series, at + // their level and after the same page. + let both: Vec = order(&native) + .into_iter() + .map(|(sid, _)| sid) + .filter(|sid| original.iter().any(|(s, _)| s == sid)) + .collect(); + let placement = |image: &[u8]| -> BTreeMap, u32)> { + let arena = onestore::Arena::default(); + let series = onestore::Section::open(&arena, image.to_vec()).unwrap().series().unwrap(); + let shared: Vec<_> = order(image).into_iter().filter(|(sid, _)| both.contains(sid)).collect(); + (0..shared.len()) + .map(|at| { + let (sid, level) = shared[at]; + (sid, (series[&sid], at.checked_sub(1).map(|b| shared[b].0), level)) + }) + .collect() }; - let first = if expected_conflict { - cache.sync_once(&mut server).unwrap().edit.unwrap() - } else { - (text_id, EditStatus::Pending) - }; - let mut reviewed = edits.clone(); - if expected_conflict { - assert_eq!( - first.1, - EditStatus::Conflict(ConflictKind::StructureChanged), - "{mode}:{phase}" - ); - assert_eq!(server.publications, 0); - assert_eq!( - server::pages(&cache.snapshot().unwrap()), - server::pages(&local) - ); - // Keeping mine drops the moves that no longer apply. - cache.resolve(id, Resolution::Mine).unwrap(); - reviewed = match &cache.pending().unwrap()[0].edit.ops[..] { - [Op::Section(SectionOp::Pages(kept))] => kept.clone(), - [] => Vec::new(), - other => panic!("{other:?}"), - }; - } + let (before, after) = (placement(SOURCE), placement(&native)); + let reviewed: Vec = edits + .iter() + .filter(|edit| after.get(&edit.space()) == before.get(&edit.space())) + .cloned() + .collect(); + let trimmed = reviewed != edits; assert!( matches!(cache.sync_once(&mut server).unwrap().edit, Some((published, EditStatus::Published { .. })) if published == text_id), "{mode}:{phase}" ); - counts[usize::from(expected_conflict)] += 1; + counts[usize::from(trimmed)] += 1; drop(cache); let cache = Replica::open(&path).unwrap(); assert!(cache.pending().unwrap().is_empty()); @@ -525,7 +500,7 @@ fn native_page_changes_reconcile_with_atomic_offline_batches_and_review() { path.join("manifest.json"), serde_json::to_vec_pretty( &serde_json::json!({"mode": mode, "phase": phase, "original": edits, - "reviewed": reviewed, "conflict": expected_conflict, + "reviewed": reviewed, "conflict": trimmed, "publications": server.publications, "confirmations": server.confirmations, "page_receipt": format!("{:?}", cache.status(id).unwrap()), "text_receipt": format!("{:?}", cache.status(text_id).unwrap())}), @@ -536,7 +511,7 @@ fn native_page_changes_reconcile_with_atomic_offline_batches_and_review() { } } } - assert_eq!(counts, [9, 9]); + assert_eq!(counts.iter().sum::(), 18); } #[test] @@ -545,7 +520,7 @@ fn twelve_disjoint_page_batches_merge_with_dependent_bodies_without_review() { let mut created = Vec::new(); for _ in 0..36 { let page = onestore::PageCreation::new(None, Some("Same title"), "Author").unwrap(); - source = PreparedEdit::create_page(&source, &page) + source = ops::section_op(&source, SectionOp::Create(page.clone())) .unwrap() .as_bytes() .to_vec(); @@ -564,7 +539,7 @@ fn twelve_disjoint_page_batches_merge_with_dependent_bodies_without_review() { ]; section_op(&cache, SectionOp::Pages(edits.to_vec())); let body = format!("Actor {actor} 🦀 e\u{301}"); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let mut model = model_ops::page_of(&local, group[1].space()); let text = body_outline(&mut model, &body); model_ops::save_as(&cache, group[1].space(), &model, "Author") @@ -594,7 +569,7 @@ fn twelve_disjoint_page_batches_merge_with_dependent_bodies_without_review() { for (path, queue) in &queues { let cache = Replica::open(path).unwrap(); assert_eq!(cache.sync_once(&mut server).unwrap().edit, None); - assert_eq!(order(&cache.snapshot().unwrap()), expected); + assert_eq!(order(&snapshot(&cache)), expected); for edit in queue { assert!(matches!( cache.status(edit.id).unwrap(), @@ -627,3 +602,77 @@ fn twelve_disjoint_page_batches_merge_with_dependent_bodies_without_review() { .unwrap(); } } + +/// Two OneNote 2010 clients' page merges (`corpus/conflict-page/native-*`), replayed: client +/// A, offline, `moves` pages (each last, or before another) and edits `Target`'s body while +/// client B publishes `b-published`. The pages the merge lists after the first, and +/// `Target`'s space and texts. +fn native_page_merge(capture: &str, moves: &[(&str, Option<&str>)]) -> (Vec, ExGuid, Vec) { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../corpus/conflict-page") + .join(capture); + let read = |phase: &str| std::fs::read(root.join(phase).join("notebook/synthetic.one")).unwrap(); + let pages = |image: &[u8]| { + let arena = onestore::Arena::default(); + onestore::Section::open(&arena, image.to_vec()).unwrap().pages().unwrap() + }; + let initial = read("initial"); + let listed = pages(&initial); + let space = |title: &str| listed.iter().find(|page| page.1 == title).unwrap().0; + let directory = tempfile::tempdir().unwrap(); + let cache = Replica::create(directory.path().join("pages.sqlite"), &initial).unwrap(); + for (page, before) in moves { + section_op( + &cache, + SectionOp::Pages(vec![PageEdit::move_to(space(page), before.map(space), 1).unwrap()]), + ); + } + let body = texts(&initial) + .into_iter() + .find(|(_, text)| text == "Body Target.") + .unwrap() + .0 + .1; + model_ops::save(&cache, body, |page| { + model_ops::replace_text(page, body, 0..12, "Body Target edited offline.") + }) + .unwrap() + .unwrap(); + let mut server = Server::new(&read("b-published")); + for _ in 0..moves.len() + 2 { + cache.sync_once(&mut server).unwrap(); + } + assert!(cache.pending().unwrap().is_empty(), "{capture}"); + let merged = pages(&server.durable); + let target = merged.iter().find(|page| page.1 == "Target").unwrap().0; + let texts = texts(&server.durable) + .into_iter() + .filter(|((page, _), _)| *page == target) + .map(|(_, text)| text) + .collect(); + (merged[1..].iter().map(|page| page.1.clone()).collect(), target, texts) +} + +/// Each side keeps the pages it gave a series of their own, the remote's where both did; +/// a page the remote deleted comes back as a new page where the queue had it. OneNote's +/// COM moves re-series the pages a page jumps over, so its moves replay as moves of those. +#[test] +fn page_moves_and_a_removed_page_merge_as_onenote_merges_them() { + for (capture, moves) in [ + ("native-pages", &[("One", Some("Target")), ("Three", Some("Target")), ("Two", None)][..]), + ("native-restore", &[("One", None), ("Two", None), ("Target", None), ("Three", None)][..]), + ("native-restore", &[("Four", Some("One"))][..]), + ] { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus/conflict-page"); + let native: serde_json::Value = + serde_json::from_slice(&std::fs::read(root.join(capture).join("merged.json")).unwrap()).unwrap(); + let (order, target, texts) = native_page_merge(capture, moves); + let native: Vec = serde_json::from_value(native["order"].clone()).unwrap(); + assert_eq!(order, native[1..], "{capture}"); + assert!(texts.contains(&"Body Target edited offline.".to_owned()), "{capture}"); + let arena = onestore::Arena::default(); + let initial = std::fs::read(root.join(capture).join("initial/notebook/synthetic.one")).unwrap(); + let listed = onestore::Section::open(&arena, initial).unwrap().pages().unwrap(); + assert!(listed.iter().all(|page| page.0 != target), "{capture}: a new page"); + } +} diff --git a/crates/notebook/tests/sync_paragraph.rs b/crates/notebook/tests/sync_paragraph.rs index 787b98694edef420111e836f4c98496766537612..aa54b56ff2c2db8b1f699f2636c475467a7034c4 100644 --- a/crates/notebook/tests/sync_paragraph.rs +++ b/crates/notebook/tests/sync_paragraph.rs @@ -1,9 +1,12 @@ //! Paragraph splits and joins expressed as page-model differences: reconciliation against //! native OneNote captures, dependent edits, uncertain attempts and competing clients. -use notebook::{ConflictKind, EditStatus, Replica}; +#[path = "../../onestore/tests/support/ops.rs"] +mod ops; + +use notebook::{EditStatus, Replica}; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::Document, page::{Page, PageObject, PageParagraph, ParagraphContent, TextObject, text::new_id}, }; @@ -11,7 +14,7 @@ use std::path::Path; #[path = "support/server.rs"] mod server; -use server::{Fault, Server, pages}; +use server::{Fault, Server, conflicts, pages, snapshot, typed}; #[path = "support/model_ops.rs"] mod model_ops; use model_ops::{AUTHOR, outlines_mut, page_of, replace_text}; @@ -175,18 +178,27 @@ fn save_page( text: ExGuid, edit: impl FnOnce(&mut Page), ) -> Result, notebook::Error> { - let source = cache.snapshot()?; + let source = snapshot(cache); let (space, mut page) = holder(&source, text); edit(&mut page); model_ops::save_as(cache, space, &page, AUTHOR) } -/// Resolves a conflict by taking the remote page, then saves `edit` of it. -fn review_page(cache: &Replica, id: u64, text: ExGuid, edit: impl FnOnce(&mut Page)) { - cache.resolve(id, notebook::Resolution::Theirs).unwrap(); +/// Makes `edit` again on the page as the remote's version left it, as a writer does after +/// a conflict page kept the first try. +fn redo_page(cache: &Replica, text: ExGuid, edit: impl FnOnce(&mut Page)) { save_page(cache, text, edit).unwrap().unwrap(); } +/// The conflict pages `image` lists under page `space`. +fn conflict_pages(image: &[u8], space: ExGuid) -> usize { + conflicts(image) + .into_iter() + .filter(|(page, _)| *page == space) + .map(|(_, pages)| pages.len()) + .sum() +} + /// The styled text of an object anywhere on a page. fn content(page: &Page, text: ExGuid) -> onestore::page::Paragraph { locate_in(&mut page.clone(), text, |list, index| { @@ -250,12 +262,15 @@ fn reconcile_controls() -> (Vec, Vec) { drop(cache); cache = Replica::open(&path).unwrap(); assert_eq!(cache.pending().unwrap(), pending); - let (actual, status) = cache.sync_once(&mut server).unwrap().edit.unwrap(); - assert_eq!(actual, id); - let reviewed = matches!(status, EditStatus::Conflict(_)); + let kept = conflict_pages(&server.visible, space); + let (_, status) = cache.sync_once(&mut server).unwrap().edit.unwrap(); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + let reviewed = conflict_pages(&server.visible, space) > kept; let receipt = if reviewed { - assert_eq!(cache.pending().unwrap(), pending); - review_page(&cache, id, left, apply); + redo_page(&cache, left, apply); cache.sync_once(&mut server).unwrap().edit.unwrap().1 } else { automatic += 1; @@ -301,7 +316,7 @@ fn reconcile_controls() -> (Vec, Vec) { panic!("{name} dependent: {status:?}") }; assert!(cache.pending().unwrap().is_empty()); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); recorded.push( serde_json::json!({"case": name, "space": space, "intent": intent, "outcome": if reviewed { "reviewed" } else { "automatic" }, @@ -309,12 +324,13 @@ fn reconcile_controls() -> (Vec, Vec) { ); } assert_eq!((recorded.len(), automatic), (16, 8)); - assert_eq!(server.publications, 32); + // Each edit and its dependent, and each conflict page before its edit made again. + assert_eq!(server.publications, 32 + 16 - automatic); (server.durable, recorded) } #[test] -fn native_keyboard_edits_merge_with_offline_splits_and_joins_or_survive_review() { +fn native_keyboard_edits_merge_with_offline_splits_and_joins_or_keep_a_conflict_page() { let (durable, cases) = reconcile_controls(); let expected = [ ("Split remote prefix", "automatic", vec!["Xab", "🦀Cd"]), @@ -349,8 +365,9 @@ fn native_keyboard_edits_merge_with_offline_splits_and_joins_or_survive_review() } #[test] -fn a_native_text_object_replacement_conflicts_with_every_offline_split_and_join() { +fn a_native_text_object_replacement_keeps_a_conflict_page_for_every_offline_split_and_join() { let mut server = Server::new(REPLACED); + let mut count = 0; for (space, name, split) in controls() { let original = page_of(BEFORE, space); let left = body(&original)[0].text().unwrap().id; @@ -366,24 +383,23 @@ fn a_native_text_object_replacement_conflicts_with_every_offline_split_and_join( let id = model_ops::save_as(&cache, space, &after, AUTHOR) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); - let pending = cache.pending().unwrap(); - assert_eq!( - cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))), + let remote = page_of(&server.visible, space); + assert!( + matches!( + cache.sync_once(&mut server).unwrap().edit, + Some((_, EditStatus::Published { .. })) + ), "{name}" ); - drop(cache); - let cache = Replica::open(&path).unwrap(); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); - assert_eq!(cache.pending().unwrap(), pending); - assert_eq!( - cache.status(id).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) + assert!( + matches!(cache.status(id).unwrap(), Some(EditStatus::Published { .. })), + "{name}" ); + assert_eq!(page_of(&server.visible, space), remote, "{name}"); + assert_eq!(conflict_pages(&server.visible, space), 1, "{name}"); + count += 1; } - assert_eq!(server.publications, 0); - assert_eq!(server.visible, REPLACED); + assert_eq!(server.publications, count); } /// Replays a recorded native split or join intent as a page-model difference. @@ -459,7 +475,7 @@ fn offline_paragraphs(output: Option<&Path>) { { let intent = &case["intent"]; let left = origin(intent, split); - let (space, page) = holder(&cache.snapshot().unwrap(), left); + let (space, page) = holder(&snapshot(&cache), left); let offset = if split { intent["offset"].as_u64().unwrap() } else { @@ -468,7 +484,7 @@ fn offline_paragraphs(output: Option<&Path>) { let prefixed = offset > 0 && !content(&page, left).text().is_empty(); if prefixed { server.visible = - onestore::replace_text(&server.visible, space, left, 0..0, "\u{2602}").unwrap(); + typed(&server.visible, space, left, 0..0, "\u{2602}"); server.durable.clone_from(&server.visible); } let Ok(queued) = save_page(&cache, left, |page| replay(page, intent, split)) else { @@ -484,6 +500,8 @@ fn offline_paragraphs(output: Option<&Path>) { let case = entry["case"].as_str().unwrap().to_owned(); let intent = entry["intent"].clone(); let mut uncertain = id % 2 == 0; + let mut kept = conflict_pages(&server.visible, space); + let mut id = id; let revision = loop { if uncertain { server.fault = Fault::UnknownAfter; @@ -501,7 +519,7 @@ fn offline_paragraphs(output: Option<&Path>) { Ok(notebook::Synced { edit: Some((_, EditStatus::Published { revision })), .. - }) => { + }) if conflict_pages(&server.visible, space) == kept => { assert!(matches!( cache.status(id).unwrap(), Some(EditStatus::Published { .. }) @@ -509,20 +527,21 @@ fn offline_paragraphs(output: Option<&Path>) { break revision; } Ok(notebook::Synced { - edit: Some((actual, EditStatus::Conflict(_))), + edit: Some((_, EditStatus::Published { .. })), .. }) => { - assert_eq!(actual, id); + kept += 1; server.fault = Fault::None; reviewed.push(case.clone()); - // The reviewed split sits after the remote's one-unit prefix. + // The split made again sits after the remote's one-unit prefix. let mut placed = intent.clone(); if split && entry["remote_prefix"] == true { placed["offset"] = (intent["offset"].as_u64().unwrap() + 1).into(); } - review_page(&cache, id, origin(&intent, split), |page| { + redo_page(&cache, origin(&intent, split), |page| { replay(page, &placed, split) }); + id = cache.pending().unwrap().last().unwrap().id; } other => panic!("{name} {case}: {other:?}"), } @@ -543,7 +562,7 @@ fn offline_paragraphs(output: Option<&Path>) { ); assert_eq!(server.publications, entries.len()); assert_eq!(cache.sync_once(&mut server).unwrap().edit, None); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); if output.is_some() { cache .export_recovery(folder.join("recovery.sqlite")) @@ -573,9 +592,9 @@ fn remote_with(space: ExGuid, change: impl FnOnce(&mut Page)) -> Server { let mut page = page_of(BEFORE, space); change(&mut page); Server::new( - PreparedEdit::page(BEFORE, space, &page, "Native author") + ops::saved(BEFORE, space, &page) .unwrap() - .as_bytes(), + .as_slice(), ) } @@ -611,7 +630,7 @@ fn a_split_publishes_two_paragraphs_and_a_join_puts_them_back() { ["ab\u{1f980}cd", "Right", "Preserved sibling"] ); assert_eq!(server.publications, 2); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); } #[test] @@ -637,7 +656,7 @@ fn a_split_merges_with_a_remote_edit_to_another_paragraph() { } #[test] -fn a_join_conflicts_when_the_remote_changed_the_paragraph_it_removes() { +fn a_join_keeps_a_conflict_page_when_the_remote_changed_the_paragraph_it_removes() { let (space, page) = control("Join remote prefix"); let (left, right) = ( body(&page)[0].text().unwrap().id, @@ -649,12 +668,16 @@ fn a_join_conflicts_when_the_remote_changed_the_paragraph_it_removes() { .unwrap() .unwrap(); let mut server = remote_with(space, |page| replace_text(page, right, 0..0, "Remote ")); - assert_eq!( + assert!(matches!( cache.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - assert_eq!(server.publications, 0); - review_page(&cache, id, left, |page| join_paragraphs(page, left, right)); + Some((_, EditStatus::Published { .. })) + )); + assert!(matches!( + cache.status(id).unwrap(), + Some(EditStatus::Published { .. }) + )); + assert_eq!(conflict_pages(&server.durable, space), 1); + redo_page(&cache, left, |page| join_paragraphs(page, left, right)); assert!(matches!( cache.sync_once(&mut server).unwrap().edit, Some((_, EditStatus::Published { .. })) @@ -680,7 +703,7 @@ fn edits_dependent_on_a_published_split_survive_reopen() { .unwrap() .unwrap(); cache.sync_once(&mut server).unwrap().edit.unwrap(); - let tail = body(&page_of(&cache.snapshot().unwrap(), space))[1] + let tail = body(&page_of(&snapshot(&cache), space))[1] .text() .unwrap() .id; @@ -689,17 +712,17 @@ fn edits_dependent_on_a_published_split_survive_reopen() { }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let pending = cache.pending().unwrap(); drop(cache); cache = Replica::open(&path).unwrap(); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); assert_eq!(cache.pending().unwrap(), pending); let mut remote = page_of(&server.durable, space); replace_text(&mut remote, other, 0..0, "Remote "); - server.visible = PreparedEdit::page(&server.durable, space, &remote, "Native author") + server.visible = ops::saved(&server.durable, space, &remote) .unwrap() - .as_bytes() + .as_slice() .to_vec(); server.durable.clone_from(&server.visible); assert!( @@ -742,7 +765,7 @@ fn uncertain_splits_and_joins_keep_the_original_attempt_across_reopen() { }) .unwrap() .unwrap(); - let local = cache.snapshot().unwrap(); + let local = snapshot(&cache); let pending = cache.pending().unwrap(); let mut server = Server::new(BEFORE); server.fault = fault; @@ -764,7 +787,7 @@ fn uncertain_splits_and_joins_keep_the_original_attempt_across_reopen() { Fault::UnknownBefore => { assert!(matches!(state, EditStatus::AwaitingConfirmation { .. })); assert_eq!(cache.pending().unwrap(), pending); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); for _ in 0..3 { assert_eq!( cache.sync_once(&mut server).unwrap().edit, @@ -777,7 +800,7 @@ fn uncertain_splits_and_joins_keep_the_original_attempt_across_reopen() { } Fault::UnknownAfter => { assert!(matches!(state, EditStatus::AwaitingConfirmation { .. })); - assert_eq!(pages(&cache.snapshot().unwrap()), pages(&local)); + assert_eq!(pages(&snapshot(&cache)), pages(&local)); assert!(matches!( cache.sync_once(&mut server).unwrap().edit.unwrap().1, EditStatus::Published { .. } @@ -797,13 +820,13 @@ fn uncertain_splits_and_joins_keep_the_original_attempt_across_reopen() { }; assert_eq!(texts(&page_of(&server.durable, space)), expected); assert_eq!(cache.sync_once(&mut server).unwrap().edit, None); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); } } } #[test] -fn a_competing_join_leaves_the_other_client_a_reviewable_split() { +fn a_competing_join_leaves_the_other_client_its_split_on_a_conflict_page() { let (space, page) = control("Join remote prefix"); let (left, right) = ( body(&page)[0].text().unwrap().id, @@ -819,27 +842,24 @@ fn a_competing_join_leaves_the_other_client_a_reviewable_split() { let id = save_page(&second, right, |page| split_paragraph(page, right, 2)) .unwrap() .unwrap(); - let local = second.snapshot().unwrap(); - let pending = second.pending().unwrap(); let mut server = Server::new(BEFORE); assert!(matches!( first.sync_once(&mut server).unwrap().edit.unwrap().1, EditStatus::Published { .. } )); - assert_eq!( + assert!(matches!( second.sync_once(&mut server).unwrap().edit, - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - assert_eq!(server.publications, 1); + Some((_, EditStatus::Published { .. })) + )); + assert_eq!(server.publications, 2); + assert_eq!(conflict_pages(&server.durable, space), 1); drop(second); let second = Replica::open(&path).unwrap(); - assert_eq!(pages(&second.snapshot().unwrap()), pages(&local)); - assert_eq!(second.pending().unwrap(), pending); - assert_eq!( + assert!(matches!( second.status(id).unwrap(), - Some(EditStatus::Conflict(ConflictKind::ContentChanged)) - ); - review_page(&second, id, left, |page| { + Some(EditStatus::Published { .. }) + )); + redo_page(&second, left, |page| { split_paragraph(page, left, 2); }); assert!(matches!( diff --git a/crates/notebook/tests/sync_worker.rs b/crates/notebook/tests/sync_worker.rs index ecf4189e963aaed6629d73fc19acad8f23059d34..14a63b9e1b065f3b5d31046a883c3cc2e1452b5a 100644 --- a/crates/notebook/tests/sync_worker.rs +++ b/crates/notebook/tests/sync_worker.rs @@ -1,6 +1,7 @@ //! The background worker: waking, backoff, ownership and error propagation. -use notebook::{ConflictKind, EditStatus, Error, Remote, Replica}; +use notebook::{EditStatus, Error, Remote, Replica}; +use onestore::Stamp; use onestore::{ CommitError, CommitState, ExGuid, RevisionIndex, Store, Transaction, document::Document, }; @@ -25,14 +26,14 @@ impl Remote for Shared { fn read(&mut self) -> io::Result> { self.0.lock().unwrap().read() } - fn stamp(&mut self) -> io::Result> { + fn stamp(&mut self) -> io::Result { self.0.lock().unwrap().stamp() } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.0.lock().unwrap().publish(transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.0.lock().unwrap().confirm(snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.0.lock().unwrap().confirm(base) } } @@ -75,11 +76,17 @@ fn reconnects_after_connect_read_and_uncertain_publish_without_replaying() { } self.shared.read() } + fn stamp(&mut self) -> io::Result { + if self.fail_read { + return Err(io::ErrorKind::ConnectionReset.into()); + } + self.shared.stamp() + } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.shared.publish(transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.shared.confirm(snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.shared.confirm(base) } } let dir = tempfile::tempdir().unwrap(); @@ -142,7 +149,7 @@ fn reconnects_after_connect_read_and_uncertain_publish_without_replaying() { drop(cache); let cache = Replica::open(&path).unwrap(); assert_eq!(cache.status(id).unwrap(), Some(published)); - let cached = cache.snapshot().unwrap(); + let cached = snapshot(&cache); // Confirmation changes reader-notification fields without changing the committed graph. assert_eq!(cached[..212], server.durable[..212]); assert_eq!(cached[252..], server.durable[252..]); @@ -159,7 +166,7 @@ fn reconnects_after_connect_read_and_uncertain_publish_without_replaying() { fn local_saves_wake_an_idle_worker_and_publish_every_writer_marker() { let dir = tempfile::tempdir().unwrap(); let source = onestore::create_section("worker.one", "abc", "Fixture").unwrap(); - let (_, oid, _) = text(&source); + let (sid, oid, _) = text(&source); let cache = Arc::new(Replica::create(dir.path().join("cache.sqlite"), &source).unwrap()); let server = Arc::new(Mutex::new(Server::new(&source))); let shared = Shared(Arc::clone(&server)); @@ -190,13 +197,19 @@ fn local_saves_wake_an_idle_worker_and_publish_every_writer_marker() { (0..12) .map(|writer| { let cache = &cache; + // Typed as an op: a page read before another writer's edit would lower stale. scope.spawn(move || { - let marker = format!("[{writer}] "); - model_ops::save(cache, oid, |page| { - model_ops::replace_text(page, oid, 0..0, &marker) - }) - .unwrap() - .unwrap() + let op = onestore::op::PageOp::Text { + text: oid, + range: 0..0, + with: format!("[{writer}] "), + }; + let ops = vec![onestore::op::Op::Page { space: sid, op }]; + let edit = onestore::op::Edit { + at: model_ops::now(), + ops, + }; + cache.apply(model_ops::AUTHOR, edit).unwrap() }) }) .collect::>() @@ -221,7 +234,7 @@ fn local_saves_wake_an_idle_worker_and_publish_every_writer_marker() { Some(EditStatus::Published { .. }) )); } - let content = text(&cache.snapshot().unwrap()).2; + let content = text(&snapshot(&cache)).2; for writer in 0..12 { assert_eq!(content.matches(&format!("[{writer}] ")).count(), 1); } @@ -230,7 +243,7 @@ fn local_saves_wake_an_idle_worker_and_publish_every_writer_marker() { assert_eq!(text(&server.durable).2, content); // Edits queued while the worker was busy publish together. assert_eq!(server.publications, 1); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!(snapshot(&cache), server.durable); } #[test] @@ -244,13 +257,16 @@ fn dropping_during_publication_is_nonblocking_and_retains_ownership_until_recove fn read(&mut self) -> io::Result> { self.shared.read() } + fn stamp(&mut self) -> io::Result { + self.shared.stamp() + } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { self.entered.send(()).unwrap(); self.resume.recv_timeout(Duration::from_secs(5)).unwrap(); self.shared.publish(transaction) } - fn confirm(&mut self, snapshot: &[u8]) -> Result<(), CommitError> { - self.shared.confirm(snapshot) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.shared.confirm(base) } } let dir = tempfile::tempdir().unwrap(); @@ -358,81 +374,65 @@ fn cache_failures_stop_retries_and_return_the_error_without_remote_publication() assert!(rx.try_iter().next().is_none()); assert_eq!(server.lock().unwrap().publications, 0); assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); - assert_eq!(text(&cache.snapshot().unwrap()).2, "L abc"); + assert_eq!(text(&snapshot(&cache)).2, "L abc"); } #[test] -fn polling_reports_a_blocked_queue_once_per_remote_change_without_replay() { - for uncertain in [false, true] { - let dir = tempfile::tempdir().unwrap(); - let source = onestore::create_section("worker.one", "abc", "Fixture").unwrap(); - let (sid, oid, _) = text(&source); - let cache = Arc::new(Replica::create(dir.path().join("cache.sqlite"), &source).unwrap()); - let id = save(&cache, oid, 1..2, "L").unwrap(); - let local = text(&cache.snapshot().unwrap()).2; - let mut server = if uncertain { - Server::new(&source) - } else { - Server::new(&onestore::replace_text(&source, sid, oid, 1..2, "R").unwrap()) - }; - if uncertain { - server.fault = Fault::UnknownBefore; - } - let server = Arc::new(Mutex::new(server)); - let shared = Shared(Arc::clone(&server)); - let (tx, rx) = mpsc::channel(); - let worker = cache - .start_sync( - Duration::from_millis(10), - move || Ok(shared.clone()), - move |result| { - tx.send( - result - .as_ref() - .map(|synced| synced.edit.clone()) - .map_err(|_| ()), - ) - .unwrap(); - }, - ) +fn polling_reports_an_uncertain_attempt_once_per_remote_change_without_replay() { + let dir = tempfile::tempdir().unwrap(); + let source = onestore::create_section("worker.one", "abc", "Fixture").unwrap(); + let (sid, oid, _) = text(&source); + let cache = Arc::new(Replica::create(dir.path().join("cache.sqlite"), &source).unwrap()); + let id = save(&cache, oid, 1..2, "L").unwrap(); + let local = text(&snapshot(&cache)).2; + let mut server = Server::new(&source); + server.fault = Fault::UnknownBefore; + let server = Arc::new(Mutex::new(server)); + let shared = Shared(Arc::clone(&server)); + let (tx, rx) = mpsc::channel(); + let worker = cache + .start_sync( + Duration::from_millis(10), + move || Ok(shared.clone()), + move |result| { + tx.send( + result + .as_ref() + .map(|synced| synced.edit.clone()) + .map_err(|_| ()), + ) + .unwrap(); + }, + ) + .unwrap(); + assert!(rx.recv_timeout(Duration::from_secs(5)).unwrap().is_err()); + for round in 0..3 { + let (actual, status) = rx + .recv_timeout(Duration::from_secs(5)) + .unwrap() + .unwrap() .unwrap(); - if uncertain { - assert!(rx.recv_timeout(Duration::from_secs(5)).unwrap().is_err()); - } - let expected = |status: &EditStatus| { - if uncertain { - matches!(status, EditStatus::AwaitingConfirmation { .. }) - } else { - *status == EditStatus::Conflict(ConflictKind::ContentChanged) - } - }; - for round in 0..3 { - let (actual, status) = rx - .recv_timeout(Duration::from_secs(5)) - .unwrap() - .unwrap() - .unwrap(); - assert_eq!(actual, id); - assert!(expected(&status), "{status:?}"); - // An unchanged remote is not read or reported again. - assert!(rx.recv_timeout(Duration::from_millis(100)).is_err()); - // Another writer's change elsewhere in the text is read and decided again. - let mut server = server.lock().unwrap(); - let changed = - onestore::replace_text(&server.visible, sid, oid, 0..0, &round.to_string()) - .unwrap(); - server.visible = changed.clone(); - server.durable = changed; - drop(server); - worker.wake(); - } - worker.stop().unwrap(); - assert_eq!(text(&cache.snapshot().unwrap()).2, local); - assert_eq!(cache.pending().unwrap().len(), 1); - let server = server.lock().unwrap(); - assert_eq!(server.publications, usize::from(uncertain)); - assert_eq!(server.confirmations, 0); + assert_eq!(actual, id); + assert!( + matches!(status, EditStatus::AwaitingConfirmation { .. }), + "{status:?}" + ); + // An unchanged remote is not read or reported again. + assert!(rx.recv_timeout(Duration::from_millis(100)).is_err()); + // Another writer's change elsewhere in the text is read and decided again. + let mut server = server.lock().unwrap(); + let changed = typed(&server.visible, sid, oid, 0..0, &round.to_string()); + server.visible = changed.clone(); + server.durable = changed; + drop(server); + worker.wake(); } + worker.stop().unwrap(); + assert_eq!(text(&snapshot(&cache)).2, local); + assert_eq!(cache.pending().unwrap().len(), 1); + let server = server.lock().unwrap(); + assert_eq!(server.publications, 1); + assert_eq!(server.confirmations, 0); } #[test] @@ -533,51 +533,46 @@ fn invalid_intervals_and_callback_panics_leave_worker_ownership_recoverable() { .unwrap(); assert_eq!(rx.recv_timeout(Duration::from_secs(5)).unwrap(), None); worker.stop().unwrap(); - assert_eq!(cache.snapshot().unwrap(), source); + assert_eq!(snapshot(&cache), source); } +/// A conflict never blocks the worker: one step publishes the remote's version with the +/// local one as its conflict page. #[test] -fn a_resolved_conflict_wakes_the_worker_and_publishes_the_original_edit_once() { +fn a_conflict_publishes_its_conflict_page_in_one_worker_step() { let dir = tempfile::tempdir().unwrap(); let source = onestore::create_section("resolve.one", "abc", "Fixture").unwrap(); let (sid, oid, _) = text(&source); let cache = Arc::new(Replica::create(dir.path().join("cache.sqlite"), &source).unwrap()); let id = save(&cache, oid, 1..2, "L").unwrap(); - let remote = onestore::replace_text(&source, sid, oid, 1..2, "R").unwrap(); + let remote = typed(&source, sid, oid, 1..2, "R"); let server = Arc::new(Mutex::new(Server::new(&remote))); let shared = Shared(Arc::clone(&server)); let (tx, rx) = mpsc::channel(); - let (resume_tx, resume_rx) = mpsc::channel(); - let mut first = true; let worker = cache .start_sync( Duration::from_secs(3600), move || Ok(shared.clone()), move |result| { tx.send(result.as_ref().unwrap().edit.clone()).unwrap(); - if first { - first = false; - resume_rx.recv_timeout(Duration::from_secs(5)).unwrap(); - } }, ) .unwrap(); - assert_eq!( - rx.recv_timeout(Duration::from_secs(5)).unwrap(), - Some((id, EditStatus::Conflict(ConflictKind::ContentChanged))) - ); - cache.resolve(id, notebook::Resolution::Mine).unwrap(); - assert_eq!(cache.status(id).unwrap(), Some(EditStatus::Pending)); - resume_tx.send(()).unwrap(); assert!( - matches!(rx.recv_timeout(Duration::from_secs(5)).unwrap(), Some((actual, EditStatus::Published { .. })) if actual == id) + matches!(rx.recv_timeout(Duration::from_secs(5)).unwrap(), Some((actual, EditStatus::Published { .. })) if actual > id) ); worker.stop().unwrap(); assert!(cache.pending().unwrap().is_empty()); let server = server.lock().unwrap(); assert_eq!(server.publications, 1); - assert_eq!(text(&server.durable).2, "aLc"); - assert_eq!(cache.snapshot().unwrap(), server.durable); + assert_eq!( + conflicts(&server.durable), + [( + sid, + vec![(model_ops::AUTHOR.to_owned(), vec!["aLc".to_owned()])] + )] + ); + assert_eq!(snapshot(&cache), server.durable); } #[test] @@ -589,11 +584,14 @@ fn ordinary_read_and_unpublished_write_contention_reuse_the_connection() { } impl Remote for Busy { fn read(&mut self) -> io::Result> { + self.server.read() + } + fn stamp(&mut self) -> io::Result { self.reads += 1; match self.reads { 1 => Err(io::ErrorKind::WouldBlock.into()), 2 => Err(io::ErrorKind::ResourceBusy.into()), - _ => self.server.read(), + _ => self.server.stamp(), } } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { @@ -611,8 +609,8 @@ fn ordinary_read_and_unpublished_write_contention_reuse_the_connection() { _ => self.server.publish(transaction), } } - fn confirm(&mut self, source: &[u8]) -> Result<(), CommitError> { - self.server.confirm(source) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.server.confirm(base) } } let dir = tempfile::tempdir().unwrap(); @@ -648,7 +646,7 @@ fn ordinary_read_and_unpublished_write_contention_reuse_the_connection() { matches!(rx.recv_timeout(Duration::from_secs(5)).unwrap().unwrap(), Some((actual, EditStatus::Published { .. })) if actual == id) ); worker.stop().unwrap(); - assert_eq!(text(&cache.snapshot().unwrap()).2, "L abc"); + assert_eq!(text(&snapshot(&cache)).2, "L abc"); assert!(cache.pending().unwrap().is_empty()); } @@ -659,6 +657,9 @@ fn publication_backoff_drains_local_wakes_without_waiting_for_the_idle_poll() { fn read(&mut self) -> io::Result> { self.0.read() } + fn stamp(&mut self) -> io::Result { + self.0.stamp() + } fn publish(&mut self, transaction: &Transaction) -> Result<(), CommitError> { let server = &mut self.0; if server.publications == 0 { @@ -670,8 +671,8 @@ fn publication_backoff_drains_local_wakes_without_waiting_for_the_idle_poll() { } server.publish(transaction) } - fn confirm(&mut self, source: &[u8]) -> Result<(), CommitError> { - self.0.confirm(source) + fn confirm(&mut self, base: &Stamp) -> Result<(), CommitError> { + self.0.confirm(base) } } let dir = tempfile::tempdir().unwrap(); @@ -714,6 +715,6 @@ fn publication_backoff_drains_local_wakes_without_waiting_for_the_idle_poll() { } worker.stop().unwrap(); assert!(cache.pending().unwrap().is_empty()); - assert_eq!(content(&cache.snapshot().unwrap(), oid), "L abc"); - assert_eq!(pages(&cache.snapshot().unwrap()), 2); + assert_eq!(content(&snapshot(&cache), oid), "L abc"); + assert_eq!(pages(&snapshot(&cache)), 2); } diff --git a/crates/onestore/README.md b/crates/onestore/README.md index c969393e74c3777ff8a682e6383fcaa4f92c3cb4..09df6594d155a27c8b088d5e36ab03d4fdbfcec5 100644 --- a/crates/onestore/README.md +++ b/crates/onestore/README.md @@ -49,32 +49,27 @@ harness also accepts `--client-profile release`. | `page::Page`, `page::Paragraph`, `page::Ink`, `page::Math` | Build an editable page model (title, outlines, paragraphs with coalesced text spans, tables, images, attachments, ink drawings and handwriting decoded to stroke polylines in page points) with stored identities; equations parse from their linear text and run data into a tree that renders the MathML OneNote exports; content outside the model is retained as `Unsupported` | | `protected::UnlockedSection` (optional feature) | Own decoded buffers for explicit known-password inspection; clear those buffers on drop; derived document strings/exports remain caller-owned | | `create_section` | Create one page containing one plain-text paragraph and an author, including Unicode | -| `PageCreation`, `PreparedEdit::create_page` | Add an empty top-level page and its section entry atomically, retaining page identities across retries | -| `PageEdit`, `PreparedEdit::pages` | Publish explicitly selected page moves and indentation changes together, preserving page content and historical revisions | -| `PreparedEdit::delete_pages_permanently` | Remove explicit pages and their section references atomically while retaining stored revisions | +| `PageCreation`, `SectionOp::Create` | Add an empty top-level page and its section entry atomically, under identities the intent retains across retries | +| `PageEdit`, `SectionOp::Pages` | Publish explicitly selected page moves and indentation changes together, preserving page content and historical revisions | +| `SectionOp::Delete` | Remove explicit pages and their section references atomically while retaining stored revisions | | `create_table_of_contents` | Create ordered section entries from filenames and file identities | -| `TocEdit`, `PreparedEdit::table_of_contents` | Add, rename, colour, order and remove a table of contents' section and group entries as one revision | +| `TocEdit`, `edit_table_of_contents` | Add, rename, order and remove a table of contents' section and group entries, and colour the notebook, as one revision's `Transaction`; a section's colour is `SectionOp::Color` in its own metadata | | `place`, `place_file` | Name a file for its notebook as OneNote does on adoption (parent TOC identity and name CRC in the header), so OneNote keeps its identity, on any `CommitIo` or under the filesystem adapter | -| `replace_property_bytes` | Append one scalar-property revision; preserve prior revisions and unrelated property values and references | -| `replace_text`, `commit_text`, `commit_file_text` | Replace a UTF-16 range across ordinary text runs; publish text, run boundaries and modification time together | -| `Insertion`, `PreparedEdit::insert` | Insert paragraphs into editable containers or positioned outlines into a page, retaining intent identities across rebases | -| `ParagraphSplit`, `PreparedEdit::split` | Split ordinary text at a UTF-16 scalar boundary, retaining the original left identities and moving children to the right | -| `ParagraphJoin`, `PreparedEdit::join` | Join adjacent ordinary text while preserving inherited character styles and native text-identity rules | -| `TextAttribute`, `PreparedEdit::format` | Change character formatting over a UTF-16 range while sharing immutable styles; preserve unselected runs | -| `OutlineEdit`, `PreparedEdit::outline` | Change ordinary outline position/width or a paragraph's saved expansion default, preserving identities and content | -| `PreparedEdit::page` | Publish an edited `page::Page` as one revision per changed space: text, character and paragraph formatting, hyperlinks (external and, via `page::link`, to pages, paragraphs and sections), bullets and numbering, note tags, table rows, columns, cell shading and indents, nested tables, inserted pictures (in paragraphs or on the page) and attachments, picture position, size and description, a stored attachment's shown name, source path and icon size, paragraph styles on new paragraphs, ink drawings and handwriting (strokes added and erased), equations (built from `page::Math` trees), paragraph insertion/split/join/move/deletion, outline insertion/deletion/position/width and saved collapse state, lowered onto the typed writers with the model's identities | -| `TreeEdit`, `PreparedEdit::tree` | Move or delete a subtree on one page, normalize surviving containers, and replace an emptied table cell's paragraph atomically | -| `Transaction`, `Stamp`, `PreparedEdit::transaction` | The bytes a commit writes (appended data, in-place list-tail and log patches, header) and the header and length it requires unchanged; `commit` under caller-held exclusion, `commit_file` under the conservative filesystem adapter, `apply` to the base image in memory; serializable for queues | +| `TextAttribute`, `PageOp::Format` | Change character formatting over a UTF-16 range while sharing immutable styles; preserve unselected runs | +| `OutlineEdit`, `PageOp::Outline` | Change ordinary outline position/width or a paragraph's saved expansion default, preserving identities and content | +| `Transaction`, `Stamp` | The bytes a commit writes (appended data, in-place list-tail and log patches, header) and the header and length it requires unchanged; `commit` under caller-held exclusion, `commit_file` under the conservative filesystem adapter, `apply` to the base image in memory; serializable for queues | | `Arena`, `Section` | Keep a section parsed across edits: `open` validates an image once; `seal` appends one revision per changed space as a `Transaction` on `stamp`, checking only what it appends; `replay` applies a queued transaction; `image` and `page` read the result | -| `op::{Edit, Op, PageOp, TableEdit, SectionOp}`, `Section::apply` | Object-level edits (text, formatting, links, equations, paragraph insertion/split/join/move/deletion/levels, outlines, lists, tags, styles, paragraph formatting, tables, pictures, attachments, ink, page creation/import/moves/removal) applied whole or not to a kept-open section with emitter-chosen identities, UTF-16 ranges and the edit's time; refusals name the target, identity or structure at fault | -| `op::lower`, `op::lower_page`, `Section::apply_page` | The ops turning a range of paragraphs, or a whole page model, into another, computed from the models alone | +| `op::{Edit, Op, PageOp, TableEdit, SectionOp}`, `Section::apply` | Object-level edits (text, formatting, links, equations, paragraph insertion/split/join/move/deletion/levels, outlines, lists, tags, styles, paragraph formatting, tables, pictures, attachments, ink, page creation/import/moves/removal, conflict pages) applied whole or not to a kept-open section with emitter-chosen identities, UTF-16 ranges and the edit's time; refusals name the target, identity or structure at fault | +| `SectionOp::Conflict`, `Section::conflicts`, `ConflictPage` | Keep the version of a page a merge could not take as OneNote 2010 does: a read-only conflict page (`jcidConflictPageMetaData`, `IsConflictPage`, conflict objects marked) under the page's manifest, which says it has conflict pages; list each page's, newest first, and delete one (`SectionOp::Delete`) as OneNote's Delete Conflict Page does | +| `op::lower`, `op::lower_page` | The ops turning a range of paragraphs, or a whole page model, into another, computed from the models alone, for editors and imports | +| `op::predict` | The page an op leaves, as the section stores it and reading it back shows it | | `read_file` | Read a snapshot under whole-file exclusion | | `read_snapshot` | Read a validated snapshot through fresh positioned I/O while the caller excludes maintenance | -| `commit_file_property` | Lock, check the source snapshot's stamp, append and flush, then publish the revision | -| `CommitIo`, `commit_property_bytes` | Supply another storage backend with equivalent exclusion and ordered durability | +| `CommitIo`, `confirm`, `confirm_file` | Supply another storage backend with equivalent exclusion and ordered durability; check that a stamp still holds | -Scalar edits accept encoded values and require the caller to maintain MS-ONE -semantics. Text edits maintain run boundaries, inherit the insertion run's formatting, +Edits enter a kept-open `Section` as ops and leave as one appended revision per +changed space; only section and page creation, imports and opening files handle +whole images. Text edits maintain run boundaries, inherit the insertion run's formatting, and promote legacy text to Unicode when needed. Explicit and body-derived navigation titles update in the same transaction; unsupported fields, protected objects and split surrogate pairs are @@ -85,19 +80,19 @@ sections retain their encrypted structure and payloads. With the optional `protected` feature, `protected::UnlockedSection` opens native OneNote 2010 AES-128/CBC, SHA-1 password wrappers into a borrowed document view. Incorrect passwords, unsupported protection profiles and work-limit failures remain distinct. -The source stays encrypted. `PreparedEdit::page_protected` publishes a page-model -edit stored under the section's key (fresh IV per object, payloads under the file -IV); the other writers reject protected sections. +The source stays encrypted. `UnlockedSection::apply` applies page ops and seals them +under the section's key (fresh IV per object, payloads under the file IV); `Section` +rejects protected sections. `PageCreation::new` appends, or inserts before the first page space of an existing series. `Some("")` creates an empty title field; `None` omits the title node. The page has no body outlines, generated date/time text or applied template. Retain the intent to preserve its page, title and space identities; existing identities require reconciliation before retry. Body insertion and title edits -use those identities through the existing APIs. [Native page-creation fixtures](../../corpus/page-lifecycle/creation/README.md) +use those identities through page ops. [Native page-creation fixtures](../../corpus/page-lifecycle/creation/README.md) cover duplicate Unicode titles, native edits and Rust follow-up edits. `PageEdit::set_level` changes one page's indentation in place. `PageEdit::move_to` moves before an existing page space, or appends for `None`, and sets its level. -`PreparedEdit::pages` applies moves in slice order and publishes the final order, +`SectionOp::Pages` applies moves in slice order and publishes the final order, series membership and metadata levels in one transaction. Each page occurs once; levels are 1–3 and the final first page must have level 1. A following deeper-level page remains in place unless explicitly selected. To move a group, supply all its @@ -105,40 +100,39 @@ pages in order. Retain the intents across retries so newly formed series keep their identities; `reposition(PagePosition, level)` revises their placement while preserving those identities. [Native page-edit fixtures](../../corpus/page-lifecycle/page-edits/README.md) cover individual tabs, selected and collapsed groups, nesting and promotion. -`PreparedEdit::delete_pages_permanently` removes exactly the supplied page spaces, +`SectionOp::Delete` removes exactly the supplied page spaces, including subpages only when selected explicitly. The first remaining page becomes top-level; other page levels and surviving content are retained. The operation creates no recycle-bin copies and preserves prior revisions, so it is not secure erasure. Duplicate, missing or non-page identities reject the entire batch; -an empty selection leaves the file unchanged. Commit uses the exact source -snapshot and rejects a stale prepared removal before writing. -Insertions update child references, reference counts, modification times and automatic -titles atomically. Paragraphs can be nested or inserted into table cells; outline -coordinates use points. `Insertion::with_formatting` includes nonoverlapping UTF-16 -spans in that same publication; gaps keep the default insertion style. Empty text -accepts one `0..0` span for subsequent typing. Retain the `Insertion` value for rebasing: creating another -value creates different object identities. Duplicate insertion identities require -reconciliation. Formatting accepts explicit attributes, preserves inherited values, +an empty selection leaves the file unchanged. +`PageOp::Insert` and `PageOp::Add` update child references, reference counts, +modification times and automatic titles atomically. Paragraphs can be nested or +inserted into table cells; outline coordinates use points. Inserted text keeps its +spans' formats; `PageOp::Format` over an empty text's `0..0` sets its insertion style. +New objects carry the emitter's identities, so a queued edit replays onto another +image unchanged; an identity already on the page refuses the edit. Formatting accepts explicit attributes, preserves inherited values, and gives retired immutable styles zero current references while retaining history. Outline layout edits use points. Width is at least 36 points; an explicit user width and an automatic maximum-width hint remain distinct. Native layout generates the rendered height. Saved paragraph collapse defaults can be overridden by the native client's cached view. [Native layout captures](../../corpus/outline-edit/README.md) verify these edits through a fresh OneNote cache, including fields and nested content. -`TreeEdit::move_to` takes an existing parent and an optional direct sibling to insert +`PageOp::Move` takes an existing parent and an optional direct sibling to insert before; `None` appends. Paragraphs retain their descendants and explicit list styles. Outlines remain page children, so reordering changes their stacking order while -retaining coordinates. `TreeEdit::delete` removes the selected subtree from the active +retaining coordinates. `PageOp::Delete` removes the selected subtree from the active graph and preserves historical objects. Empty outlines/groups are removed; surviving -group indentation is normalized without shifting other paragraphs. Empty table cells -receive new empty paragraph/text objects with identities retained in the intent. +group indentation is normalized without shifting other paragraphs. An edit that +leaves a table cell without a paragraph is refused; insert its replacement in the +same edit. Title/protected content, ambiguous ancestry, cycles, and incompatible destinations reject before publication. Modification times, move attribution and automatic titles publish with the tree change. These explicit destinations differ from keyboard list indentation, which can also substitute list markers. Paragraph splits preserve character formatting, retain tags on the left, and clone -mutable list objects without restarting numbering. The new right paragraph/text -identities belong to the retained `ParagraphSplit` intent. Its publication includes +mutable list objects without restarting numbering. `PageOp::Split` names +the new right paragraph/text identities. Its publication includes the complete child graph and title metadata; repeating an existing identity requires reconciliation. Title containers, generated fields, recording-linked text and associated run metadata are rejected before I/O. Native split controls and subsequent @@ -206,9 +200,8 @@ fn main() -> Result<(), Box> { ``` `Store::parse` exposes checksum mismatches for diagnostic readers; the writer -rejects them. The resolved graph borrows the snapshot. Select the object-space ID, -object ID, and property from that graph, then pass those IDs, the same snapshot, -and the replacement's encoded bytes to `commit_file_property`. The +rejects them. The resolved graph borrows the snapshot. Open it as a `Section`, +apply ops naming objects from that graph, seal, and commit the `Transaction`. The `edit_property` example demonstrates selection by JCID/property/expected bytes, with optional explicit IDs when conflict copies contain identical text. @@ -228,7 +221,7 @@ the header (MS-ONESTORE 2.3.1), so the body is never compared. Live readers must equivalent exclusion. Native conflict creation can still expose cross-space references before their targets are saved; such snapshots must be rejected and reread while synchronization -proceeds. `read_file` and `commit_file_property` serialize within the process because +proceeds. `read_file` and `Transaction::commit_file` serialize within the process because macOS SMB locks can be reentrant. The lock is nonblocking across processes; contention requires a fresh read and a later retry. @@ -245,13 +238,16 @@ native acceptance captures. A no-op still flushes; a failed flush has an unknown durability outcome. The filesystem adapter uses whole-file locking. On macOS it acquires the lock -as part of opening the file (`O_EXLOCK | O_NONBLOCK`): separate open and `flock` -calls allowed overlapping exclusive holders and stranded server locks under -multi-process SMB contention. `tools/smb_lock_race.py` reproduces that failure -without notebook parsing or writing. On the tested macOS SMB mount, -POSIX byte-range locks returned `ENOTSUP`; whole-file locks excluded native -OneNote's lock ranges. This adapter serializes readers and writers during each -operation; it does not reproduce native reader/writer concurrency. The +as part of opening the file (`O_EXLOCK` to commit, `O_SHLOCK` to read, with +`O_NONBLOCK`): separate open and `flock` calls allowed overlapping exclusive holders +and stranded server locks under multi-process SMB contention. +`tools/smb_lock_race.py` reproduces that failure without notebook parsing or writing, +and with `--shared-reads` checks that shared readers never overlap a writer. On the +tested macOS SMB mount, POSIX byte-range locks returned `ENOTSUP`, and `flock` of +either kind became an exclusive lock over the whole file, which fails OneNote's reads. +smbfs sends `O_SHLOCK` and `O_EXLOCK` as share modes: a read excludes writers, +OneNote's included, but not OneNote's readers; a commit excludes everyone. It does not +reproduce native reader/writer concurrency. The [locking audit](../../evidence/LOCKING.md) records native coordination bytes, write-open share modes, and a working macOS SMB-specific byte-range lock probe. `sync_all` falls back to `fsync` on macOS only when `F_FULLFSYNC` is unsupported. Successful SMB FLUSH replies were observed on the diff --git a/crates/onestore/examples/concurrent_client.rs b/crates/onestore/examples/concurrent_client.rs index c1c4f7797c92304505d3e1fb9a3a73fa1cc3be6d..7a6e1484327a1633212ce80f3631f851fcdd51fa 100644 --- a/crates/onestore/examples/concurrent_client.rs +++ b/crates/onestore/examples/concurrent_client.rs @@ -2,6 +2,8 @@ mod concurrent; #[path = "../src/flush.rs"] mod flush; +#[path = "support/typing.rs"] +mod typing; use std::{env, fs, io::Write}; @@ -22,7 +24,15 @@ fn main() -> Result<(), Box> { &args, |path| onestore::read_file(path), |path, source, space, object, range, replacement| { - onestore::commit_file_text(path, source, space, object, range, replacement) + let edit = typing::text(space, object, range, replacement); + match typing::sealed(source, "Concurrent client", &edit) { + Ok(Some(transaction)) => transaction.commit_file(path), + Ok(None) => Ok(()), + Err(error) => Err(onestore::CommitError { + state: onestore::CommitState::NotCommitted, + error: std::io::Error::new(std::io::ErrorKind::InvalidData, error), + }), + } }, ) } diff --git a/crates/onestore/examples/edit_property.rs b/crates/onestore/examples/edit_property.rs index a2a8402cef734f39429c1e9a8fd214aed9fcb429..2a720da5cb0f5f34f7065899856eebbbfbeb8ded 100644 --- a/crates/onestore/examples/edit_property.rs +++ b/crates/onestore/examples/edit_property.rs @@ -1,9 +1,15 @@ +//! Changes one stored property value: a text object's text (`6000e 1c001c22`, UTF-16LE, +//! a trailing NUL dropped) as a text op, the notebook's colour on the table of contents root +//! (`20001 14001cbe`) as a TOC edit, each appending a revision; any other value of the same +//! length only in a copy, patched where it is stored, as a fixture that is no edit. + #[path = "../src/flush.rs"] mod flush; +#[path = "support/typing.rs"] +mod typing; use onestore::{ - ExGuid, ObjectData, PropertySets, RevisionIndex, Store, Value, commit_file_property, - replace_property_bytes, + ExGuid, ObjectData, PropertySets, RevisionIndex, Store, TocEdit, Transaction, Value, }; use std::{env, fs, io::Write}; @@ -59,16 +65,67 @@ fn main() -> Result<(), Box> { } } let (space, object) = selected.ok_or("No matching object")?; + let transaction: Option = match (jcid, id) { + (0x6000e, 0x1c001c22) => { + let units = |bytes: &[u8]| -> Vec { + let mut units: Vec = bytes + .chunks_exact(2) + .map(|pair| u16::from_le_bytes([pair[0], pair[1]])) + .collect(); + if units.last() == Some(&0) { + units.pop(); + } + units + }; + let length = u32::try_from(units(&expected).len())?; + let text = String::from_utf16(&units(&value))?; + let edit = typing::text(space, object, 0..length, &text); + typing::sealed(&bytes, "Property editor", &edit)? + } + (0x20001, 0x14001cbe) => { + if index.resolve_active(space)?.roots.get(&1) != Some(&object) { + return Err("Only the notebook's colour, on the TOC root, changes".into()); + } + let color = u32::from_le_bytes(value.as_slice().try_into()?); + onestore::edit_table_of_contents(&bytes, &[TocEdit::Color(color)])? + } + _ if args[1] != "--in-place" && value.len() == expected.len() => { + let revision = index.resolve_active(space)?; + let ObjectData::Properties(data) = revision.objects[&object].data else { + return Err("The object stores no properties".into()); + }; + let properties = PropertySets::parse(data)?; + let Some(Value::Bytes(stored)) = properties.sets[0] + .iter() + .find(|property| property.id == id) + .map(|property| &property.value) + else { + return Err("The property stores no bytes".into()); + }; + let at = stored.as_ptr().addr() - bytes.as_ptr().addr(); + let mut patched = bytes.clone(); + patched[at..at + value.len()].copy_from_slice(&value); + write(&args[1], &patched)?; + return Ok(()); + } + _ => return Err("Only text and TOC colours change as edits; other values are patched into a copy of equal length".into()), + }; + let transaction = transaction.ok_or("The value is already stored")?; if args[1] == "--in-place" { - commit_file_property(&args[0], &bytes, space, object, id, &value)?; + transaction.commit_file(&args[0])?; return Ok(()); } - let written = replace_property_bytes(&bytes, space, object, id, &value)?; + let mut written = bytes.clone(); + transaction.apply(&mut written)?; + write(&args[1], &written) +} + +fn write(path: &str, written: &[u8]) -> Result<(), Box> { let mut output = fs::OpenOptions::new() .write(true) .create_new(true) - .open(&args[1])?; - output.write_all(&written)?; + .open(path)?; + output.write_all(written)?; flush::flush(&output)?; Ok(()) } diff --git a/crates/onestore/examples/header_faults.rs b/crates/onestore/examples/header_faults.rs index 5ef222ae5af5b1ffcebe31d48e2ca94baa17892e..00886c3137eed02af387c4a67202b25f0f426200 100644 --- a/crates/onestore/examples/header_faults.rs +++ b/crates/onestore/examples/header_faults.rs @@ -1,24 +1,28 @@ +#[path = "support/typing.rs"] +mod typing; + use onestore::*; fn main() -> Result<(), Box> { let source = std::fs::read("corpus/native/20260905-05/snapshots/02-text/notebook/synthetic.one")?; let store = Store::parse(&source)?; let index = RevisionIndex::parse(&store)?; - let mut target = None; - for (osid, space) in &index.spaces { - let revision = index.resolve(*osid, space.labels[&(ExGuid::default(), 1)])?; - for (oid, object) in revision.objects { - if let ObjectData::Properties(data) = object.data { - for p in &PropertySets::parse(data)?.sets[0] { - if p.value == Value::Bytes(b"Fictitious plain text.") { - target = Some((*osid, oid, p.id)); - } - } - } - } - } - let (osid, oid, property) = target.unwrap(); - let output = replace_property_bytes(&source, osid, oid, property, b"A durable edit.")?; + let document = document::Document::parse(&index)?; + let (osid, oid) = document + .spaces + .iter() + .find_map(|(sid, space)| { + space.active()?.nodes.iter().find_map(|(oid, node)| { + matches!(&node.kind, document::Kind::RichText { text, .. } if text == "Fictitious plain text.") + .then_some((*sid, *oid)) + }) + }) + .ok_or("Missing native fixture text")?; + let end = "Fictitious plain text.".encode_utf16().count() as u32; + let edit = typing::text(osid, oid, 0..end, "A durable edit."); + let transaction = typing::sealed(&source, "Header faults", &edit)?.ok_or("No change")?; + let mut output = source.clone(); + transaction.apply(&mut output)?; let mut invalid = Vec::new(); for prefix in 0..=1024 { let mut torn = output.clone(); diff --git a/crates/onestore/examples/insert.rs b/crates/onestore/examples/insert.rs index e443909cfddb89775826197ba05b3a74b3f9b78d..41a08d32b447ea4e672e334da186c0db5ba45386 100644 --- a/crates/onestore/examples/insert.rs +++ b/crates/onestore/examples/insert.rs @@ -1,37 +1,96 @@ #[path = "../src/flush.rs"] mod flush; +#[path = "support/typing.rs"] +mod typing; -use onestore::{Insertion, PreparedEdit}; +use onestore::{ + document::{Format, Layout}, + op::{Edit, Op, PageOp}, + page::{Outline, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, text::new_id}, +}; use std::{fs, io::Write}; +/// A paragraph holding `text` in OneNote's default text style. +fn paragraph(text: &str) -> Result> { + let format = Format { + font: Some("Calibri".into()), + font_size: Some(11.0), + language: Some(0x409), + ..Format::default() + }; + Ok(PageParagraph { + id: new_id()?, + parent: None, + level: 1, + style: None, + format: Format::default(), + content: ParagraphContent::Text(TextObject { + id: new_id()?, + date_field: None, + text: Paragraph::new(text.into(), format), + tags: Vec::new(), + }), + lists: Vec::new(), + tags: Vec::new(), + media: Default::default(), + collapsed: false, + }) +} + fn main() -> Result<(), Box> { let args: Vec<_> = std::env::args().skip(1).collect(); - let usage = "Usage: insert paragraph INPUT OUTPUT|--in-place SPACE PARENT BEFORE|- TEXT AUTHOR\n insert outline INPUT OUTPUT|--in-place SPACE PAGE X Y TEXT AUTHOR"; - let insertion = match args.first().map(String::as_str) { - Some("paragraph") if args.len() == 8 => Insertion::paragraph( - args[4].parse()?, - if args[5] == "-" { - None - } else { - Some(args[5].parse()?) - }, - &args[6], - &args[7], - )?, - Some("outline") if args.len() == 9 => Insertion::outline( - args[4].parse()?, - args[5].parse()?, - args[6].parse()?, - &args[7], - &args[8], - )?, + let usage = "Usage: insert paragraph INPUT OUTPUT|--in-place SPACE CONTAINER BEFORE|- TEXT AUTHOR\n insert outline INPUT OUTPUT|--in-place SPACE X Y TEXT AUTHOR"; + let (op, object, text, author) = match args.first().map(String::as_str) { + Some("paragraph") if args.len() == 8 => { + let paragraph = paragraph(&args[6])?; + let (object, text) = (paragraph.id, paragraph.text().unwrap().id); + let op = PageOp::Insert { + container: args[4].parse()?, + before: if args[5] == "-" { + None + } else { + Some(args[5].parse()?) + }, + paragraphs: vec![paragraph], + }; + (op, object, text, &args[7]) + } + Some("outline") if args.len() == 8 => { + let paragraph = paragraph(&args[6])?; + let text = paragraph.text().unwrap().id; + let object = new_id()?; + let op = PageOp::Add { + object: PageObject::Outline(Outline { + id: object, + title: false, + min_width: None, + layout: Layout { + x: Some(args[4].parse()?), + y: Some(args[5].parse()?), + ..Layout::default() + }, + indents: Vec::new(), + paragraphs: vec![paragraph], + unsupported: Vec::new(), + }), + before: None, + }; + (op, object, text, &args[7]) + } _ => return Err(usage.into()), }; let source = onestore::read_file(&args[1])?; - let prepared = PreparedEdit::insert(&source, args[3].parse()?, &insertion)?; - let mut record = serde_json::json!({"intent": insertion, "object": insertion.object(), "text_object": insertion.text_object()}); + let edit = Edit { + at: typing::now(), + ops: vec![Op::Page { + space: args[3].parse()?, + op, + }], + }; + let transaction = typing::sealed(&source, author, &edit)?.ok_or("The insertion stores nothing")?; + let mut record = serde_json::json!({"edit": edit, "object": object, "text_object": text}); if args[2] == "--in-place" { - if let Err(error) = prepared.commit_file(&args[1]) { + if let Err(error) = transaction.commit_file(&args[1]) { record["state"] = format!("{:?}", error.state).into(); record["error"] = error.error.to_string().into(); println!("{record}"); @@ -39,11 +98,13 @@ fn main() -> Result<(), Box> { } record["state"] = "Committed".into(); } else { + let mut written = source; + transaction.apply(&mut written)?; let mut file = fs::OpenOptions::new() .write(true) .create_new(true) .open(&args[2])?; - file.write_all(prepared.as_bytes())?; + file.write_all(&written)?; flush::flush(&file)?; record["state"] = "Created".into(); } diff --git a/crates/onestore/examples/maintenance_fixture.rs b/crates/onestore/examples/maintenance_fixture.rs index ccfab15788a4bc6b940847fa6a127fe89e465851..6b97f160df8721518e4052daeadf36f4115fe1f0 100644 --- a/crates/onestore/examples/maintenance_fixture.rs +++ b/crates/onestore/examples/maintenance_fixture.rs @@ -1,3 +1,6 @@ +#[path = "support/typing.rs"] +mod typing; + use onestore::{ ExGuid, RevisionIndex, Store, document::{Document, Kind}, @@ -46,7 +49,10 @@ fn main() -> Result<(), Box> { } else { format!("Revision {revision}: {}", "x".repeat(65536)) }; - bytes = onestore::replace_text(&bytes, sid, oid, 0..end, &text)?; + let edit = typing::text(sid, oid, 0..end, &text); + if let Some(transaction) = typing::sealed(&bytes, "Fixture", &edit)? { + transaction.apply(&mut bytes)?; + } } let store = Store::parse(&bytes)?; let toc = onestore::create_table_of_contents( diff --git a/crates/onestore/examples/power_loss.rs b/crates/onestore/examples/power_loss.rs index 274b7fcf3d7a72df55b8c401483c1f111cea2357..66d93a25d28b626db09844f3925ba95bedb029b4 100644 --- a/crates/onestore/examples/power_loss.rs +++ b/crates/onestore/examples/power_loss.rs @@ -8,6 +8,8 @@ use trace::{Event, Trace}; #[path = "../tests/support/checkpoint.rs"] mod checkpoint; +#[path = "support/typing.rs"] +mod typing; use onestore::{ ExGuid, RevisionIndex, Store, @@ -15,6 +17,16 @@ use onestore::{ }; use std::{collections::BTreeMap, fs, path::PathBuf}; +/// `source`, a table of contents, with the notebook recoloured to `color`. +fn recolored(source: &[u8], color: u32) -> Result, onestore::Error> { + let mut image = source.to_vec(); + let edit = onestore::TocEdit::Color(color); + if let Some(transaction) = onestore::edit_table_of_contents(source, &[edit])? { + transaction.apply(&mut image)?; + } + Ok(image) +} + fn main() -> Result<(), Box> { let mut args = std::env::args_os().skip(1); let destination = PathBuf::from(args.next().ok_or("Provide a new evidence directory")?); @@ -69,12 +81,11 @@ fn main() -> Result<(), Box> { let index = RevisionIndex::parse(&store)?; let document = Document::parse(&index)?; let toc = store.header.file_type == onestore::FileType::TableOfContents; + // A table of contents changes the notebook's colour. let (sid, oid) = if toc { let root = &document.spaces[&document.root]; - ( - document.root, - root.revisions[&root.contexts[&ExGuid::default()]].roots[&1], - ) + let root = &root.revisions[&root.contexts[&ExGuid::default()]]; + (document.root, root.roots[&1]) } else { document .spaces @@ -95,37 +106,43 @@ fn main() -> Result<(), Box> { }) .ok_or("Missing native fixture text")? }; - if name.ends_with("checkpoint") { - source = - checkpoint::pending(&source, sid, oid, if toc { 0x14001cbe } else { 0x14001d7a }); - } - if name == "toc-rollover" { - loop { + match name { + "checkpoint" => source = checkpoint::pending(&source, sid, oid), + "toc-checkpoint" => { + for value in 0.. { + let store = Store::parse(&source)?; + let index = RevisionIndex::parse(&store)?; + let rid = index.spaces[&sid].labels[&(ExGuid::default(), 1)]; + let depth = std::iter::successors(Some(rid), |id| { + index.spaces[&sid].revisions[id].dependency + }) + .count(); + if depth == 512 { + break; + } + source = recolored(&source, value)?; + } + } + "toc-rollover" => loop { let count = Store::parse(&source)?.header.transaction_count; if count == 255 { break; } assert!(count < 255); - source = onestore::replace_property_bytes( - &source, - sid, - oid, - 0x14001cbe, - &count.to_le_bytes(), - )?; - } + source = recolored(&source, count)?; + }, + _ => {} } let apply = |io: &mut Trace, bytes: &[u8], recovering: bool| -> Result<(), Box> { if toc { - let value = if recovering { - [0x44, 0x55, 0x66, 0] - } else { - [0x33, 0x66, 0x99, 0] - }; - onestore::commit_property_bytes(io, bytes, sid, oid, 0x14001cbe, &value)?; + let color = if recovering { 0x665544 } else { 0x996633 }; + let edit = onestore::TocEdit::Color(color); + let transaction = + onestore::edit_table_of_contents(bytes, &[edit])?.ok_or("No change")?; + transaction.commit(io)?; } else { let store = Store::parse(bytes)?; let index = RevisionIndex::parse(&store)?; @@ -136,18 +153,14 @@ fn main() -> Result<(), Box> { unreachable!() }; let end = u32::try_from(text.encode_utf16().count())?; - onestore::commit_text( - io, - bytes, - sid, - oid, - end..end, - if recovering { - " [recovered]" - } else { - " [durable café 🦀]" - }, - )?; + let with = if recovering { + " [recovered]" + } else { + " [durable café 🦀]" + }; + let edit = typing::text(sid, oid, end..end, with); + let transaction = typing::sealed(bytes, "Power loss", &edit)?.ok_or("No change")?; + transaction.commit(io)?; } Ok(()) }; diff --git a/crates/onestore/examples/random_edit.rs b/crates/onestore/examples/random_edit.rs index 4a93ec13169510713cf9731ce052b94614727c3f..965efd77cb361a65e40d8dbeeffe6af49751d6be 100644 --- a/crates/onestore/examples/random_edit.rs +++ b/crates/onestore/examples/random_edit.rs @@ -1,5 +1,7 @@ #[path = "../src/flush.rs"] mod flush; +#[path = "support/typing.rs"] +mod typing; use onestore::{ ExGuid, RevisionIndex, Store, @@ -111,13 +113,20 @@ fn main() -> Result<(), Box> { end }; let range = offset + boundaries[start]..offset + boundaries[end]; - let written = match onestore::replace_text(&source, sid, oid, range.clone(), replacement) { - Ok(written) => written, + let edit = typing::text(sid, oid, range.clone(), replacement); + let transaction = match typing::sealed(&source, "Random editor", &edit) { + Ok(Some(transaction)) => transaction, + Ok(None) => { + rejected.push("The edit stores nothing".into()); + continue; + } Err(error) => { rejected.push(error.to_string()); continue; } }; + let mut written = source.clone(); + transaction.apply(&mut written)?; let space = &document.spaces[&sid]; let revision = &space.revisions[&space.contexts[&ExGuid::default()]]; let Kind::RichText { runs, .. } = &revision.nodes[&oid].kind else { @@ -133,7 +142,7 @@ fn main() -> Result<(), Box> { "run_before": actual[selected].text, "run_start": runs[selected].start, "source_md5": format!("{:x}", md5::compute(&source)), "started_ms": started, "rejected_candidates": rejected}); let failed = if args[1] == "--in-place" { - match onestore::commit_file_text(&args[0], &source, sid, oid, range, replacement) { + match transaction.commit_file(&args[0]) { Ok(()) => { record["state"] = "Committed".into(); false diff --git a/crates/onestore/examples/scratch_dump.rs b/crates/onestore/examples/scratch_dump.rs new file mode 100644 index 0000000000000000000000000000000000000000..552781b7f6128203e993a10d2617f370c6d21650 --- /dev/null +++ b/crates/onestore/examples/scratch_dump.rs @@ -0,0 +1,29 @@ +use onestore::{ObjectData, PropertySets, RevisionIndex, Store, Value, document::Document}; +fn main() -> Result<(), Box> { + let args: Vec = std::env::args().skip(1).collect(); + let bytes = std::fs::read(&args[0])?; + let store = Store::parse(&bytes)?; + let index = RevisionIndex::parse(&store)?; + let pages = Document::parse(&index).and_then(|document| document.pages()).unwrap_or_default(); + let space = if args[1] == "root" { index.root } else { pages[args[1].parse::()?].0 }; + println!("space {space}; pages {:?}", pages.iter().map(|p| p.0).collect::>()); + let raw = index.resolve_active(space)?; + for (id, object) in &raw.objects { + println!("{id} jcid {:#x}", object.jcid); + if let ObjectData::Properties(data) = object.data { + let sets = PropertySets::parse(data)?; + for (n, set) in sets.sets.iter().enumerate() { + for p in set { + let v = match &p.value { + Value::NoData => "-".to_string(), + Value::Bytes(b) => b.iter().map(|x| format!("{x:02x}")).collect(), + Value::References { stream, compact_ids } => format!("{stream:?} {}", compact_ids.iter().map(|x| format!("{x:02x}")).collect::()), + Value::Sets(r) => format!("sets {r:?}"), + }; + println!(" [{n}] {:#010x} {v}", p.id); + } + } + } + } + Ok(()) +} diff --git a/crates/onestore/examples/support/concurrent.rs b/crates/onestore/examples/support/concurrent.rs index d39a781b522c604809473846aff03471fb3a73e2..8b8984b9b660e473761c5ec94ba38e1c68e46953 100644 --- a/crates/onestore/examples/support/concurrent.rs +++ b/crates/onestore/examples/support/concurrent.rs @@ -318,7 +318,46 @@ pub fn run( #[cfg(test)] mod tests { use super::*; - use onestore::{Insertion, ParagraphJoin, ParagraphSplit, PreparedEdit, TextAttribute}; + use onestore::{ + TextAttribute, + document::{Format, Layout}, + op::{Edit, Op, PageOp}, + page::{Outline, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, text::new_id}, + }; + + fn paragraph(text: &str, level: u32) -> PageParagraph { + PageParagraph { + id: new_id().unwrap(), + parent: None, + level, + style: None, + format: Format::default(), + content: ParagraphContent::Text(TextObject { + id: new_id().unwrap(), + date_field: None, + text: Paragraph::new(text.into(), Format { + font: Some("Calibri".into()), + font_size: Some(11.0), + language: Some(0x409), + ..Format::default() + }), + tags: Vec::new(), + }), + lists: Vec::new(), + tags: Vec::new(), + media: Default::default(), + collapsed: false, + } + } + + fn edited(image: &[u8], space: ExGuid, ops: Vec) -> Vec { + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, image.to_vec()).unwrap(); + let ops = ops.into_iter().map(|op| Op::Page { space, op }).collect(); + section.apply("Author", &Edit { at: 134_000_000_000_000_000, ops }).unwrap(); + section.seal().unwrap(); + section.image() + } #[test] fn observation_follows_split_suffixes_and_moved_children_through_active_ancestry() { @@ -328,57 +367,96 @@ mod tests { let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); let (space, page) = document.pages().unwrap()[0]; - let insertion = Insertion::outline(page, 144.0, 216.0, "Document w0:0 🦀", "Author") - .unwrap() - .with_formatting(0..16, &[TextAttribute::Bold(true)]) - .unwrap(); - let inserted = PreparedEdit::insert(&source, space, &insertion).unwrap(); - let before = document_view(inserted.as_bytes()).unwrap(); + let first = paragraph("Document w0:0 🦀", 1); + let (paragraph_id, text) = (first.id, first.text().unwrap().id); + let outline_id = new_id().unwrap(); + let inserted = edited( + &source, + space, + vec![ + PageOp::Add { + object: PageObject::Outline(Outline { + id: outline_id, + title: false, + min_width: None, + layout: Layout { + x: Some(144.0), + y: Some(216.0), + ..Default::default() + }, + indents: Vec::new(), + paragraphs: vec![first], + unsupported: Vec::new(), + }), + before: None, + }, + PageOp::Format { + text, + range: 0..16, + set: vec![TextAttribute::Bold(true)], + clear: Vec::new(), + }, + ], + ); + let before = document_view(&inserted).unwrap(); assert_eq!(before.texts.len(), 1); assert_eq!(before.graph.len(), 2); - let outline = insertion.object().to_string(); - let paragraph = before.graph[&outline]["children"][0] - .as_str() - .unwrap() - .to_owned(); - let child = - Insertion::paragraph(paragraph.parse().unwrap(), None, "Unmarked child", "Author") - .unwrap(); - let with_child = PreparedEdit::insert(inserted.as_bytes(), space, &child).unwrap(); - let before = document_view(with_child.as_bytes()).unwrap(); + let outline = outline_id.to_string(); + let paragraph_name = paragraph_id.to_string(); + assert_eq!(before.graph[&outline]["children"], json!([paragraph_name])); + let child = paragraph("Unmarked child", 2); + let child_id = child.id; + let with_child = edited( + &inserted, + space, + vec![PageOp::Insert { + container: paragraph_id, + before: None, + paragraphs: vec![child], + }], + ); + let before = document_view(&with_child).unwrap(); assert_eq!(before.texts.len(), 2); assert_eq!(before.graph.len(), 3); assert_eq!( before.graph[&outline], - json!({"parent":page.to_string(), "children":[paragraph], "content":[], "child_level":1, "position":{"x":144.0,"y":216.0}}) + json!({"parent":page.to_string(), "children":[paragraph_name], "content":[], "child_level":1, "position":{"x":144.0,"y":216.0}}) ); for offset in [14, 16] { - let split = ParagraphSplit::new(insertion.text_object(), offset, "Author").unwrap(); - let split_edit = PreparedEdit::split(with_child.as_bytes(), space, &split).unwrap(); - let observed = document_view(split_edit.as_bytes()).unwrap(); + let (split, right) = (new_id().unwrap(), new_id().unwrap()); + let split_edit = edited( + &with_child, + space, + vec![PageOp::Split { + text, + at: offset, + paragraph: split, + right, + lists: Vec::new(), + }], + ); + let observed = document_view(&split_edit).unwrap(); assert_eq!(observed.texts.len(), 3); assert_eq!(observed.graph.len(), 4); assert_eq!( - observed.texts[&split.text_object().to_string()]["text"], + observed.texts[&right.to_string()]["text"], if offset == 14 { "🦀" } else { "" } ); assert_eq!( observed.graph[&outline]["children"], - json!([paragraph, split.object().to_string()]) + json!([paragraph_name, split.to_string()]) ); - assert_eq!(observed.graph[¶graph]["children"], json!([])); + assert_eq!(observed.graph[¶graph_name]["children"], json!([])); assert_eq!( - observed.graph[&split.object().to_string()]["children"], - json!([child.object().to_string()]) + observed.graph[&split.to_string()]["children"], + json!([child_id.to_string()]) ); assert_eq!( - observed.graph[&child.object().to_string()]["parent"], - split.object().to_string() + observed.graph[&child_id.to_string()]["parent"], + split.to_string() ); - let join = - ParagraphJoin::new(insertion.text_object(), split.text_object(), "Author").unwrap(); - let joined = PreparedEdit::join(split_edit.as_bytes(), space, &join).unwrap(); - let joined = document_view(joined.as_bytes()).unwrap(); + let joined = edited(&split_edit, space, vec![PageOp::Join { left: text, right }]); + let joined = document_view(&joined).unwrap(); assert_eq!(joined.graph, before.graph); let characters = |view: &DocumentView| { view.texts diff --git a/crates/onestore/examples/support/typing.rs b/crates/onestore/examples/support/typing.rs new file mode 100644 index 0000000000000000000000000000000000000000..97e5a6a8936a27f75e72538b3da89ed79ff5f883 --- /dev/null +++ b/crates/onestore/examples/support/typing.rs @@ -0,0 +1,41 @@ +//! Text typed as ops through a `Section`, for the examples that edit a section file. +#![allow(dead_code)] + +use onestore::{ + Arena, ExGuid, Section, Transaction, + op::{Edit, Op, OpError, PageOp}, +}; +use std::{ + ops::Range, + time::{SystemTime, UNIX_EPOCH}, +}; + +/// FILETIME now, as an edit's `at`. +pub fn now() -> u64 { + let unix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default(); + (unix.as_secs() + 11_644_473_600) * 10_000_000 + u64::from(unix.subsec_nanos() / 100) +} + +/// The op replacing `range` of the text object `text` in `space` with `with`. +pub fn text(space: ExGuid, text: ExGuid, range: Range, with: &str) -> Edit { + let op = PageOp::Text { + text, + range, + with: with.to_owned(), + }; + Edit { + at: now(), + ops: vec![Op::Page { space, op }], + } +} + +/// The transaction `edit` seals into on `image`, as `author` made it; none when it stores +/// nothing. +pub fn sealed(image: &[u8], author: &str, edit: &Edit) -> Result, OpError> { + let arena = Arena::default(); + let mut section = Section::open(&arena, image.to_vec()).map_err(OpError::Failed)?; + section.apply(author, edit)?; + section.seal().map_err(OpError::Failed) +} diff --git a/crates/onestore/src/active.rs b/crates/onestore/src/active.rs index 6ed47dcb39e3259ff8b0b688b87b5103e14f33bb..7abdae2e79e56a3a4beb7e111dd2320b3cc8cc87 100644 --- a/crates/onestore/src/active.rs +++ b/crates/onestore/src/active.rs @@ -1,7 +1,13 @@ use crate::{ - Error, ExGuid, FileType, Object, RevisionIndex, Store, - document::{Document, Element, Kind, Revision, Space}, - write::{Commit, LiveRevision, PropertyObject, chain_depth, declared, differing}, + Error, ExGuid, FileType, Object, + document::{Element, Kind, Revision}, + write::{Commit, LiveRevision, PropertyObject, declared, differing}, +}; +#[cfg(test)] +use crate::{ + RevisionIndex, Store, + document::{Document, Space}, + write::chain_depth, }; use bumpalo::Bump; use std::{ @@ -35,6 +41,8 @@ pub(crate) struct ActivePage<'a> { } impl<'a> ActivePage<'a> { + /// The active page of `space`, for tests comparing writers. + #[cfg(test)] pub(crate) fn parse(index: &'a RevisionIndex<'a>, space: ExGuid) -> Result { index.validate_current()?; let mut document = Document::parse(index)?; @@ -131,6 +139,7 @@ impl<'a> ActivePage<'a> { /// Stores a writer's objects and payloads as the next revision; false when it stores /// nothing, as appending it would leave the image unchanged. + #[cfg(test)] pub(crate) fn write( &mut self, arena: &'a Bump, @@ -328,26 +337,19 @@ fn is_title(node: &Element<'_>) -> bool { .is_some_and(|fields| fields.iter().any(|field| field.id == 0x88001cb4)) } -/// Writes one revision of a page space: `changes` computes its objects from the active page. -pub(crate) fn write( - source: &[u8], - space: ExGuid, - changes: impl FnOnce(&ActivePage<'_>) -> Result, -) -> Result> { - let store = Store::parse(source)?; - let index = RevisionIndex::parse(&store)?; - let changes = changes(&ActivePage::parse(&index, space)?)?; - if changes.is_empty() { - return Ok(source.to_vec()); - } - crate::write::write_revision_on(&index, space, |_| Ok(changes)) -} - #[cfg(test)] pub(crate) mod tests { use super::*; use crate::{Insertion, ParagraphSplit, TextAttribute, TreeEdit, document::Kind}; + /// An identity from `fresh_guid`, which seeded tests draw deterministically per thread. + fn new_id() -> Result { + Ok(ExGuid { + guid: crate::write::fresh_guid()?, + n: 1, + }) + } + /// A seeded source of the typed writers' changes: insertions, moves, deletions, splits /// and formatting of what a page holds. pub(crate) struct Writes(pub u64); @@ -392,15 +394,31 @@ pub(crate) mod tests { let text = *texts.get(self.pick(texts.len())).ok_or(unavailable)?; let words = ["", "a", "Two words", "東京 🦀", "longer text here"]; let word = words[self.pick(words.len())]; - match self.pick(8) { - 0..=3 => Insertion::paragraph(container, anchor, word, "Author") - .and_then(|insertion| insertion.changes(active)), + // `Section::apply` refuses an edit ending with a table cell emptied. + let fills_a_cell = active.parents[¶graph].iter().any(|parent| { + matches!(nodes[parent].kind, Kind::Cell { .. }) + && nodes[parent].children == [paragraph] + }); + let choice = match self.pick(8) { + 4 | 5 if fills_a_cell => 0, + choice => choice, + }; + match choice { + 0..=3 => Insertion::paragraph(container, anchor, word, "Author").and_then( + |insertion| { + let paragraph = new_id()?; + insertion.changes_as(active, paragraph, paragraph, new_id()?) + }, + ), 4 => TreeEdit::move_to(paragraph, container, anchor, "Author") .and_then(|edit| edit.changes(active)), 5 => TreeEdit::delete(paragraph, "Author").and_then(|edit| edit.changes(active)), 6 => { let at = self.pick(3) as u32; - ParagraphSplit::new(text, at, "Author").and_then(|split| split.changes(active)) + let lists = (0..8).map(|_| new_id()).collect::>>()?; + ParagraphSplit::new(text, at, "Author").and_then(|split| { + split.changes_as(active, new_id()?, new_id()?, &lists) + }) } _ => { let end = self.pick(2) as u32; diff --git a/crates/onestore/src/commit.rs b/crates/onestore/src/commit.rs index 87ecff00fd31b2f8ef33abfdce63dc3a1bac15b0..d372d746452d028b4474f254f402695c3f1e6d55 100644 --- a/crates/onestore/src/commit.rs +++ b/crates/onestore/src/commit.rs @@ -1,4 +1,3 @@ -use crate::{ExGuid, replace_property_bytes}; use std::io::{self, ErrorKind}; #[cfg(any(unix, windows))] @@ -33,8 +32,14 @@ impl FileIo { #[cfg(target_os = "macos")] { use std::os::unix::fs::OpenOptionsExt; - // SMB can lose exclusion when separate opens race with flock. - options.custom_flags(nix::libc::O_EXLOCK | nix::libc::O_NONBLOCK); + // SMB can lose exclusion when separate opens race with flock. On smbfs these are + // share modes: a shared reader denies only writers, so OneNote's readers proceed. + let lock = if write { + nix::libc::O_EXLOCK + } else { + nix::libc::O_SHLOCK + }; + options.custom_flags(lock | nix::libc::O_NONBLOCK); } let file = options.open(path)?; #[cfg(not(target_os = "macos"))] @@ -100,14 +105,14 @@ pub fn place_file(path: impl AsRef, ancestor: [u8; 16], name: &str) -> io: released } -/// Reads a snapshot under the same whole-file exclusion used for commits. -/// Native writers can expose incomplete graphs to unlocked filesystem reads. +/// Reads a snapshot excluding writers, as commits exclude everyone (macOS shares it with +/// other readers). Native writers can expose incomplete graphs to unlocked filesystem reads. #[cfg(any(unix, windows))] pub fn read_file(path: impl AsRef) -> io::Result> { read_file_limited(path, usize::MAX) } -/// Reads under whole-file exclusion, rejecting a snapshot larger than the byte limit. +/// `read_file`, rejecting a snapshot larger than the byte limit. /// A size failure returns `FileTooLarge` without a partial snapshot. #[cfg(any(unix, windows))] pub fn read_file_limited(path: impl AsRef, limit: usize) -> io::Result> { @@ -147,278 +152,25 @@ impl CommitIo for FileIo { } } -/// Commits under an exclusive whole-file lock; contention returns WouldBlock. -/// Calls are serialized within this process because macOS SMB locks are reentrant. -/// Durability relies on the filesystem and server honoring their flush contract. -/// A changed snapshot returns ResourceBusy without publishing the edit. +/// `confirm` under the same whole-file exclusion `commit_file` uses. #[cfg(any(unix, windows))] -pub fn commit_file_property( - path: impl AsRef, - source: &[u8], - space: ExGuid, - object: ExGuid, - property: u32, - value: &[u8], -) -> Result<(), CommitError> { +pub fn confirm_file(path: impl AsRef, base: &Stamp) -> Result<(), CommitError> { let mut io = FileIo::open(path, true).map_err(|error| CommitError { state: CommitState::NotCommitted, error, })?; - let result = commit_property_bytes(&mut io, source, space, object, property, value); + let result = confirm(&mut io, base); io.finish(result) } -/// Commits a text edit with the same exclusion and snapshot check as scalar edits. -#[cfg(any(unix, windows))] -pub fn commit_file_text( - path: impl AsRef, - source: &[u8], - space: ExGuid, - object: ExGuid, - range: std::ops::Range, - replacement: &str, -) -> Result<(), CommitError> { - let mut io = FileIo::open(path, true).map_err(|error| CommitError { - state: CommitState::NotCommitted, - error, - })?; - let result = commit_text(&mut io, source, space, object, range, replacement); - io.finish(result) -} - -/// Atomically publishes text and its dependent run boundaries under caller-held exclusion. -pub fn commit_text( - io: &mut impl CommitIo, - source: &[u8], - space: ExGuid, - object: ExGuid, - range: std::ops::Range, - replacement: &str, -) -> Result<(), CommitError> { - PreparedEdit::text(source, space, object, range, replacement) - .map_err(|error| CommitError { - state: CommitState::NotCommitted, - error: io::Error::new(ErrorKind::InvalidData, error), - })? - .commit(io) -} - -/// An immutable writer-generated transition tied to its original snapshot. -/// Persist intended revision identities from `as_bytes` before publishing an offline edit. -/// Missing identities after native maintenance do not prove an edit was never published. -pub struct PreparedEdit<'a> { - source: &'a [u8], - written: Vec, -} - -impl<'a> PreparedEdit<'a> { - /// Applies page moves in slice order and publishes final order and indentation atomically. - /// Each page occurs once; all pages to move must be explicit, including selected subpages. - pub fn pages(source: &'a [u8], edits: &[crate::PageEdit]) -> Result { - Ok(Self { - source, - written: crate::pages::edit_pages(source, edits, &[])?, - }) - } - - /// Applies table-of-contents edits (sections and section groups: add, rename, colour, - /// order, remove) as one revision of a `.onetoc2` file. - pub fn table_of_contents( - source: &'a [u8], - edits: &[crate::TocEdit], - ) -> Result { - Ok(Self { - source, - written: crate::toc::edit_table_of_contents(source, edits)?, - }) - } - - /// Permanently removes explicitly selected pages from the section in one transaction. - /// Subpages must be selected explicitly; a surviving first subpage becomes top-level. - /// Creates no recycle-bin copies. Prior revisions remain stored; this is not secure erasure. - pub fn delete_pages_permanently( - source: &'a [u8], - pages: &[crate::ExGuid], - ) -> Result { - Ok(Self { - source, - written: crate::pages::edit_pages(source, &[], pages)?, - }) - } - - /// Publishes an edited page model as one revision per changed space. - /// The model must come from this snapshot; new paragraphs and outlines carry the identities - /// the model assigned, and content outside the model is untouched. - pub fn page( - source: &'a [u8], - space: ExGuid, - page: &crate::page::Page, - author: &str, - ) -> Result { - Ok(Self { - source, - written: crate::page::write::write_page(source, space, page, author)?, - }) - } - - /// `page` for a password-protected section: the revision is stored under the section's - /// key. The model must come from this snapshot unlocked with `password`. - #[cfg(feature = "protected")] - pub fn page_protected( - source: &'a [u8], - password: &str, - space: ExGuid, - page: &crate::page::Page, - author: &str, - ) -> Result { - Ok(Self { - source, - written: crate::protected::write_page(source, password, space, page, author)?, - }) - } - - /// Creates a page and its section entry in one transaction, retaining the intent's identities. - pub fn create_page(source: &'a [u8], page: &crate::PageCreation) -> Result { - Ok(Self { - source, - written: page.apply(source)?, - }) - } - - /// Moves or removes a subtree and normalizes its containers in one revision. - pub fn tree( - source: &'a [u8], - space: ExGuid, - edit: &crate::TreeEdit, - ) -> Result { - Ok(Self { - source, - written: edit.apply(source, space)?, - }) - } - - /// Changes outline geometry or a paragraph's saved expansion state, preserving content. - pub fn outline( - source: &'a [u8], - space: ExGuid, - object: ExGuid, - edit: crate::OutlineEdit, - ) -> Result { - Ok(Self { - source, - written: edit.apply(source, space, object)?, - }) - } - - /// Joins adjacent ordinary paragraphs with native left-tag and text-identity semantics. - pub fn join( - source: &'a [u8], - space: ExGuid, - join: &crate::ParagraphJoin, - ) -> Result { - Ok(Self { - source, - written: join.apply(source, space)?, - }) - } - - /// Splits a paragraph and updates its children, lists, tags and title metadata atomically. - /// Fields and associated run metadata are rejected before I/O. - pub fn split( - source: &'a [u8], - space: ExGuid, - split: &crate::ParagraphSplit, - ) -> Result { - Ok(Self { - source, - written: split.apply(source, space)?, - }) - } - - /// Prepares an insertion and its dependent metadata in one revision, without I/O. - pub fn insert( - source: &'a [u8], - space: ExGuid, - insertion: &crate::Insertion, - ) -> Result { - Ok(Self { - source, - written: insertion.apply(source, space)?, - }) - } - - /// Validates and prepares a text edit without I/O, with `replace_text` semantics. - pub fn text( - source: &'a [u8], - space: ExGuid, - object: ExGuid, - range: std::ops::Range, - replacement: &str, - ) -> Result { - Ok(Self { - source, - written: crate::replace_text(source, space, object, range, replacement)?, - }) - } - - /// Changes character formatting over a UTF-16 range, preserving unselected runs and styles. - /// A zero-length range sets the insertion style only when the paragraph is empty. - /// Fields, associated run objects and boundaries splitting preserved run data are rejected. - pub fn format( - source: &'a [u8], - space: ExGuid, - object: ExGuid, - range: std::ops::Range, - attributes: &[crate::TextAttribute], - ) -> Result { - Ok(Self { - source, - written: crate::formatting::format_text(source, space, object, range, attributes)?, - }) - } - - /// The exact complete image this edit will publish; identities do not regenerate on commit. - /// Do not overwrite a live notebook with this image; use `commit` under exclusion. - pub fn as_bytes(&self) -> &[u8] { - &self.written - } - - /// The bytes publishing this edit writes, against its snapshot's stamp. - pub fn transaction(&self) -> Transaction { - Transaction::between(self.source, &self.written) - } - - /// `Transaction::commit` of this edit. - pub fn commit(&self, io: &mut impl CommitIo) -> Result<(), CommitError> { - self.transaction().commit(io) - } - - /// `Transaction::commit_file` of this edit. - #[cfg(any(unix, windows))] - pub fn commit_file(&self, path: impl AsRef) -> Result<(), CommitError> { - self.transaction().commit_file(path) - } -} - -/// `confirm_snapshot` under the same whole-file exclusion `commit_file` uses. -#[cfg(any(unix, windows))] -pub fn confirm_file_snapshot(path: impl AsRef, source: &[u8]) -> Result<(), CommitError> { - let mut io = FileIo::open(path, true).map_err(|error| CommitError { - state: CommitState::NotCommitted, - error, - })?; - let result = confirm_snapshot(&mut io, source); - io.finish(result) -} - -/// Checks and flushes a snapshot's stamp, then refreshes its header version metadata. -/// No revision is added; reread before using the snapshot for another physical commit. +/// Checks that the file still has `base`'s stamp and flushes it, then refreshes its header +/// version metadata. No revision is added; reread before committing on `base` again. /// The caller must hold OneNote-compatible exclusion and independently establish which -/// intents the snapshot contains. A successful read alone is not a durable acknowledgement. -pub fn confirm_snapshot(io: &mut impl CommitIo, source: &[u8]) -> Result<(), CommitError> { +/// intents the image contains. A successful read alone is not a durable acknowledgement. +pub fn confirm(io: &mut impl CommitIo, base: &Stamp) -> Result<(), CommitError> { let mut state = CommitState::NotCommitted; let result = (|| -> io::Result<()> { - let header = crate::Header::parse(source).map_err(io::Error::other)?; + let header = crate::Header::parse(&base.header).map_err(io::Error::other)?; let generation = header .generation .checked_add(1) @@ -427,7 +179,7 @@ pub fn confirm_snapshot(io: &mut impl CommitIo, source: &[u8]) -> Result<(), Com version[..16].copy_from_slice(&crate::write::fresh_guid().map_err(io::Error::other)?); version[16..24].copy_from_slice(&generation.to_le_bytes()); version[24..].copy_from_slice(&crate::write::fresh_guid().map_err(io::Error::other)?); - Stamp::of(source).map_err(io::Error::other)?.check(io)?; + base.check(io)?; state = CommitState::Unknown; io.flush()?; write_all(io, 212, &version)?; @@ -493,26 +245,6 @@ fn write_all(io: &mut impl CommitIo, mut offset: u64, mut bytes: &[u8]) -> io::R Ok(()) } -/// Publishes a scalar revision after checking the locked file against its snapshot. -/// Unknown outcomes require rereading; Committed errors affect lock release. -pub fn commit_property_bytes( - io: &mut impl CommitIo, - source: &[u8], - space: ExGuid, - object: ExGuid, - property: u32, - value: &[u8], -) -> Result<(), CommitError> { - let written = - replace_property_bytes(source, space, object, property, value).map_err(|error| { - CommitError { - state: CommitState::NotCommitted, - error: io::Error::new(ErrorKind::InvalidData, error), - } - })?; - Transaction::between(source, &written).commit(io) -} - /// What a commit requires unchanged since its snapshot: the header, which every committed /// transaction and every placement rewrites (MS-ONESTORE 2.3.1 `guidFileVersion`), and the /// length appends start from. Equal stamps name the same committed image. @@ -619,31 +351,6 @@ impl TryFrom for Transaction { } impl Transaction { - /// The transaction turning `source`, a parsed image, into `written`, which extends it. - pub(crate) fn between(source: &[u8], written: &[u8]) -> Self { - let differs = |at: &usize| source[*at] != written[*at]; - let mut patches = Vec::new(); - let mut offset = 1024; - while let Some(start) = (offset..source.len()).find(differs) { - let mut end = start + 1; - // Equal gaps shorter than a write request's overhead join the patch. - while let Some(next) = (end..source.len().min(end + 64)).find(differs) { - end = next + 1; - } - patches.push((start as u64, written[start..end].to_vec())); - offset = end; - } - Self { - base: Stamp { - header: *source.first_chunk().unwrap(), - length: source.len() as u64, - }, - append: written[source.len()..].to_vec(), - patches, - header: *written.first_chunk().unwrap(), - } - } - /// The image this transaction applies to. pub fn base(&self) -> &Stamp { &self.base diff --git a/crates/onestore/src/create.rs b/crates/onestore/src/create.rs index 4c3824efd1c512308787f79d4b83d3fc60c52383..c39c23aa9ee1c14540c8df06d473eeb5c268e8e9 100644 --- a/crates/onestore/src/create.rs +++ b/crates/onestore/src/create.rs @@ -17,6 +17,21 @@ pub(crate) fn string(value: &str) -> Vec { .collect() } +/// An author's initials as OneNote 2010 stores them beside the name (`0x1c001df8`, with +/// no MS-ONE entry): Office's default, the first letter of each word of the name. +pub(crate) fn initials(author: &str) -> String { + author + .split_whitespace() + .filter_map(|word| word.chars().next()) + .flat_map(char::to_uppercase) + .collect() +} + +/// An author object's properties as OneNote 2010 writes them: initials, then the name. +pub(crate) fn author_properties(name: &str) -> Vec<(u32, Vec)> { + vec![(0x1c001df8, string(&initials(name))), (0x1c001d75, string(name))] +} + pub(crate) fn default_text_style() -> Vec<(u32, Vec)> { vec![ (0x14001c3b, 0x409_u32.to_le_bytes().to_vec()), @@ -249,6 +264,7 @@ pub fn create_section(file_name: &str, text: &str, author: &str) -> Result Result Result) -> Result> { + if file_name.contains(['/', '\\', '\0']) || !file_name.to_ascii_lowercase().ends_with(".one") { + return Err(Error { + offset: 0, + message: "Invalid section name", + }); + } + let timestamp = current_timestamps()?.1.to_le_bytes().to_vec(); + let id = |value: u32| value.to_le_bytes().to_vec(); + let spaces = vec![NewSpace { + id: 1, + roots: vec![(1, 10), (2, 11)], + objects: vec![ + NewObject { + id: 10, + jcid: 0x60007, + properties: vec![ + (0x1c001c30, fresh_guid()?.to_vec()), + (0x18001c65, timestamp), + ], + }, + NewObject { + id: 11, + jcid: 0x20031, + properties: vec![ + (0x14001d82, id(40)), + (0x1400348b, id(40)), + (0x14001cbe, id(color.unwrap_or(0xffff_ffff))), + ], + }, + ], + }]; + create(file_name, FileType::Section, spaces) +} + /// Creates a notebook table of contents from section filenames and file identities. pub fn create_table_of_contents(file_name: &str, sections: &[(&str, [u8; 16])]) -> Result> { if file_name.contains(['/', '\\', '\0']) diff --git a/crates/onestore/src/edit.rs b/crates/onestore/src/edit.rs index 4e97286956a4152a579493ae92dca906b56380a4..7caa327195eb331e17f8910c85a4dbd62671596f 100644 --- a/crates/onestore/src/edit.rs +++ b/crates/onestore/src/edit.rs @@ -28,26 +28,6 @@ pub(crate) fn automatic_title(text: &str) -> &str { /// Insertion at a run boundary uses the following run, except at the end of text. /// The final run retains its insertion style even when emptied. /// Replacing a range with identical text leaves its existing formatting unchanged. -/// Returns a complete file image without I/O; use `commit_file_text` to update an existing file. -pub fn replace_text( - source: &[u8], - space: ExGuid, - object: ExGuid, - range: Range, - replacement: &str, -) -> Result, Error> { - if range.start > range.end || replacement.contains(['\0', '\n', '\r', '\u{fffc}']) { - return Err(Error { - offset: 0, - message: "Use a valid text range and ordinary paragraph text", - }); - } - crate::active::write(source, space, |active| { - text_changes(active, object, range, replacement) - }) -} - -/// `replace_text` on an active page. pub(crate) fn text_changes( active: &ActivePage<'_>, object: ExGuid, @@ -312,7 +292,9 @@ fn rewrite( .roots .get(&2) .ok_or_else(|| invalid("Page title metadata is unavailable"))?; - let Kind::Metadata { title, .. } = &revision.nodes[metadata].kind else { + let (Kind::Metadata { title, .. } | Kind::ConflictMetadata { title, .. }) = + &revision.nodes[metadata].kind + else { return Err(invalid("Page title metadata is unavailable")); }; let cached: Vec<_> = title_text @@ -544,7 +526,7 @@ pub(crate) fn update_title( .roots .get(&2) .ok_or_else(|| invalid("Page title metadata is unavailable"))?; - if raw.objects[metadata].jcid != 0x20030 { + if ![0x20030, 0x20038].contains(&raw.objects[metadata].jcid) { return Err(invalid("Page title metadata is unavailable")); } let title = string(&title); diff --git a/crates/onestore/src/formatting.rs b/crates/onestore/src/formatting.rs index 44b35d32379deb52ba3e76da402d9355b1f7a8e0..61d0da02fc7f81fc05727c6e1fc6911bdd9ef3f9 100644 --- a/crates/onestore/src/formatting.rs +++ b/crates/onestore/src/formatting.rs @@ -100,20 +100,8 @@ impl TextAttribute { } } -pub(crate) fn format_text( - source: &[u8], - space: ExGuid, - object: ExGuid, - range: Range, - attributes: &[TextAttribute], -) -> Result, Error> { - crate::active::write(source, space, |active| { - format_changes(active, object, range, attributes, &[]) - }) -} - -/// `format_text` on an active page, also removing the `cleared` properties from the range's -/// run styles so the text inherits them. +/// Character formatting set over a UTF-16 range, also removing the `cleared` properties +/// from the range's run styles so the text inherits them. pub(crate) fn format_changes( active: &ActivePage<'_>, object: ExGuid, diff --git a/crates/onestore/src/insertion.rs b/crates/onestore/src/insertion.rs index 0ce998267f8effcd436520af297ca20ae50e6347..dda6301876eff8cc7d772db236a5b0c6d15517a3 100644 --- a/crates/onestore/src/insertion.rs +++ b/crates/onestore/src/insertion.rs @@ -6,10 +6,8 @@ use crate::{ op::content::{NATIVE_INDENTS, measurement_bytes}, write::{PropertyObject, fresh_guid}, }; -use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, - ops::Range, sync::Arc, }; @@ -17,59 +15,29 @@ fn invalid(message: &'static str) -> Error { Error { offset: 0, message } } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, Clone, PartialEq)] enum Placement { Paragraph { before: Option }, Outline { x: f32, y: f32 }, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -struct FormatSpan { - guid: [u8; 16], - range: Range, - attributes: Vec, -} - -/// A paragraph or outline insertion with stable object identities and creation time. -/// Retain this intent across rebases; constructing another intent allocates different identities. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Insertion { +/// A paragraph or outline insertion with its creation time; `changes_as` names the new +/// objects. +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct Insertion { guid: [u8; 16], parent: ExGuid, placement: Placement, text: String, author: String, created: u32, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - formats: Vec, } impl Insertion { - /// Adds a nonoverlapping UTF-16 formatting span, retaining all existing identities. - /// Gaps use the ordinary insertion style; only empty text accepts a zero-length span. - pub fn with_formatting( - &self, - range: Range, - attributes: &[crate::TextAttribute], - ) -> Result { - let mut intent = self.clone(); - intent.formats.push(FormatSpan { - guid: fresh_guid()?, - range, - attributes: attributes.to_vec(), - }); - intent.formats.sort_by_key(|span| span.range.start); - intent.validate()?; - Ok(intent) - } - /// Inserts before a direct child, or appends when `before` is None. /// The parent must be an editable outline, paragraph, outline group or table cell. /// Carriage returns represent soft line breaks; line feeds and embedded-field markers are rejected. - pub fn paragraph( + pub(crate) fn paragraph( parent: ExGuid, before: Option, text: &str, @@ -79,38 +47,10 @@ impl Insertion { } /// Adds an outline to an editable page at coordinates measured in points. - pub fn outline(page: ExGuid, x: f32, y: f32, text: &str, author: &str) -> Result { + pub(crate) fn outline(page: ExGuid, x: f32, y: f32, text: &str, author: &str) -> Result { Self::new(page, Placement::Outline { x, y }, text, author) } - /// Changes a paragraph intent's placement while retaining its identities, text and author. - pub fn reposition_paragraph( - &self, - parent: ExGuid, - before: Option, - ) -> Result { - if !matches!(self.placement, Placement::Paragraph { .. }) { - return Err(invalid("An outline intent cannot become a paragraph")); - } - let mut intent = self.clone(); - intent.parent = parent; - intent.placement = Placement::Paragraph { before }; - intent.validate()?; - Ok(intent) - } - - /// Changes an outline intent's placement while retaining its identities, text and author. - pub fn reposition_outline(&self, page: ExGuid, x: f32, y: f32) -> Result { - if !matches!(self.placement, Placement::Outline { .. }) { - return Err(invalid("A paragraph intent cannot become an outline")); - } - let mut intent = self.clone(); - intent.parent = page; - intent.placement = Placement::Outline { x, y }; - intent.validate()?; - Ok(intent) - } - fn new(parent: ExGuid, placement: Placement, text: &str, author: &str) -> Result { let intent = Self { guid: fresh_guid()?, @@ -119,28 +59,11 @@ impl Insertion { text: text.to_owned(), author: author.to_owned(), created: current_timestamps()?.0, - formats: Vec::new(), }; intent.validate()?; Ok(intent) } - /// Identity of the new paragraph, or the new outline for an outline insertion. - pub fn object(&self) -> ExGuid { - ExGuid { - guid: self.guid, - n: 1, - } - } - - /// Identity of the insertion's ordinary rich-text object. - pub fn text_object(&self) -> ExGuid { - ExGuid { - guid: self.guid, - n: 2, - } - } - fn validate(&self) -> Result<(), Error> { if self.guid == [0; 16] || self.parent.guid == [0; 16] @@ -156,69 +79,11 @@ impl Insertion { { return Err(invalid("Outline coordinates must be finite")); } - if !self.formats.is_empty() { - let units: Vec<_> = self.text.encode_utf16().collect(); - let length = u32::try_from(units.len()) - .map_err(|_| invalid("Text exceeds UTF-16 offset range"))?; - let mut identities = BTreeSet::from([self.guid]); - let mut end = 0; - for span in &self.formats { - if span.guid == [0; 16] || !identities.insert(span.guid) { - return Err(invalid("Formatting identities must be distinct")); - } - if span.range.start > span.range.end - || span.range.end > length - || span.range.start < end - || (span.range.is_empty() && (!units.is_empty() || self.formats.len() != 1)) - { - return Err(invalid( - "Formatting spans must be nonoverlapping ranges within the new text", - )); - } - for boundary in [span.range.start, span.range.end] { - if boundary > 0 - && boundary < length - && (0xd800..=0xdbff).contains(&units[boundary as usize - 1]) - { - return Err(invalid("Formatting boundary splits a surrogate pair")); - } - } - crate::formatting::attribute_values(&span.attributes)?; - end = span.range.end; - } - } Ok(()) } - pub(crate) fn apply(&self, source: &[u8], space: ExGuid) -> Result, Error> { - self.validate()?; - crate::active::write(source, space, |active| self.changes(active)) - } - - pub(crate) fn changes(&self, active: &ActivePage<'_>) -> Result { - let paragraph = ExGuid { - guid: self.guid, - n: if matches!(self.placement, Placement::Outline { .. }) { - 3 - } else { - 1 - }, - }; - let changes = self.changes_as(active, self.object(), paragraph, self.text_object())?; - let raw = &active.live.revision; - if changes - .keys() - .any(|id| id.guid == self.guid && raw.objects.contains_key(id)) - { - return Err(invalid( - "An insertion identity is already present; reconcile the existing edit", - )); - } - Ok(changes) - } - - /// `changes` creating the outline or paragraph `object`, the paragraph `paragraph` and its - /// text `text` under those identities. + /// The objects creating the outline or paragraph `object`, the paragraph `paragraph` and + /// its text `text` under those identities. pub(crate) fn changes_as( &self, active: &ActivePage<'_>, @@ -321,7 +186,7 @@ impl Insertion { (0x10001cfe, 0x409_u16.to_le_bytes().to_vec()), ], ), - (author, 0x120001, vec![(0x1c001d75, string(&self.author))]), + (author, 0x120001, crate::create::author_properties(&self.author)), (default, 0x12004d, default_text_style()), ] { new.insert( @@ -333,51 +198,6 @@ impl Insertion { }, ); } - if !self.formats.is_empty() { - let mut segments = Vec::new(); - let mut end = 0; - for span in &self.formats { - if end < span.range.start { - segments.push((span.range.start, default)); - } - let id = ExGuid { - guid: span.guid, - n: 1, - }; - let mut style = PropertyObject { - jcid: 0x12004d, - bytes: properties(&default_text_style())?, - global_ids: Arc::new(BTreeMap::from([(0, span.guid)])), - }; - let values = crate::formatting::attribute_values(&span.attributes)?; - style.set( - &values - .iter() - .map(|(id, value)| (*id, value.as_slice())) - .collect::>(), - )?; - new.insert(id, style); - segments.push((span.range.end, id)); - end = span.range.end; - } - let length = u32::try_from(self.text.encode_utf16().count()) - .map_err(|_| invalid("Text exceeds UTF-16 offset range"))?; - if end < length { - segments.push((length, default)); - } - if !segments.iter().any(|(_, id)| *id == default) { - new.remove(&default); - } - let target = new.get_mut(&text).unwrap(); - let mut references = Vec::new(); - let mut ends = Vec::new(); - for (end, id) in segments { - references.extend_from_slice(&target.reference(id)?); - ends.extend_from_slice(&end.to_le_bytes()); - } - ends.truncate(ends.len() - 4); - target.set(&[(0x24001e13, &references), (0x1c001e12, &ends)])?; - } if let Placement::Outline { x, y } = self.placement { let children = reference(paragraph)?; new.insert( @@ -449,7 +269,7 @@ impl Insertion { .roots .get(&2) .ok_or_else(|| invalid("Page title metadata is unavailable"))?; - if raw.objects[metadata].jcid != 0x20030 { + if ![0x20030, 0x20038].contains(&raw.objects[metadata].jcid) { return Err(invalid("Page title metadata is unavailable")); } let title = string(&title); diff --git a/crates/onestore/src/lib.rs b/crates/onestore/src/lib.rs index d60291cd2898af3dfb123177aa011dc332c413a1..065d879e8c8c54755bb14f599a51308e60dec2a1 100644 --- a/crates/onestore/src/lib.rs +++ b/crates/onestore/src/lib.rs @@ -28,29 +28,21 @@ mod toc; mod tree; mod write; -pub use commit::{ - CommitError, CommitIo, CommitState, PreparedEdit, Stamp, Transaction, commit_property_bytes, - commit_text, confirm_snapshot, place, -}; +pub use commit::{CommitError, CommitIo, CommitState, Stamp, Transaction, confirm, place}; #[cfg(any(unix, windows))] -pub use commit::{ - commit_file_property, commit_file_text, confirm_file_snapshot, place_file, read_file, - read_file_limited, -}; -pub use create::{create_section, create_table_of_contents}; -pub use edit::replace_text; +pub use commit::{confirm_file, place_file, read_file, read_file_limited}; +pub use create::{create_empty_section, create_section, create_table_of_contents}; pub use files::FileDataReference; pub use formatting::{FONT_SIZES, TextAttribute}; -pub use insertion::Insertion; +pub(crate) use insertion::Insertion; pub use objects::{Object, ObjectData, ObjectReferences, ResolvedRevision}; pub use outline::OutlineEdit; -pub use pages::{PageCreation, PageEdit, PagePosition}; -pub use paragraph::{ParagraphJoin, ParagraphSplit}; +pub use pages::{ConflictPage, PageCreation, PageEdit, PagePosition}; +pub(crate) use paragraph::{ParagraphJoin, ParagraphSplit}; pub use properties::{IdStream, Property, PropertySets, Value}; pub use revisions::{ExGuid, ObjectSpace, Revision, RevisionIndex}; pub use section::{Arena, Section}; pub use snapshot::{read_snapshot, read_storage_snapshot}; pub use store::{Chunk, Error, FileType, Header, Node, NodeList, Reference, Store}; -pub use toc::TocEdit; -pub use tree::TreeEdit; -pub use write::replace_property_bytes; +pub use toc::{TocEdit, edit_table_of_contents}; +pub(crate) use tree::TreeEdit; diff --git a/crates/onestore/src/op/apply.rs b/crates/onestore/src/op/apply.rs index d82c371bd17b36a2e86298e7d4b2981d446390cc..b9b9c292ac294d898bf012fb16235541b124fdbd 100644 --- a/crates/onestore/src/op/apply.rs +++ b/crates/onestore/src/op/apply.rs @@ -49,6 +49,28 @@ impl<'a> Section<'a> { Op::Section(op) => section.apply_section(author, op)?, } } + let spaces: BTreeSet<_> = edit + .ops + .iter() + .filter_map(|op| match op { + Op::Page { space, .. } => Some(*space), + Op::Section(_) => None, + }) + .collect(); + for space in spaces { + // A page the edit went on to delete has no cells left to check. + let Ok(page) = section.active(space) else { + continue; + }; + if page.view.nodes.values().any(|node| { + matches!(node.kind, Kind::Cell { .. }) && node.children.is_empty() + }) { + return Err(OpError::Unsupported( + "A table cell keeps a paragraph; insert its replacement first", + ) + .into()); + } + } Ok(()) }) }) @@ -99,28 +121,26 @@ impl<'a> Section<'a> { } SectionOp::Import { creation, page } => { self.apply_section(author, &SectionOp::Create(creation.clone()))?; + self.fill(author, creation.space(), page)?; + } + SectionOp::Conflict { + of, + creation, + page, + objects, + } => { + let level = self + .pages()? + .into_iter() + .find(|(space, ..)| space == of) + .map(|(.., level)| level) + .ok_or(OpError::TargetUnavailable(*of))?; let space = creation.space(); - let before = self.page(space)?; - let mut after = page.clone(); - // The created page's title stays; the imported page brings the rest. - after.objects.retain(|object| !matches!(object, PageObject::Title(_))); - for object in before.objects.iter().rev() { - if let PageObject::Title(_) = object { - after.objects.insert(0, object.clone()); - } - } - after.title = before.title.clone(); - after.identity = before.identity; - after.created = before.created; - after.margin_origin = before.margin_origin; - for op in super::lower_page(&before, &after)? { - Writer { - section: self, - space, - author, - } - .apply(&op)?; - } + let (roots, created, metadata) = creation.conflict(self, &page.title, level)?; + self.create_space(space, roots, created)?; + self.fill(author, space, page)?; + self.mark_conflict(space, creation, page, objects)?; + self.link_conflict(*of, space, metadata)?; } SectionOp::Pages(edits) => { for (space, changes) in crate::pages::section_changes(self, edits, &[])? { @@ -128,15 +148,237 @@ impl<'a> Section<'a> { } } SectionOp::Delete(pages) => { - for (space, changes) in crate::pages::section_changes(self, &[], pages)? { - self.apply_changes(space, &[], changes)?; + let mut listed = Vec::new(); + let conflicts = self.conflicts()?; + for space in pages { + let of = conflicts.iter().find_map(|(of, conflicts)| { + conflicts.iter().any(|page| page.space == *space).then_some(*of) + }); + match of { + Some(of) => self.unlink_conflict(of, *space)?, + None => listed.push(*space), + } } + if !listed.is_empty() { + for (space, changes) in crate::pages::section_changes(self, &[], &listed)? { + self.apply_changes(space, &[], changes)?; + } + } + } + SectionOp::Color(color) => { + let root = self.root(); + let page = self.active(root)?; + let metadata = *page + .view + .roots + .get(&2) + .filter(|id| page.live.revision.objects.contains_key(id)) + .ok_or_else(|| invalid("The section has no metadata to colour"))?; + let mut object = crate::write::PropertyObject::from_object( + &page.live.revision.objects[&metadata], + )?; + object.set(&[(0x14001cbe, &color.unwrap_or(0xffff_ffff).to_le_bytes())])?; + self.apply_changes(root, &[], BTreeMap::from([(metadata, object)]))?; + } + } + Ok(()) + } + + /// Writes `page`'s content onto the page just created in `space`, whose title stays. + fn fill(&mut self, author: &str, space: ExGuid, page: &crate::page::Page) -> Result<(), Failure> { + let before = self.page(space)?; + let mut after = page.clone(); + after.objects.retain(|object| !matches!(object, PageObject::Title(_))); + for object in &mut after.objects { + // Stroke points are page coordinates; new ink takes its position from them. + if let PageObject::Ink(ink) = object { + ink.layout = Default::default(); + } + } + for object in before.objects.iter().rev() { + if let PageObject::Title(_) = object { + after.objects.insert(0, object.clone()); + } + } + after.title = before.title.clone(); + after.identity = before.identity; + after.created = before.created; + after.margin_origin = before.margin_origin; + for op in super::lower_page(&before, &after)? { + Writer { + section: self, + space, + author, + } + .apply(&op)?; + } + Ok(()) + } + + /// Marks a filled conflict page as OneNote 2010 does: the page read-only, deletable and + /// a conflict page (MS-ONE 2.1.2), and each of `objects` a conflict object (2.1.1). The + /// created title stands for `page`'s, so objects of that title mark its counterparts. + fn mark_conflict( + &mut self, + space: ExGuid, + creation: &crate::PageCreation, + page: &crate::page::Page, + objects: &[ExGuid], + ) -> Result<(), Failure> { + let created = |n| ExGuid { + guid: creation.space().guid, + n, + }; + let mut titles = BTreeMap::new(); + for object in &page.objects { + let PageObject::Title(title) = object else { + continue; + }; + titles.insert(title.id, created(13)); + if let Some(outline) = title.outlines.first() { + titles.insert(outline.id, created(14)); + if let Some(paragraph) = outline.paragraphs.first() { + titles.insert(paragraph.id, created(15)); + if let Some(text) = paragraph.text() { + titles.insert(text.id, created(16)); + } + } + } + } + let active = self.active(space)?; + let mut changes = BTreeMap::new(); + let mut page_node = crate::write::PropertyObject::from_object( + &active.live.revision.objects[&creation.object()], + )?; + page_node.set(&[(0x88001cde, &[]), (0x88001d0c, &[]), (0x88001d7c, &[])])?; + changes.insert(creation.object(), page_node); + for id in objects { + let id = titles.get(id).copied().unwrap_or(*id); + if !active.live.is_reachable(id) { + return Err(OpError::TargetUnavailable(id).into()); + } + let mut object = + crate::write::PropertyObject::from_object(&active.live.revision.objects[&id])?; + // A table is rendered as conflicting but is not selectable as such. + if object.jcid == 0x60022 { + object.set(&[(0x88001d96, &[])])?; + } else { + object.set(&[(0x88001ddb, &[]), (0x88001d96, &[])])?; + } + changes.insert(id, object); + } + self.apply_changes(space, &[], changes)?; + Ok(()) + } + + /// Lists conflict page `space` under page `of` as OneNote 2010 does: the page's manifest + /// references it and keeps a copy of its metadata, and the page's metadata and the + /// section's copy of it say the page has conflict pages (MS-ONE 2.2.34, 2.3.71). + fn link_conflict( + &mut self, + of: ExGuid, + space: ExGuid, + mut metadata: crate::write::PropertyObject, + ) -> Result<(), Failure> { + use crate::{pages::set_references, write::PropertyObject}; + let page = self.active(of)?; + let manifest_id = page.view.roots[&1]; + let manifest_node = &page.view.nodes[&manifest_id]; + let mut spaces = manifest_node.spaces.clone(); + spaces.push(space); + let mut copies = objects(manifest_node, 0x24003442); + let copy = crate::pages::metadata_id(space); + copies.push(copy); + metadata.reference(copy)?; + let mut manifest = PropertyObject::from_object(&page.live.revision.objects[&manifest_id])?; + set_references(&mut manifest, 0x2c001d63, &spaces)?; + set_references(&mut manifest, 0x24003442, &copies)?; + self.apply_changes( + of, + &[], + BTreeMap::from([(manifest_id, manifest), (copy, metadata)]), + )?; + self.has_conflicts(of, true) + } + + /// Deletes conflict page `space` of page `of` as OneNote 2010 does: the manifest stops + /// referencing it (its metadata copy stays) and the last one's removal clears + /// `HasConflictPages`; the conflict page's own space is left as it is. + fn unlink_conflict(&mut self, of: ExGuid, space: ExGuid) -> Result<(), Failure> { + use crate::{pages::set_references, write::PropertyObject}; + let page = self.active(of)?; + let manifest_id = page.view.roots[&1]; + let spaces: Vec = page.view.nodes[&manifest_id] + .spaces + .iter() + .copied() + .filter(|listed| *listed != space) + .collect(); + let mut manifest = PropertyObject::from_object(&page.live.revision.objects[&manifest_id])?; + if spaces.is_empty() { + manifest.remove(&[0x2c001d63])?; + } else { + set_references(&mut manifest, 0x2c001d63, &spaces)?; + } + self.apply_changes(of, &[], BTreeMap::from([(manifest_id, manifest)]))?; + if spaces.is_empty() { + self.has_conflicts(of, false)?; + } + Ok(()) + } + + /// Sets or clears `HasConflictPages` on page `of`'s metadata and on the section's copy + /// of it, where its series keeps one. + fn has_conflicts(&mut self, of: ExGuid, value: bool) -> Result<(), Failure> { + use crate::write::PropertyObject; + let flag = |object: &crate::Object<'_>| -> Result { + let mut object = PropertyObject::from_object(object)?; + if value { + object.set(&[(0x88001d97, &[])])?; + } else { + object.remove(&[0x88001d97])?; } + Ok(object) + }; + let page = self.active(of)?; + let stored_id = page.view.roots[&2]; + let identity = crate::pages::metadata_guid(&page.view.nodes[&stored_id])?; + let stored = flag(&page.live.revision.objects[&stored_id])?; + self.apply_changes(of, &[], BTreeMap::from([(stored_id, stored)]))?; + let root = self.root(); + let section = self.active(root)?; + let copy = section + .view + .nodes + .values() + .find(|node| node.spaces.contains(&of)) + .map(|series| objects(series, 0x24003442)) + .unwrap_or_default() + .into_iter() + .find(|id| { + section.view.nodes.get(id).is_some_and(|node| { + crate::pages::metadata_guid(node).is_ok_and(|guid| guid == identity) + }) + }); + if let Some(id) = copy { + let object = flag(§ion.live.revision.objects[&id])?; + self.apply_changes(root, &[], BTreeMap::from([(id, object)]))?; } Ok(()) } } +/// The objects an element's reference property names. +fn objects(node: &crate::document::Element<'_>, property: u32) -> Vec { + node.extra[0] + .iter() + .find_map(|field| match &field.value { + crate::document::FieldValue::Objects(ids) if field.id == property => Some(ids.clone()), + _ => None, + }) + .unwrap_or_default() +} + /// Whether an op may store more than one revision of a page, so its failure part way needs /// the page as it was. fn stores_more_than_once(op: &Op) -> bool { @@ -348,6 +590,19 @@ impl<'a> Writer<'_, 'a> { Ok(BTreeMap::from([(*metadata, object)])) }) } + PageOp::Color(color) => self.write(|page| { + let [node] = crate::active::manifest_pages(&page.view)[..] else { + return Err(invalid("The page has no page node to colour")); + }; + let mut object = + crate::write::PropertyObject::from_object(&page.live.revision.objects[&node])?; + // OneNote's "No color" removes the property. + match color { + Some(color) => object.set(&[(crate::page::PAGE_COLOR, &color.to_le_bytes())])?, + None => object.remove(&[crate::page::PAGE_COLOR])?, + } + Ok(BTreeMap::from([(node, object)])) + }), PageOp::Insert { container, before, @@ -386,18 +641,6 @@ impl<'a> Writer<'_, 'a> { } PageOp::Delete { object } => { self.target(*object)?; - let page = self.page()?; - if matches!(page.view.nodes[object].kind, Kind::Paragraph { .. }) { - let container = self.container(*object)?; - if matches!(self.page()?.view.nodes[&container].kind, Kind::Cell { .. }) - && levels::flat_children(&self.page()?.view, container) == [*object] - { - return Err(OpError::Unsupported( - "A table cell keeps a paragraph; insert its replacement first", - ) - .into()); - } - } let edit = TreeEdit::delete(*object, self.author)?; self.write(|page| edit.changes(page)) } diff --git a/crates/onestore/src/op/levels.rs b/crates/onestore/src/op/levels.rs index 4143bed29890b99348f68feec990d63a6e3f56c3..b015fe417e7d64573bc74674e63c2ed85ab51336 100644 --- a/crates/onestore/src/op/levels.rs +++ b/crates/onestore/src/op/levels.rs @@ -14,21 +14,6 @@ fn invalid(message: &'static str) -> Error { Error { offset: 0, message } } -/// The children of `container` with its outline groups lifted into it, in order. -pub(crate) fn flat_children(view: &Revision<'_>, container: ExGuid) -> Vec { - fn flat(view: &Revision<'_>, id: ExGuid, out: &mut Vec) { - for child in &view.nodes[&id].children { - match view.nodes[child].kind { - Kind::OutlineGroup => flat(view, *child, out), - _ => out.push(*child), - } - } - } - let mut children = Vec::new(); - flat(view, container, &mut children); - children -} - /// How much deeper than `container` each of its children lies, groups lifted. pub(crate) fn stored_depths(view: &Revision<'_>, container: ExGuid) -> Vec<(ExGuid, u32)> { fn walk(view: &Revision<'_>, id: ExGuid, base: u32, out: &mut Vec<(ExGuid, u32)>) { @@ -46,23 +31,6 @@ pub(crate) fn stored_depths(view: &Revision<'_>, container: ExGuid) -> Vec<(ExGu out } -/// Lifts the children of `container`'s outline groups into it. -pub(crate) fn ungroup_changes(active: &ActivePage<'_>, container: ExGuid) -> Result { - let children = flat_children(&active.view, container); - let parents = active.editable_parents(container)?; - let modified = crate::create::current_timestamps()?.0.to_le_bytes(); - let raw = &active.live.revision; - let mut holder = PropertyObject::from_object(&raw.objects[&container])?; - let mut references = Vec::new(); - for child in children { - references.extend(holder.reference(child)?); - } - holder.set(&[(0x24001c20, &references), (0x14001d7a, &modified)])?; - let mut changed = BTreeMap::from([(container, holder)]); - crate::formatting::touch_ancestors(raw, parents, container, &modified, &mut changed)?; - Ok(changed) -} - /// Children in runs: one child at the container's child level, or a run sharing a deeper /// depth. pub(crate) type Runs = Vec<(u32, Vec)>; diff --git a/crates/onestore/src/op/lower.rs b/crates/onestore/src/op/lower.rs index 249ac6809d2244831cbf5feb195cf5cc73494ed8..995f4fafdb1bbf293c56264f3c604bf2a0b575f7 100644 --- a/crates/onestore/src/op/lower.rs +++ b/crates/onestore/src/op/lower.rs @@ -44,6 +44,7 @@ pub fn lower( identity: None, created: None, margin_origin: [0.0; 2], + color: None, objects: vec![PageObject::Outline(Outline { id: container, title: false, @@ -163,16 +164,6 @@ impl<'a> View<'a> { self.paragraphs.get(¶graph).and_then(|p| p.text()) } - /// How much deeper than `container` each of its children lies: their outline levels - /// less the paragraph's, or less nothing in an outline or cell. - pub(crate) fn depths(&self, container: ExGuid, children: &[ExGuid]) -> Option> { - let base = self.paragraphs.get(&container).map_or(0, |p| p.level); - children - .iter() - .map(|id| self.paragraphs.get(id)?.level.checked_sub(base)) - .collect() - } - /// Each outline's paragraphs in document order, a table's cells following its paragraph. pub(crate) fn documents(&self) -> impl Iterator> + '_ { fn walk<'a>(list: &'a [PageParagraph], out: &mut Vec<&'a PageParagraph>) { @@ -677,11 +668,6 @@ pub(crate) fn paragraph_fields( }) } -/// `paragraph_fields` encoded, as the page writer stores them. -pub(crate) fn paragraph_change(stored: &Paragraph, target: &Paragraph) -> Result { - paragraph_fields(stored, target)?.values() -} - /// Whether two tag lists store the same tags. pub(crate) fn same_tags(a: &[Tag], b: &[Tag]) -> bool { let key = |t: &Tag| { @@ -1555,6 +1541,9 @@ impl Lowering { let shown = fields.iter().all(|(id, shown)| { model::paragraph_text(&self.current, *id).is_some_and(|text| text.text.text() == shown) }); + if before.color != after.color { + self.emit(PageOp::Color(after.color))?; + } if before.created == after.created && shown { return Ok(()); } diff --git a/crates/onestore/src/op/mod.rs b/crates/onestore/src/op/mod.rs index 32eefd577750ba90de58345808041dca1fb91851..cdda21dc027bd590d687e5bd132a187f58f71acf 100644 --- a/crates/onestore/src/op/mod.rs +++ b/crates/onestore/src/op/mod.rs @@ -19,7 +19,7 @@ pub(crate) mod model; pub(crate) mod properties; pub(crate) mod table; #[cfg(test)] -mod tests; +pub(crate) mod tests; pub use lower::{lower, lower_page}; /// `op` applied to `page`, the model of what the section holds: the page `Section::apply` @@ -29,7 +29,7 @@ pub use model::apply as predict; /// Property identifiers with their encoded values. pub(crate) type Values = Vec<(u32, Vec)>; -/// One user action, applied whole or not at all. +/// One user action, applied whole or not at all; it leaves every table cell a paragraph. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct Edit { @@ -80,6 +80,8 @@ pub enum PageOp { created: u64, fields: Vec<(ExGuid, String)>, }, + /// The page's colour (View, Page Color), COLORREF; `None` is OneNote's "No color". + Color(Option), /// Inserts paragraphs before a direct child of `container`, or last. Children follow /// their parents in `paragraphs`; levels are absolute. Text keeps its spans' formats; /// lists, tags, styles and collapse state are set by their own ops. @@ -108,7 +110,7 @@ pub enum PageOp { parent: Option, before: Option, }, - /// Removes a subtree; an emptied table cell receives an empty paragraph. + /// Removes a subtree. Delete { object: ExGuid }, /// Sets a paragraph's absolute outline level, regrouping its container. Level { paragraph: ExGuid, level: u32 }, @@ -204,10 +206,23 @@ pub enum SectionOp { /// A page created with `creation` holding `page`'s content under the identities it /// carries (`Page::copy` gives fresh ones); the created title stays. Import { creation: PageCreation, page: Page }, + /// A read-only conflict page of the listed page `of`, as OneNote 2010 keeps the version of + /// a page a merge could not take: created with `creation` (not placed, its author the user + /// whose version it is, a title only where `page` has one) and holding `page` as `Import` + /// does, with `objects`, identities on `page`, marked as the conflicting ones. + Conflict { + of: ExGuid, + creation: PageCreation, + page: Page, + objects: Vec, + }, /// Page moves and indentation, in order. Pages(Vec), - /// Removes pages permanently; their revisions stay stored. + /// Removes pages permanently, listed or conflict pages; their revisions stay stored. Delete(Vec), + /// The section's colour as COLORREF, kept in its own metadata; `None` is OneNote's + /// "no colour". + Color(Option), } /// A character property that `PageOp::Format` can clear so the text inherits it. diff --git a/crates/onestore/src/op/model.rs b/crates/onestore/src/op/model.rs index 474982b04a5f52b4a6e518f2b65fa24b3e8be954..3e293dec4c83ba064814ce4667ec105fb681915e 100644 --- a/crates/onestore/src/op/model.rs +++ b/crates/onestore/src/op/model.rs @@ -603,6 +603,7 @@ fn interpret(page: &mut Page, op: &PageOp) -> Result<(), Error> { } page.created = Some(*created); } + PageOp::Color(color) => page.color = *color, PageOp::Text { text, range, with } => { let object = text_mut(page, *text)?; object.text = replace_text(&object.text, range.clone(), with)?; @@ -861,13 +862,7 @@ fn interpret(page: &mut Page, op: &PageOp) -> Result<(), Error> { let (path, index) = find(page, *object).ok_or_else(|| invalid("The object is not on the page"))?; let list = list_at(page, &path); - let range = subtree(list, index); - if !path.cells.is_empty() && range.len() == list.len() { - return Err(invalid( - "A table cell keeps a paragraph; insert its replacement first", - )); - } - list.drain(range); + list.drain(subtree(list, index)); } drop_emptied(page); retitle(page, None); diff --git a/crates/onestore/src/op/table.rs b/crates/onestore/src/op/table.rs index 37678fee9457d3bfe8bdecc47130b6926f0c3da8..1edccce0d28f1824f5352fbb048c6b5b1fe9533c 100644 --- a/crates/onestore/src/op/table.rs +++ b/crates/onestore/src/op/table.rs @@ -35,7 +35,6 @@ pub(crate) fn validate_table(table: &Table) -> Result<(), Error> { return Err(invalid("Every table row has one cell per column")); } for cell in &row.cells { - // An emptied cell keeps a replacement paragraph, as the tree writer provides. for paragraph in &cell.paragraphs { if let crate::page::ParagraphContent::Table(nested) = ¶graph.content { validate_table(nested)?; diff --git a/crates/onestore/src/op/tests.rs b/crates/onestore/src/op/tests.rs index 4501ae30eb9dadd83ce5bbb2c3e23f7027025eab..6fafcec7d42b38ad91f38f1af19b9365e581a98c 100644 --- a/crates/onestore/src/op/tests.rs +++ b/crates/onestore/src/op/tests.rs @@ -1,5 +1,6 @@ -//! Ops against the page-model writer: for random edits of corpus pages, lowering the edit -//! and applying the ops through a `Section` stores what `PreparedEdit::page` stores. +//! Ops against the model: for random edits of corpus pages, lowering the edit and applying +//! the ops through a `Section` stores the edited page, as the model oracle predicts and as +//! the sealed image reads back. use super::*; use crate::{ @@ -11,7 +12,7 @@ use crate::{ }, write::GUIDS, }; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; const SOURCES: &[(&str, &[u8])] = &[ ("outline-edit", include_bytes!("../../../../corpus/outline-edit/before/notebook/synthetic.one")), @@ -85,20 +86,25 @@ fn read(image: &[u8], space: ExGuid) -> Page { } /// `page` with writer-chosen identities replaced in document order: list nodes, which a -/// copy for a second owner gets, and tag field indices. +/// copy for a second owner gets (so equal definitions become one), tag field indices, and ink +/// coordinates at the resolution they are stored in. pub(super) fn normalize(page: &Page) -> Page { let mut page = page.clone(); let mut lists = BTreeMap::new(); + let mut canonical = BTreeMap::new(); let mut paths: Vec<_> = model::lists(&page).into_iter().map(|(path, _, _)| path).collect(); paths.sort_by_key(|path| format!("{path:?}")); + let definitions = page.definitions.clone(); for path in &paths { for paragraph in model::list_at(&mut page, path).iter_mut() { for list in &mut paragraph.lists { let next = ExGuid { guid: [0xee; 16], - n: 1 + lists.len() as u32, + n: 1 + canonical.len() as u32, }; - *list = *lists.entry(*list).or_insert(next); + let content = definitions.get(list).map_or(format!("{list:?}"), |d| format!("{d:?}")); + let id = *canonical.entry(content).or_insert(next); + *list = *lists.entry(*list).or_insert(id); } for tag in paragraph.tags.iter_mut() { tag.extra_set = 0; @@ -136,9 +142,113 @@ pub(super) fn normalize(page: &Page) -> Page { _ => Some((id, definition)), }) .collect(); + fn quantize(ink: &mut Ink) { + for stroke in &mut ink.strokes { + for point in &mut stroke.points { + *point = point.map(|value| (value * 10.0).round() / 10.0); + } + } + ink.groups.iter_mut().for_each(quantize); + } + for object in &mut page.objects { + if let PageObject::Ink(ink) = object { + quantize(ink); + } + } + for path in &paths { + for paragraph in model::list_at(&mut page, path).iter_mut() { + if let ParagraphContent::Ink(ink) = &mut paragraph.content { + quantize(ink); + } + } + } page } +/// `after` with what the writers derive rather than store as given: the title from the +/// title text, the values they give new objects where the model leaves them unset, and only +/// the definitions something uses. +fn as_written(after: &Page, before: &Page, stored: &Page) -> Page { + use serde_json::Value; + fn ids(value: &Value, out: &mut BTreeSet) { + match value { + Value::Object(map) => { + if let Some(Value::String(id)) = map.get("id") { + out.insert(id.clone()); + } + map.values().for_each(|value| ids(value, out)); + } + Value::Array(values) => values.iter().for_each(|value| ids(value, out)), + _ => {} + } + } + fn adopt(after: &mut Value, stored: &Value, old: &BTreeSet, new: bool) { + match (after, stored) { + (Value::Object(after), Value::Object(stored)) => { + let new = new + || matches!(after.get("id"), Some(Value::String(id)) if !old.contains(id)); + for (key, value) in after.iter_mut() { + let Some(written) = stored.get(key) else { + continue; + }; + // Ink coordinates are stored at a coarser resolution. + let near = |a: &Value, b: &Value| { + a.as_f64().zip(b.as_f64()).is_some_and(|(a, b)| (a - b).abs() < 0.05) + }; + let close_points = key == "points" + && value.as_array().zip(written.as_array()).is_some_and(|(a, b)| { + a.len() == b.len() + && a.iter().zip(b).all(|(a, b)| { + a.as_array().zip(b.as_array()).is_some_and(|(a, b)| { + a.len() == b.len() && a.iter().zip(b).all(|(a, b)| near(a, b)) + }) + }) + }); + let unset = new && (value.is_null() || value.as_array().is_some_and(Vec::is_empty)); + if close_points || unset { + *value = written.clone(); + } else { + adopt(value, written, old, new); + } + } + } + (Value::Array(after), Value::Array(stored)) if after.len() == stored.len() => { + for (value, written) in after.iter_mut().zip(stored) { + adopt(value, written, old, new); + } + } + _ => {} + } + } + let mut old = BTreeSet::new(); + ids(&serde_json::to_value(before).unwrap(), &mut old); + let mut value = serde_json::to_value(after).unwrap(); + adopt(&mut value, &serde_json::to_value(stored).unwrap(), &old, false); + let mut page: Page = serde_json::from_value(value).unwrap(); + page.title = stored.title.clone(); + unused_dropped(&mut page); + page +} + +/// Drops the definitions nothing on `page` uses, as reading a page leaves them out. +fn unused_dropped(page: &mut Page) { + let mut used = BTreeSet::new(); + for (_, _, list) in model::lists(page) { + for paragraph in list { + used.extend(paragraph.style); + used.extend(paragraph.lists.iter().copied()); + used.extend(paragraph.tags.iter().filter_map(|tag| tag.definition)); + if let ParagraphContent::Table(table) = ¶graph.content { + used.extend(table.tags.iter().filter_map(|tag| tag.definition)); + } + if let Some(text) = paragraph.text() { + used.extend(text.tags.iter().filter_map(|tag| tag.definition)); + } + } + } + page.definitions.retain(|id, _| used.contains(id)); +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub(super) enum Family { Text, @@ -224,7 +334,17 @@ fn id() -> ExGuid { new_id().unwrap() } -fn text_paragraph(text: &str, format: Format) -> PageParagraph { +/// `ops` on page `space` of `source` as one edit, sealed: the image they leave. +pub(crate) fn edited(source: &[u8], space: ExGuid, ops: Vec) -> Result, OpError> { + let arena = Arena::default(); + let mut section = Section::open(&arena, source.to_vec()).map_err(OpError::Failed)?; + let ops = ops.into_iter().map(|op| Op::Page { space, op }).collect(); + section.apply("Author", &Edit { at: AT, ops })?; + section.seal().map_err(OpError::Failed)?; + Ok(section.image()) +} + +pub(crate) fn text_paragraph(text: &str, format: Format) -> PageParagraph { PageParagraph { id: id(), parent: None, @@ -473,8 +593,10 @@ pub(super) fn mutate(page: &mut Page, rng: &mut Rng, family: Family) -> bool { } inserted.push(paragraph); } - // Children follow their parent; keep them after the first inserted paragraph. - inserted.sort_by_key(|p| p.parent.is_some()); + // Children follow their parent, the first inserted paragraph, directly. + let (children, mut inserted): (Vec<_>, Vec<_>) = + inserted.into_iter().partition(|p| p.parent.is_some()); + inserted.splice(1..1, children); let cell_level = list.first().map_or(1, |p| p.level); for paragraph in &mut inserted { if paragraph.parent.is_none() { @@ -993,125 +1115,70 @@ fn stroke(rng: &mut Rng) -> InkStroke { } } -/// How one differential case came out. +/// How one lowered edit came out. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub(super) enum Outcome { /// The edit made no change the writers store. Unchanged, - /// Both refused the edit. + /// Lowering or the section refused the edit; the section is as it was. Refused, - /// The page writer refused; the ops were written. - OnlyOps, - /// Identical bytes. - Bytes, - /// Equal pages, both images valid, the op revision no larger. - Model, - /// The op revision declares more than the page writer's. - Larger, + /// The sealed image reads back as the edited page. + Stored, Mismatch(String), } -/// Objects the space's active revision declares in `image`. -pub(super) fn declared(image: &[u8], space: ExGuid) -> usize { - let store = Store::parse(image).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let rid = index.active(space).unwrap(); - index.spaces[&space].revisions[&rid] - .nodes - .iter() - .filter_map(|node| match node.reference { - Some(crate::Reference::NodeList(chunk)) if node.id == 0xb0 => Some(chunk), - _ => None, - }) - .map(|chunk| { - store - .lists - .values() - .find(|list| list.fragments.first().is_some_and(|f| f.offset == chunk.offset)) - .map_or(0, |list| { - list.nodes - .iter() - .filter(|n| { - matches!(n.id, 0x2d | 0x2e | 0x41 | 0x42 | 0xa4 | 0xa5 | 0xc4 | 0xc5 | 0x72 | 0x73) - }) - .count() - }) - }) - .sum() -} - -/// Writes `after` over page `space` of `source` both ways and compares the images. -pub(super) fn differential(source: &[u8], space: ExGuid, after: &Page, seed: u64) -> Outcome { +/// Lowers `after` over page `space` of `source`, applies and seals the ops, and compares +/// the page the section holds with the edited page, with the model oracle's prediction and +/// with the page the sealed image reads back as. +pub(super) fn stored(source: &[u8], space: ExGuid, after: &Page, seed: u64) -> Outcome { let before = read(source, space); - let oracle = seeded(seed, || { - crate::create::at(AT, || crate::PreparedEdit::page(source, space, after, "Author")) - }); let arena = Arena::default(); let mut section = Section::open(&arena, source.to_vec()).unwrap(); - let ops = lower_page(&before, after); - let written = ops.as_ref().ok().map(|ops| { - seeded(seed, || { - let edit = Edit { - at: AT, - ops: ops - .iter() - .map(|op| Op::Page { - space, - op: op.clone(), - }) - .collect(), - }; - section.apply("Author", &edit).map(|()| { - let predicted = { - let mut page = before.clone(); - for op in ops { - model::apply(&mut page, op).unwrap(); - } - page - }; - (section.seal().unwrap(), predicted) - }) - }) - }); - let image = section.image(); - match (oracle, written) { - (Err(_), None | Some(Err(_))) => Outcome::Refused, - (Err(_), Some(Ok(_))) => Outcome::OnlyOps, - (Ok(_), None) => Outcome::Mismatch(format!("lowering refused: {:?}", ops.unwrap_err())), - (Ok(_), Some(Err(error))) => Outcome::Mismatch(format!("apply refused: {error:?} for {:?}", ops.unwrap())), - (Ok(edit), Some(Ok((transaction, predicted)))) => { - let stored = normalize(&read(&image, space)); - if normalize(&predicted) != stored { - return Outcome::Mismatch(format!( - "the model predicts otherwise: {}", - first_difference(&stored, &normalize(&predicted)) - )); - } - let expected = edit.as_bytes(); - if expected == source && transaction.is_none() { - return Outcome::Unchanged; - } - if expected == image.as_slice() { - return Outcome::Bytes; - } - let store = Store::parse(&image).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - if let Err(error) = index.validate_current() { - return Outcome::Mismatch(format!("invalid image: {error:?}")); - } - let (ours, theirs) = (normalize(&read(&image, space)), normalize(&read(expected, space))); - if ours != theirs { - return Outcome::Mismatch(first_difference(&theirs, &ours)); - } - if expected == source { - return Outcome::Model; - } - if declared(&image, space) > declared(expected, space) { - return Outcome::Larger; - } - Outcome::Model + let Ok(ops) = lower_page(&before, after) else { + return Outcome::Refused; + }; + let edit = Edit { + at: AT, + ops: ops.iter().map(|op| Op::Page { space, op: op.clone() }).collect(), + }; + if seeded(seed, || section.apply("Author", &edit)).is_err() { + if section.page(space).unwrap() != before { + return Outcome::Mismatch("a refused edit changed the page".into()); } + return Outcome::Refused; } + let mut predicted = before.clone(); + for op in &ops { + model::apply(&mut predicted, op).unwrap(); + } + let live = section.page(space).unwrap(); + if normalize(&predicted) != normalize(&live) { + return Outcome::Mismatch(format!( + "the model predicts otherwise: {}", + first_difference(&normalize(&live), &normalize(&predicted)) + )); + } + let Some(_) = seeded(seed, || section.seal().unwrap()) else { + return if live == before { Outcome::Unchanged } else { + Outcome::Mismatch("the page changed and the seal stored nothing".into()) + }; + }; + let image = section.image(); + let store = Store::parse(&image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + if let Err(error) = index.validate_current() { + return Outcome::Mismatch(format!("invalid image: {error:?}")); + } + let reread = read(&image, space); + if reread != live { + return Outcome::Mismatch(format!("reads back otherwise: {}", first_difference(&live, &reread))); + } + let written = as_written(after, &before, &reread); + let (stored, wanted) = (normalize(&reread), normalize(&written)); + if stored != wanted { + return Outcome::Mismatch(format!("stores another page: {}", first_difference(&wanted, &stored))); + } + Outcome::Stored } fn spans(text: &Paragraph) -> String { @@ -1155,7 +1222,7 @@ pub(super) fn first_difference(expected: &Page, actual: &Page) -> String { /// Every family on every corpus page it applies to, several seeds each. #[test] -fn ops_store_what_the_page_writer_stores() { +fn lowered_edits_store_the_edited_page() { let mut tally: BTreeMap<(Family, String), usize> = BTreeMap::new(); let mut mismatches = Vec::new(); let seeds = std::env::var("OP_SEEDS").map_or(3, |s| s.parse().unwrap()); @@ -1170,7 +1237,7 @@ fn ops_store_what_the_page_writer_stores() { continue; } GUIDS.set(None); - let outcome = differential(source, space, &after, 1 << 40 | seed); + let outcome = stored(source, space, &after, 1 << 40 | seed); let key = match &outcome { Outcome::Mismatch(_) => "Mismatch".to_owned(), other => format!("{other:?}"), @@ -1192,7 +1259,6 @@ fn ops_store_what_the_page_writer_stores() { println!("{mismatch}\n"); } assert!(mismatches.is_empty(), "{} mismatches", mismatches.len()); - assert!(!tally.keys().any(|(_, outcome)| outcome == "Larger")); } #[test] @@ -1674,42 +1740,34 @@ fn random_ops_read_back_as_the_model_predicts() { assert!(applied > refused); } -/// Page creation, moves, indentation and removal through a section store what the image -/// writers store, and the section lists the pages the document does. +/// Page creation, moves, indentation and removal through a section leave the page list +/// they describe, and the sealed image lists the pages the section does. #[test] -fn section_ops_store_what_the_image_writers_store() { +fn section_ops_leave_the_page_list_they_describe() { let sources: Vec> = vec![ crate::create_section("pages.one", "First", "Author").unwrap(), include_bytes!("../../../../corpus/page-lifecycle/04-nested/notebook/Lifecycle.one").to_vec(), SOURCES[3].1.to_vec(), ]; - let (mut bytes, mut total) = (0, 0); + let mut applied = 0; for (n, source) in sources.iter().enumerate() { let arena = Arena::default(); let mut section = Section::open(&arena, source.to_vec()).unwrap(); - let mut image = source.clone(); let mut rng = Rng(n as u64 + 17); for step in 0..12u64 { - let spaces = pages(&image); + let listed = section.pages().unwrap(); + let spaces: Vec = listed.iter().map(|(space, ..)| *space).collect(); + let anchor = spaces.get(rng.pick(spaces.len()).unwrap_or(0)).copied().filter(|_| rng.coin()); let op = match step % 4 { - 0 => SectionOp::Create( - seeded(1 << 50 | step << 20, || { - crate::PageCreation::new( - spaces.get(rng.pick(spaces.len()).unwrap_or(0)).copied().filter(|_| rng.coin()), - Some("Created"), - "Author", - ) - }) - .unwrap(), - ), + 0 => SectionOp::Create(crate::PageCreation::new(anchor, Some("Created"), "Author").unwrap()), 1 => { let Some(space) = spaces.get(rng.pick(spaces.len()).unwrap_or(0)).copied() else { continue }; let level = if spaces[0] == space { 1 } else { 1 + (rng.next() % 2) as u32 }; - SectionOp::Pages(vec![seeded(1 << 51 | step << 20, || crate::PageEdit::set_level(space, level)).unwrap()]) + SectionOp::Pages(vec![crate::PageEdit::set_level(space, level).unwrap()]) } 2 => { let Some(space) = spaces.get(rng.pick(spaces.len()).unwrap_or(0)).copied() else { continue }; - SectionOp::Pages(vec![seeded(1 << 52 | step << 20, || crate::PageEdit::move_to(space, None, 1)).unwrap()]) + SectionOp::Pages(vec![crate::PageEdit::move_to(space, None, 1).unwrap()]) } _ => { if spaces.len() < 2 { @@ -1718,63 +1776,71 @@ fn section_ops_store_what_the_image_writers_store() { SectionOp::Delete(vec![spaces[rng.pick(spaces.len()).unwrap()]]) } }; - let expected = seeded(1 << 40 | step << 16, || { - crate::create::at(AT, || match &op { - SectionOp::Create(creation) => crate::PreparedEdit::create_page(&image, creation), - SectionOp::Pages(edits) => crate::PreparedEdit::pages(&image, edits), - SectionOp::Delete(spaces) => crate::PreparedEdit::delete_pages_permanently(&image, spaces), - SectionOp::Import { .. } => unreachable!(), - }) - .map(|edit| edit.as_bytes().to_vec()) - }); - let applied = seeded(1 << 40 | step << 16, || { - section - .apply("Author", &Edit { at: AT, ops: vec![Op::Section(op.clone())] }) - .map(|()| section.seal().unwrap()) - }); - match (expected, applied) { - (Err(_), Err(_)) => continue, - (Ok(expected), Ok(_)) => { - total += 1; - let written = section.image(); - if written == expected { - bytes += 1; + let edit = Edit { at: AT, ops: vec![Op::Section(op.clone())] }; + if section.apply("Author", &edit).is_err() { + assert_eq!(section.pages().unwrap(), listed, "{n} step {step} {op:?}"); + continue; + } + section.seal().unwrap(); + applied += 1; + let after = section.pages().unwrap(); + let order = |list: &[(ExGuid, String, u32)], without: ExGuid| -> Vec { + list.iter().map(|(space, ..)| *space).filter(|space| *space != without).collect() + }; + match &op { + SectionOp::Create(creation) => { + let at = after.iter().position(|(space, ..)| *space == creation.space()).unwrap(); + assert_eq!(after[at].1, "Created"); + assert_eq!(order(&after, creation.space()), spaces, "{n} step {step}"); + match anchor { + Some(before) => assert_eq!(after[at + 1].0, before), + None => assert_eq!(at + 1, after.len()), + } + } + SectionOp::Pages(edits) => { + let space = edits[0].space(); + let at = after.iter().position(|(listed, ..)| *listed == space).unwrap(); + assert_eq!(after[at].2, edits[0].level(), "{n} step {step} {op:?}"); + if edits[0].position() == crate::PagePosition::Keep { + assert_eq!(order(&after, space), order(&listed, space)); + assert_eq!(after.iter().map(|(s, ..)| *s).collect::>(), spaces); } else { - let store = Store::parse(&written).unwrap(); - RevisionIndex::parse(&store).unwrap().validate_current().unwrap(); - let spaces = pages(&expected); - assert_eq!(pages(&written), spaces, "{n} step {step} {op:?}"); - for space in spaces { - assert_eq!(read(&written, space), read(&expected, space), "{n} step {step}"); - } + assert_eq!(after.last().unwrap().0, space, "{n} step {step}"); } - image = expected; - let store = Store::parse(&image).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let document = Document::parse(&index).unwrap(); - let listed: Vec<(ExGuid, String, u32)> = document - .pages() - .unwrap() - .into_iter() - .map(|(space, page)| { - let (title, level) = Page::heading(document.active(space).unwrap(), page); - (space, title, level) - }) - .collect(); - assert_eq!(section.pages().unwrap(), listed, "{n} step {step}"); } - (expected, applied) => panic!("{n} step {step} {op:?}: {:?} vs {:?}", expected.err(), applied.err()), + SectionOp::Delete(deleted) => { + assert_eq!(after.iter().map(|(s, ..)| *s).collect::>(), order(&listed, deleted[0])); + } + SectionOp::Import { .. } | SectionOp::Conflict { .. } | SectionOp::Color(_) => { + unreachable!() + } + } + let image = section.image(); + let store = Store::parse(&image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + index.validate_current().unwrap(); + let document = Document::parse(&index).unwrap(); + let reread: Vec<(ExGuid, String, u32)> = document + .pages() + .unwrap() + .into_iter() + .map(|(space, page)| { + let (title, level) = Page::heading(document.active(space).unwrap(), page); + (space, title, level) + }) + .collect(); + assert_eq!(reread, after, "{n} step {step}"); + for (space, ..) in &after { + assert_eq!(read(&image, *space), section.page(*space).unwrap(), "{n} step {step}"); } } } - println!("{bytes} of {total} section edits byte-identical"); - assert!(total > 12); + assert!(applied > 12, "{applied} section edits applied"); } -/// Importing a page stores what creating it and writing its copy through the page writer -/// does. +/// An imported page reads back as its copy under the created page's title. #[test] -fn an_imported_page_reads_as_the_page_writer_leaves_it() { +fn an_imported_page_reads_back_as_its_copy() { let target = crate::create_section("import.one", "First", "Author").unwrap(); let mut imported = 0; for (name, source) in SOURCES { @@ -1783,48 +1849,45 @@ fn an_imported_page_reads_as_the_page_writer_leaves_it() { continue; }; let creation = crate::PageCreation::new(None, Some(©.title), "Author").unwrap(); - let created = crate::PreparedEdit::create_page(&target, &creation).unwrap(); - let created = created.as_bytes(); - let mut after = read(created, creation.space()); - after.objects.retain(|object| matches!(object, PageObject::Title(_))); - after.objects.extend( + let arena = Arena::default(); + let mut section = Section::open(&arena, target.clone()).unwrap(); + let import = |section: &mut Section<'_>, op: SectionOp| { + section.apply("Author", &Edit { at: AT, ops: vec![Op::Section(op)] }) + }; + import(&mut section, SectionOp::Create(creation.clone())).unwrap(); + let created = section.page(creation.space()).unwrap(); + let mut expected = created.clone(); + let arena = Arena::default(); + let mut section = Section::open(&arena, target.clone()).unwrap(); + if import(&mut section, SectionOp::Import { creation: creation.clone(), page: copy.clone() }).is_err() { + continue; + } + section.seal().unwrap(); + let image = section.image(); + let store = Store::parse(&image).unwrap(); + RevisionIndex::parse(&store).unwrap().validate_current().unwrap(); + expected.objects.retain(|object| matches!(object, PageObject::Title(_))); + expected.objects.extend( copy.objects .iter() .filter(|object| !matches!(object, PageObject::Title(_))) .cloned(), ); - after.definitions = copy.definitions.clone(); - let expected = crate::create::at(AT, || { - crate::PreparedEdit::page(created, creation.space(), &after, "Author") - }); - let arena = Arena::default(); - let mut section = Section::open(&arena, target.clone()).unwrap(); - let applied = section.apply( - "Author", - &Edit { - at: AT, - ops: vec![Op::Section(SectionOp::Import { - creation: creation.clone(), - page: copy.clone(), - })], - }, - ); - match (expected, applied) { - (Err(_), Err(_)) => continue, - (Ok(expected), Ok(())) => { - section.seal().unwrap(); - let image = section.image(); - let store = Store::parse(&image).unwrap(); - RevisionIndex::parse(&store).unwrap().validate_current().unwrap(); - let (ours, theirs) = ( - normalize(&read(&image, creation.space())), - normalize(&read(expected.as_bytes(), creation.space())), - ); - assert!(ours == theirs, "{name}: {}", first_difference(&theirs, &ours)); - imported += 1; + expected.definitions.extend(copy.definitions.clone()); + let stored = read(&image, creation.space()); + // New page children stack as the writers add them. + let order: Vec = stored.objects.iter().map(PageObject::id).collect(); + expected.objects.sort_by_key(|object| order.iter().position(|id| *id == object.id())); + let mut expected = as_written(&expected, &created, &stored); + // An outline's height follows its content; no op carries it. + for (object, stored) in expected.objects.iter_mut().zip(&stored.objects) { + if let (PageObject::Outline(outline), PageObject::Outline(stored)) = (object, stored) { + outline.layout.max_height = stored.layout.max_height; } - (expected, applied) => panic!("{name}: {:?} vs {:?}", expected.err(), applied.err()), } + let (ours, theirs) = (normalize(&stored), normalize(&expected)); + assert!(ours == theirs, "{name}: {}", first_difference(&theirs, &ours)); + imported += 1; } } assert!(imported > 10, "{imported} pages imported"); diff --git a/crates/onestore/src/outline.rs b/crates/onestore/src/outline.rs index 4f28658702161656c4bca2d135b7c5a88b66eccd..8486e6badb744a9c26e321c0a9e7ba6e8a4f8d1e 100644 --- a/crates/onestore/src/outline.rs +++ b/crates/onestore/src/outline.rs @@ -25,15 +25,6 @@ pub enum OutlineEdit { } impl OutlineEdit { - pub(crate) fn apply( - self, - source: &[u8], - space: ExGuid, - object: ExGuid, - ) -> Result, Error> { - crate::active::write(source, space, |active| self.changes(active, object)) - } - pub(crate) fn changes(self, active: &ActivePage<'_>, object: ExGuid) -> Result { let invalid = |message| Error { offset: 0, message }; let values = match self { @@ -146,8 +137,12 @@ impl OutlineEdit { #[cfg(test)] mod tests { use super::*; - use crate::write::write_revision; - use crate::{RevisionIndex, Store, document::Document}; + use crate::{ + RevisionIndex, Store, + document::Document, + op::{PageOp, tests::edited}, + write::write_revision, + }; #[test] fn protection_on_the_target_or_ancestor_prevents_layout_edits() { @@ -184,7 +179,7 @@ mod tests { (paragraph, OutlineEdit::Collapsed(true)), ] { assert_eq!( - edit.apply(&bytes, sid, object).is_err(), + edited(&bytes, sid, vec![PageOp::Outline { object, edit }]).is_err(), enabled, "{target} {property:x} {edit:?}" ); @@ -199,10 +194,10 @@ mod tests { Ok(BTreeMap::from([(outline, object)])) }) .unwrap(); - assert!( - OutlineEdit::Collapsed(true) - .apply(&duplicated, sid, paragraph) - .is_err() - ); + let collapse = PageOp::Outline { + object: paragraph, + edit: OutlineEdit::Collapsed(true), + }; + assert!(edited(&duplicated, sid, vec![collapse]).is_err()); } } diff --git a/crates/onestore/src/page/mod.rs b/crates/onestore/src/page/mod.rs index a7e3d827eb5965b6d4afc77774ac1189629cef8e..2d7df2e8224223a168f389bc5994c7e846dd5c48 100644 --- a/crates/onestore/src/page/mod.rs +++ b/crates/onestore/src/page/mod.rs @@ -17,7 +17,6 @@ pub mod text; pub use ink::{Ink, InkStroke}; pub use math::Math; -pub(crate) mod write; /// Picture and attachment payloads travel with the model (queued intents replay them), /// as base64 text. @@ -52,6 +51,10 @@ mod payload { } pub use text::Paragraph; +/// The page node's colour (View, Page Color) as COLORREF, absent for "No color"; not in +/// MS-ONE, observed in OneNote 2010's pages (`evidence/notebook-management`). +pub(crate) const PAGE_COLOR: u32 = 0x14001d2a; + /// The role of a title-outline paragraph that displays the page's creation date or time. #[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub enum DateField { @@ -67,6 +70,9 @@ pub struct Page { /// FILETIME ticks from the page's TopologyCreationTimeStamp. pub created: Option, pub margin_origin: [f32; 2], + /// The page's colour (View, Page Color), COLORREF; `None` is OneNote's "No color". + #[serde(default)] + pub color: Option, pub objects: Vec, pub definitions: BTreeMap, } @@ -394,9 +400,14 @@ impl Page { /// links keep naming the original. Content outside the model has no copy, so an /// `Unsupported` object is an error. pub fn copy(&self) -> Result { + self.copy_with(&mut []) + } + + /// `copy`, renaming `ids`, identities on this page, to theirs on the copy. + pub fn copy_with(&self, ids: &mut [ExGuid]) -> Result { let invalid = |message| Error { offset: 0, message }; - let mut value = - serde_json::to_value(self).map_err(|_| invalid("Page is not serializable"))?; + let mut value = serde_json::to_value((self, &*ids)) + .map_err(|_| invalid("Page is not serializable"))?; let mut fresh: BTreeMap = BTreeMap::new(); fn walk( value: &mut serde_json::Value, @@ -444,8 +455,9 @@ impl Page { Ok(()) } walk(&mut value, &mut fresh)?; - let mut copy: Self = + let (mut copy, renamed): (Self, Vec) = serde_json::from_value(value).map_err(|_| invalid("Page copy does not deserialize"))?; + ids.copy_from_slice(&renamed); copy.identity = Some(crate::write::fresh_guid()?); // A paragraph's indent level follows its parent chain; a level beyond that comes // from an outline group, which the model does not hold. @@ -477,6 +489,28 @@ impl Page { Ok(copy) } + /// The date and time the title's date fields show, when it has both. + pub fn date_text(&self) -> Option<[String; 2]> { + let title = self.objects.iter().find_map(|object| match object { + PageObject::Title(title) => Some(title), + _ => None, + })?; + let outline = title.outlines.iter().find(|outline| Some(outline.id) == title.date)?; + let [first, second] = &outline.paragraphs[..] else { + return None; + }; + let [first, second] = [first.text()?, second.text()?]; + let text = |object: &TextObject| object.text.text().to_owned(); + // OneNote writes the date first; a field marked as the other role says otherwise. + Some( + if first.date_field == Some(DateField::Time) || second.date_field == Some(DateField::Date) { + [text(second), text(first)] + } else { + [text(first), text(second)] + }, + ) + } + /// The notebook-management identity of the page in an active page revision, which /// internal links name. pub fn identity_of(revision: &Revision<'_>) -> Option<[u8; 16]> { @@ -562,6 +596,17 @@ impl Page { margin_origin_x.unwrap_or(1.0_f32 * 36.0), margin_origin_y.unwrap_or(0.4_f32 * 36.0), ], + color: root + .extra + .first() + .and_then(|fields| fields.iter().find(|field| field.id == PAGE_COLOR)) + .map(|field| match field.value { + FieldValue::Bytes(&[red, green, blue, alpha]) => { + Ok(u32::from_le_bytes([red, green, blue, alpha])) + } + _ => Err(invalid("The page colour is damaged")), + }) + .transpose()?, objects: Vec::new(), definitions: BTreeMap::new(), }; diff --git a/crates/onestore/src/page/write.rs b/crates/onestore/src/page/write.rs deleted file mode 100644 index edc1011d82cc4fe8892b6ae0a304ad74c389f8fc..0000000000000000000000000000000000000000 --- a/crates/onestore/src/page/write.rs +++ /dev/null @@ -1,1646 +0,0 @@ -//! Publishes an edited page model by lowering the difference from the stored page onto -//! the typed writers, whose revisions accumulate in memory, then squashing them into one -//! revision per space. `PreparedEdit::page` keeps it until the op path replaces it. - -use super::{Image, Ink, Outline, Page, PageObject, PageParagraph, ParagraphContent}; -use crate::{ - Error, ExGuid, Insertion, ObjectData, OutlineEdit, ParagraphJoin, ParagraphSplit, PropertySets, - RevisionIndex, Store, TreeEdit, Value, - active::{ActivePage, Changes}, - document::Kind, - op::{ - content::{self, AttachmentIds}, - levels, - lower::{View, collect_containers, kept_set, text_edit, validate}, - properties, - table::{self, Structure}, - }, - write::{PropertyObject, RevisionEdit}, -}; -use bumpalo::Bump; -use std::collections::{BTreeMap, BTreeSet}; - -fn invalid(message: &'static str) -> Error { - Error { offset: 0, message } -} - -fn page_images(page: &Page) -> impl Iterator { - page.objects.iter().filter_map(|object| match object { - PageObject::Image(image) => Some((image.id, image)), - _ => None, - }) -} - -fn page_ink(page: &Page) -> impl Iterator { - page.objects.iter().filter_map(|object| match object { - PageObject::Ink(ink) => Some((ink.id, ink)), - _ => None, - }) -} - -pub(crate) fn write_page( - source: &[u8], - space: ExGuid, - after: &Page, - author: &str, -) -> Result, Error> { - if author.contains('\0') { - return Err(invalid("Choose an author name without NUL")); - } - let store = Store::parse(source)?; - let index = RevisionIndex::parse(&store)?; - let arena = Bump::new(); - let active = ActivePage::parse(&index, space)?; - let [page] = active.pages[..] else { - return Err(invalid("Choose an object space containing one active page")); - }; - let before = Page::from_revision(&active.view, page)?; - let mut lowering = Lowering { - active, - arena: &arena, - current: Some(before.clone()), - edited: false, - page, - author, - alias: BTreeMap::new(), - built: BTreeSet::new(), - }; - lowering.run(&before, after)?; - if !lowering.edited { - return Ok(source.to_vec()); - } - let active = &lowering.active; - squash( - &index, - &[(space, &active.live.revision)], - &active.payloads, - &lowering.alias, - None, - ) -} - -struct Lowering<'a> { - active: ActivePage<'a>, - /// Holds the objects the typed writers store for as long as `active` reads them. - arena: &'a Bump, - /// The page as `active` stores it, until a writer changes it. - current: Option, - /// Whether a writer stored anything. - edited: bool, - page: ExGuid, - author: &'a str, - /// Model identities of new objects mapped to the identities the typed writers allocated. - alias: BTreeMap, - /// Tables whose structure this run has written. - built: BTreeSet, -} - -impl<'a> Lowering<'a> { - fn id(&self, model: ExGuid) -> ExGuid { - self.alias.get(&model).copied().unwrap_or(model) - } - - /// A writer identity for a new object; squash renames it to the model's. - fn allocate(&mut self, model: ExGuid) -> Result { - let written = ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }; - self.alias.insert(model, written); - Ok(written) - } - - /// Stores the next revision a typed writer computes from the page as stored so far. - fn write( - &mut self, - changes: impl FnOnce(&ActivePage<'a>) -> Result, - ) -> Result<(), Error> { - self.write_with(&[], changes) - } - - fn write_with( - &mut self, - payloads: &[([u8; 16], &[u8])], - changes: impl FnOnce(&ActivePage<'a>) -> Result, - ) -> Result<(), Error> { - let changes = changes(&self.active)?; - if self.active.write(self.arena, payloads, changes)? { - self.current = None; - self.edited = true; - } - Ok(()) - } - - fn current(&mut self) -> Result { - if self.current.is_none() { - self.current = Some(Page::from_revision(&self.active.view, self.page)?); - } - Ok(self.current.clone().unwrap()) - } - - fn run(&mut self, before: &Page, after: &Page) -> Result<(), Error> { - let old = View::new(before, false)?; - let new = View::new(after, false)?; - validate(before, after, &old, &new)?; - if after.created != before.created { - return Err(invalid("Page creation time cannot be edited")); - } - for id in new.outlines.keys().chain(new.paragraphs.keys()) { - if !old.outlines.contains_key(id) - && !old.paragraphs.contains_key(id) - && self.active.live.revision.objects.contains_key(id) - { - return Err(invalid( - "A new model identity already exists in the section", - )); - } - } - let mut placed: BTreeMap> = BTreeMap::new(); - for (container, children) in &old.children { - placed.insert(*container, children.clone()); - } - let mut page_order: Vec = old - .page_children - .iter() - .copied() - .filter(|id| !old.outlines.contains_key(id) || new.outlines.contains_key(id)) - .collect(); - self.insert_outlines(&old, &new, &mut placed, &mut page_order)?; - let mut consumed = BTreeSet::new(); - self.ungroup(&old, &new)?; - self.split_and_join(&old, &new, &mut placed, &mut consumed)?; - self.place(&old, &new, &placed, &page_order)?; - self.delete(&old, &new, &consumed)?; - self.edit_levels(&new)?; - self.edit_text_identities(&new)?; - self.edit_equations(&new)?; - self.edit_text(&new)?; - self.edit_paragraph_styles(&old, &new, after)?; - self.edit_lists(after, &new)?; - self.edit_tags(after, &new)?; - self.edit_paragraph_formatting(&new)?; - self.edit_formatting(&new)?; - self.edit_layout(&old, &new)?; - Ok(()) - } - - fn insert_outlines( - &mut self, - old: &View<'_>, - new: &View<'_>, - placed: &mut BTreeMap>, - page_order: &mut Vec, - ) -> Result<(), Error> { - for id in &new.page_children { - let Some(outline) = new.outlines.get(id) else { - continue; - }; - if old.outlines.contains_key(id) { - continue; - } - if new.title_outlines.contains(id) { - return Err(invalid("Title outlines cannot be added")); - } - let (Some(x), Some(y)) = (outline.layout.x, outline.layout.y) else { - return Err(invalid("A new outline needs a position")); - }; - let Some(first) = new.children[id].first().copied() else { - return Err(invalid("A new outline needs a paragraph")); - }; - // A new outline starts as one text paragraph; other content replaces it in - // its own pass, as for a paragraph inserted into an existing outline. - let (text, text_id) = match &new.paragraphs[&first].content { - ParagraphContent::Text(text) if text.date_field.is_none() => { - (text.text.text(), Some(text.id)) - } - ParagraphContent::Table(_) - | ParagraphContent::Image(_) - | ParagraphContent::Attachment(_) - | ParagraphContent::Ink(_) => ("", None), - _ => { - return Err(invalid( - "A new outline starts with a paragraph the writer builds", - )); - } - }; - let insertion = Insertion::outline(self.page, x, y, text, self.author)?; - self.write(|active| insertion.changes(active))?; - let outline_id = insertion.object(); - self.alias.insert(*id, outline_id); - self.alias.insert( - first, - ExGuid { - guid: outline_id.guid, - n: 3, - }, - ); - if let Some(text_id) = text_id { - self.alias.insert(text_id, insertion.text_object()); - } - placed.insert(*id, vec![first]); - placed.insert(first, Vec::new()); - page_order.push(*id); - } - Ok(()) - } - - /// Splits and joins stored paragraphs as OneNote's Enter, Backspace and Delete do. A new - /// styled paragraph that is no exact split starts as Enter at the end of the nearest stored - /// paragraph that allows one, taking its properties as OneNote's new paragraphs do; later - /// passes place it and give it its text. - fn split_and_join( - &mut self, - old: &View<'_>, - new: &View<'_>, - placed: &mut BTreeMap>, - consumed: &mut BTreeSet, - ) -> Result<(), Error> { - // A new paragraph may be split from the stored paragraph nearest before it, whatever - // placement or pasted paragraphs the same save adds between them. - let mut splits = Vec::new(); - for document in new.documents() { - let mut left = None; - for paragraph in document { - if old.paragraphs.contains_key(¶graph.id) { - left = paragraph.text().is_some().then_some(paragraph.id); - } else if let Some(left) = left { - let container = new.container[¶graph.id]; - let at = new.children[&container] - .iter() - .position(|id| *id == paragraph.id); - splits.push(((container, at), left, paragraph.id)); - } - } - } - splits.sort(); - let mut split = BTreeSet::new(); - for (_, left, right) in splits { - let (Some(previous), Some(after), Some(next)) = - (old.text(left), new.text(left), new.text(right)) - else { - continue; - }; - if split.contains(&left) - || after.id != previous.id - || new.paragraphs[&right].style != old.paragraphs[&left].style - { - continue; - } - let Ok(offset) = after.text.utf16_offset(after.text.text().len()) else { - continue; - }; - // Splitting at the end only differs from appending a paragraph by the copied - // paragraph style; without one, an appended empty paragraph is an insertion. - if offset == previous.text.utf16_offset(previous.text.text().len())? - && new.paragraphs[&right].style.is_none() - { - continue; - } - let Ok(head) = previous.text.slice(0..offset) else { - continue; - }; - let Ok(tail) = previous.text.slice( - offset - ..previous - .text - .utf16_offset(previous.text.text().len()) - .unwrap_or(0), - ) else { - continue; - }; - // An emptied side takes the writer's insertion style, so only its text must agree. - let same = |expected: &super::Paragraph, actual: &super::Paragraph| { - expected.text() == actual.text() - && (expected.text().is_empty() || expected == actual) - }; - if !same(&head, &after.text) || !same(&tail, &next.text) { - continue; - } - let edit = ParagraphSplit::new(previous.id, offset, self.author)?; - if self.split(&edit, left, right, old, new, placed)? { - split.insert(left); - } - } - for document in new.documents() { - for (at, paragraph) in document.iter().enumerate() { - if old.paragraphs.contains_key(¶graph.id) - || self.alias.contains_key(¶graph.id) - || paragraph.style.is_none() - || paragraph.text().is_none() - { - continue; - } - let stored = |candidate: &&&PageParagraph| { - old.paragraphs.contains_key(&candidate.id) && candidate.text().is_some() - }; - let before = document[..at].iter().rev().filter(stored); - for template in before.chain(document[at + 1..].iter().filter(stored)) { - let text = self.active.view.nodes[&template.id].content[0]; - let Kind::RichText { text: content, .. } = &self.active.view.nodes[&text].kind - else { - continue; - }; - let end = u32::try_from(content.encode_utf16().count()) - .map_err(|_| invalid("Paragraph exceeds the UTF-16 offset range"))?; - let edit = ParagraphSplit::new(text, end, self.author)?; - if self.split(&edit, template.id, paragraph.id, old, new, placed)? { - break; - } - } - } - } - for (container, children) in &old.children { - if !new.children.contains_key(container) { - continue; - } - for pair in children.windows(2) { - let [left, right] = [pair[0], pair[1]]; - let (Some(previous), Some(after), Some(removed)) = - (old.text(left), new.text(left), old.text(right)) - else { - continue; - }; - if new.paragraphs.contains_key(&right) - || !old.children[&left].is_empty() - || !old.children[&right].is_empty() - || removed.text.text().is_empty() - { - continue; - } - let mut joined = previous.text.clone(); - if joined.append(removed.text.clone()).is_err() || joined != after.text { - continue; - } - let adopts_right = previous.text.text().is_empty(); - if after.id - != if adopts_right { - removed.id - } else { - previous.id - } - { - continue; - } - let join = ParagraphJoin::new(previous.id, removed.id, self.author)?; - let Ok(changes) = join.changes(&self.active) else { - continue; - }; - self.write(|_| Ok(changes))?; - consumed.insert(right); - placed.get_mut(container).unwrap().retain(|id| *id != right); - placed.remove(&right); - } - } - Ok(()) - } - - /// Stores `edit` of model paragraph `left` as model paragraph `right`, unless the split - /// cannot be made there. - fn split( - &mut self, - edit: &ParagraphSplit, - left: ExGuid, - right: ExGuid, - old: &View<'_>, - new: &View<'_>, - placed: &mut BTreeMap>, - ) -> Result { - let Ok(changes) = edit.changes(&self.active) else { - return Ok(false); - }; - self.write(|_| Ok(changes))?; - self.alias.insert(right, edit.object()); - // A text object a join moved into the paragraph is taken over after placement. - if let Some(text) = new.text(right) - && !self.active.live.revision.objects.contains_key(&text.id) - { - self.alias.insert(text.id, edit.text_object()); - } - // The split copies the left paragraph's list nodes in order; a list the model shares - // with a stored paragraph stays that paragraph's. - if new.paragraphs[&right].lists.len() == old.paragraphs[&left].lists.len() { - for (n, list) in (5..).zip(&new.paragraphs[&right].lists) { - if !self.active.live.revision.objects.contains_key(list) { - let guid = edit.object().guid; - self.alias.insert(*list, ExGuid { guid, n }); - } - } - } - // An earlier split may have carried the left paragraph to its tail. - let list = placed - .values_mut() - .find(|list| list.contains(&left)) - .unwrap(); - let at = list.iter().position(|id| *id == left).unwrap(); - list.insert(at + 1, right); - placed.insert(right, placed[&left].clone()); - placed.insert(left, Vec::new()); - Ok(true) - } - - fn place( - &mut self, - old: &View<'_>, - new: &View<'_>, - placed: &BTreeMap>, - page_order: &[ExGuid], - ) -> Result<(), Error> { - let title = |page: &Page, id: ExGuid| { - page.objects - .iter() - .any(|object| matches!(object, PageObject::Title(title) if title.id == id)) - }; - self.edit_page_images(old, new)?; - self.edit_page_ink(old, new)?; - let survivors: Vec = page_order - .iter() - .copied() - .filter(|id| { - (new.outlines.contains_key(id) || !old.outlines.contains_key(id)) - && !title(old.page, *id) - }) - .collect(); - let after: Vec = new - .page_children - .iter() - .copied() - .filter(|id| !title(new.page, *id)) - .collect(); - let kept = kept_set(&survivors, &after, |id| { - new.outlines.contains_key(&id) && !new.title_outlines.contains(&id) - })?; - let mut next = None; - for id in after.iter().rev() { - if !kept.contains(id) { - let edit = TreeEdit::move_to( - self.id(*id), - self.page, - next.map(|n| self.id(n)), - self.author, - )?; - self.write(|active| edit.changes(active))?; - } - next = Some(*id); - } - let mut containers: Vec = Vec::new(); - for object in &new.page.objects { - let outlines: Vec<&Outline> = match object { - PageObject::Outline(outline) => vec![outline], - PageObject::Title(title) => title.outlines.iter().collect(), - PageObject::Image(_) | PageObject::Ink(_) | PageObject::Unsupported(_) => { - Vec::new() - } - }; - for outline in outlines { - containers.push(outline.id); - collect_containers(&outline.paragraphs, &mut containers); - } - } - // Cells that do not exist yet, and containers inside them, receive their - // paragraphs once the table structure does. - let new_cell = |id: &ExGuid| { - !(old.children.contains_key(id) - || new.paragraphs.contains_key(id) - || new.outlines.contains_key(id)) - }; - let (existing, deferred): (Vec, Vec) = - containers.into_iter().partition(|container| { - let mut at = *container; - loop { - if new_cell(&at) { - return false; - } - match new.container.get(&at) { - Some(parent) => at = *parent, - None => return true, - } - } - }); - self.place_containers(old, new, placed, &existing)?; - // Each round builds the tables whose holders exist and fills their cells, which - // may hold further new tables. - let mut pending = deferred; - loop { - self.edit_table_structure(old, new)?; - if pending.is_empty() { - break; - } - let (ready, waiting): (Vec, Vec) = - pending.into_iter().partition(|container| { - self.alias.contains_key(container) || old.children.contains_key(container) - }); - if ready.is_empty() { - return Err(invalid("Table content has no table to hold it")); - } - self.place_containers(old, new, placed, &ready)?; - pending = waiting; - } - self.edit_cells(old, new)?; - self.edit_images(old, new)?; - self.edit_ink_paragraphs(old, new)?; - self.edit_attachments(old, new)?; - Ok(()) - } - - /// Gives each new attachment paragraph what OneNote stores for an inserted file: the - /// payload embedded in the file-data store, an embedded-file container declaring it, - /// and an attachment object naming the file that the paragraph holds as content. - fn edit_attachments(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - for (paragraph_id, paragraph) in &new.paragraphs { - let ParagraphContent::Attachment(attachment) = ¶graph.content else { - continue; - }; - if let Some(previous) = old.paragraphs.get(paragraph_id) { - let ParagraphContent::Attachment(stored) = &previous.content else { - return Err(invalid("Paragraph content type cannot change")); - }; - if stored != attachment { - self.write(|active| content::attachment_edit_changes(active, stored, attachment))?; - } - continue; - } - if attachment.recording.is_some() { - return Err(invalid("Recordings are captured by OneNote, not inserted")); - } - let Some(bytes) = &attachment.bytes else { - return Err(invalid("A new attachment needs its payload")); - }; - let ids = AttachmentIds { - object: self.allocate(attachment.id)?, - file: ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }, - payload: crate::write::fresh_guid()?, - preview: match &attachment.preview { - Some(_) => { - let payload = crate::write::fresh_guid()?; - let icon = ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }; - Some((payload, icon)) - } - None => None, - }, - }; - let mut payloads: Vec<([u8; 16], &[u8])> = vec![(ids.payload, bytes)]; - if let (Some((payload, _)), Some(icon)) = (ids.preview, &attachment.preview) { - payloads.push((payload, icon)); - } - let holder = self.id(*paragraph_id); - self.write_with(&payloads, |active| { - content::attachment_changes(active, attachment, &ids, holder) - })?; - } - Ok(()) - } - - /// Gives each new picture paragraph what OneNote stores for an inserted picture: the - /// payload embedded in the section's file-data store, a file-data object declaring it - /// by identity and extension, and a picture object the paragraph holds as content. - fn edit_images(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - for (paragraph_id, paragraph) in &new.paragraphs { - let ParagraphContent::Image(image) = ¶graph.content else { - continue; - }; - if let Some(previous) = old.paragraphs.get(paragraph_id) { - let ParagraphContent::Image(stored) = &previous.content else { - return Err(invalid("Paragraph content type cannot change")); - }; - if stored != image { - self.edit_image(stored, image)?; - } - continue; - } - if image.layout.x.is_some() || image.layout.y.is_some() { - return Err(invalid("A paragraph picture has no position of its own")); - } - let holder = self.id(*paragraph_id); - self.insert_image(image, Some(holder))?; - } - Ok(()) - } - - /// Page-level pictures are direct page children, as OneNote stores a picture placed - /// outside any outline: new ones are appended for the placement pass to order, changed - /// ones are moved, resized or described, and removed ones are deleted in the delete pass. - fn edit_page_images(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - let stored: BTreeMap = page_images(old.page).collect(); - for (id, image) in page_images(new.page) { - match stored.get(&id) { - Some(previous) => { - if *previous != image { - self.edit_image(previous, image)?; - } - } - None => { - if image.layout.x.is_none() || image.layout.y.is_none() { - return Err(invalid("A new page-level picture needs a position")); - } - self.insert_image(image, None)?; - } - } - } - Ok(()) - } - - /// Ink placed on the page: new drawings are appended for the placement pass to order, - /// changed ones have their stroke list rewritten, removed ones go in the delete pass. - fn edit_page_ink(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - let stored: BTreeMap = page_ink(old.page).collect(); - for (id, ink) in page_ink(new.page) { - match stored.get(&id) { - Some(previous) => { - if *previous != ink { - self.edit_ink(previous, ink)?; - } - } - None => self.insert_ink(ink, None)?, - } - } - Ok(()) - } - - fn edit_ink_paragraphs(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - for (paragraph_id, paragraph) in &new.paragraphs { - let ParagraphContent::Ink(ink) = ¶graph.content else { - continue; - }; - match old.paragraphs.get(paragraph_id) { - Some(previous) => { - let ParagraphContent::Ink(stored) = &previous.content else { - return Err(invalid("Paragraph content type cannot change")); - }; - if stored != ink { - self.edit_ink(stored, ink)?; - } - } - None => { - let holder = self.id(*paragraph_id); - self.insert_ink(ink, Some(holder))?; - } - } - } - Ok(()) - } - - /// Writes new ink the way OneNote 2010 stores a drawing: a container the page lists as a - /// child (or a paragraph holds as content), its data node listing stroke objects, each - /// stroke's packet and half-inch origin, and one drawing-attribute object per distinct pen. - fn insert_ink(&mut self, ink: &Ink, holder: Option) -> Result<(), Error> { - let container = self.allocate(ink.id)?; - let data = ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }; - let mut strokes = Vec::new(); - for stroke in &ink.strokes { - strokes.push((self.allocate(stroke.id)?, stroke)); - } - self.write(|active| content::ink_changes(active, ink, container, data, &strokes, holder)) - } - - /// Rewrites a drawing's stroke list: stored strokes stay as they are (OneNote erases whole - /// strokes rather than editing them), removed ones leave the list, new ones are created. - fn edit_ink(&mut self, stored: &Ink, ink: &Ink) -> Result<(), Error> { - if stored.id != ink.id { - return Err(invalid("Ink identity cannot change")); - } - if stored.layout != ink.layout || stored.groups != ink.groups { - return Err(invalid("Ink position and groups stay as stored")); - } - let mut kept = Vec::new(); - let mut added = Vec::new(); - for stroke in &ink.strokes { - match stored.strokes.iter().find(|s| s.id == stroke.id) { - Some(previous) if previous == stroke => kept.push(self.id(stroke.id)), - Some(_) => return Err(invalid("A stored stroke keeps its path and pen")), - None => added.push((self.allocate(stroke.id)?, stroke)), - } - } - let container = self.id(ink.id); - self.write(|active| content::strokes_changes(active, container, &kept, &added)) - } - - /// Gives a new picture what OneNote stores for an inserted one: the payload embedded - /// in the section's file-data store, a file-data object declaring it by identity and - /// extension, and a picture object that a paragraph holds as content or the page - /// lists as a child. - fn insert_image(&mut self, image: &Image, holder: Option) -> Result<(), Error> { - let Some(bytes) = &image.bytes else { - return Err(invalid("A new picture needs its payload")); - }; - let image_id = self.allocate(image.id)?; - let file_id = ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }; - let payload_guid = crate::write::fresh_guid()?; - let payload: &[u8] = bytes; - self.write_with(&[(payload_guid, payload)], |active| { - content::picture_changes(active, image, image_id, file_id, payload_guid, holder) - }) - } - - fn edit_image(&mut self, stored: &Image, image: &Image) -> Result<(), Error> { - content::picture_fixed_fields(stored, image)?; - let object = self.id(image.id); - self.write(|active| { - content::picture_edit_changes( - active, - object, - (&stored.layout, &stored.alt), - &image.layout, - &image.alt, - ) - }) - } - - fn place_containers( - &mut self, - old: &View<'_>, - new: &View<'_>, - placed: &BTreeMap>, - containers: &[ExGuid], - ) -> Result<(), Error> { - for container in containers { - let after = &new.children[container]; - let current: Vec = placed - .get(container) - .map(|list| { - list.iter() - .copied() - .filter(|id| new.paragraphs.contains_key(id)) - .collect() - }) - .unwrap_or_default(); - let kept = kept_set(¤t, after, |_| true)?; - let mut next = None; - for id in after.iter().rev() { - let anchor = next.map(|n| self.id(n)); - if !old.paragraphs.contains_key(id) && !self.alias.contains_key(id) { - let paragraph = new.paragraphs[id]; - // A styled paragraph appended to a stored outline is a split whose - // precondition failed under a merge; it stays for review. A new outline - // takes styled paragraphs as built (a copied page). - let mut root = *container; - while let Some(parent) = new.container.get(&root) { - root = *parent; - } - if paragraph.style.is_some() && old.outlines.contains_key(&root) { - return Err(invalid( - "New paragraphs in a stored outline contain plain text without styles", - )); - } - if paragraph.media != Default::default() { - return Err(invalid( - "Recording annotations are made by OneNote while it records", - )); - } - // A new table starts as an empty text paragraph whose content the - // structure pass replaces with the table. - let (text, text_id) = match ¶graph.content { - ParagraphContent::Text(text) if text.date_field.is_none() => { - (text.text.text(), Some(text.id)) - } - ParagraphContent::Table(_) - | ParagraphContent::Image(_) - | ParagraphContent::Attachment(_) - | ParagraphContent::Ink(_) => ("", None), - _ => { - return Err(invalid( - "New paragraphs contain plain text without fields or styles", - )); - } - }; - let insertion = - Insertion::paragraph(self.id(*container), anchor, text, self.author)?; - self.write(|active| insertion.changes(active))?; - self.alias.insert(*id, insertion.object()); - if let Some(text_id) = - text_id.filter(|id| !self.active.live.revision.objects.contains_key(id)) - { - self.alias.insert(text_id, insertion.text_object()); - } - } else if !kept.contains(id) { - let edit = - TreeEdit::move_to(self.id(*id), self.id(*container), anchor, self.author)?; - self.write(|active| edit.changes(active))?; - } - next = Some(*id); - } - } - Ok(()) - } - - /// Changes the shading and indents of stored cells in place. - fn edit_cells(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - let stored: BTreeMap = old - .paragraphs - .values() - .filter_map(|paragraph| match ¶graph.content { - ParagraphContent::Table(table) => Some(table), - _ => None, - }) - .flat_map(|table| table.rows.iter().flat_map(|row| &row.cells)) - .map(|cell| (cell.id, cell)) - .collect(); - for paragraph in new.paragraphs.values() { - let ParagraphContent::Table(table) = ¶graph.content else { - continue; - }; - for cell in table.rows.iter().flat_map(|row| &row.cells) { - let Some(previous) = stored.get(&cell.id) else { - continue; - }; - if (cell.shading, &cell.indents) == (previous.shading, &previous.indents) { - continue; - } - let object = self.id(cell.id); - self.write(|active| { - table::cell_changes( - active, - object, - (previous.shading, &previous.indents), - cell.shading, - &cell.indents, - ) - })?; - } - } - Ok(()) - } - - /// Creates tables, rows and cells the model added, rebuilds every table's row and cell - /// order to the model's, and writes the column widths, locks and border flag. - fn edit_table_structure(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - let old_tables: BTreeMap = old - .paragraphs - .values() - .filter_map(|p| match &p.content { - ParagraphContent::Table(table) => Some((table.id, table)), - _ => None, - }) - .collect(); - for (paragraph_id, paragraph) in &new.paragraphs { - let ParagraphContent::Table(table) = ¶graph.content else { - continue; - }; - let previous = old_tables.get(&table.id).copied(); - // A table is built once its holder is placed, and only once. - if !(old.paragraphs.contains_key(paragraph_id) || self.alias.contains_key(paragraph_id)) - || self.built.contains(&table.id) - { - continue; - } - self.built.insert(table.id); - let unchanged = previous.is_some_and(|previous| { - previous.columns == table.columns - && previous.borders == table.borders - && previous.rows.len() == table.rows.len() - && previous.rows.iter().zip(&table.rows).all(|(a, b)| { - a.id == b.id - && a.cells.len() == b.cells.len() - && a.cells.iter().zip(&b.cells).all(|(x, y)| x.id == y.id) - }) - }); - if unchanged { - continue; - } - let table_id = if previous.is_some() { - self.id(table.id) - } else { - self.allocate(table.id)? - }; - let existing_rows: BTreeSet = previous - .map(|p| p.rows.iter().map(|r| r.id).collect()) - .unwrap_or_default(); - let existing_cells: BTreeSet = previous - .map(|p| { - p.rows - .iter() - .flat_map(|r| r.cells.iter().map(|c| c.id)) - .collect() - }) - .unwrap_or_default(); - let template = previous - .and_then(|p| p.rows.first()) - .and_then(|r| r.cells.first()) - .map(|cell| cell.indents.as_slice()); - let mut structure = Structure { - rows: Vec::new(), - new_rows: BTreeSet::new(), - new_cells: BTreeMap::new(), - columns: &table.columns, - borders: table.borders, - }; - for row in &table.rows { - let row_id = if existing_rows.contains(&row.id) { - self.id(row.id) - } else { - let written = self.allocate(row.id)?; - structure.new_rows.insert(written); - written - }; - let mut cells = Vec::new(); - for cell in &row.cells { - let cell_id = if existing_cells.contains(&cell.id) { - self.id(cell.id) - } else { - let written = self.allocate(cell.id)?; - structure.new_cells.insert( - written, - (table::cell_indents(&cell.indents, template), cell.shading), - ); - written - }; - cells.push(cell_id); - } - structure.rows.push((row_id, cells)); - } - let holder = previous.is_none().then(|| self.id(*paragraph_id)); - self.write(|active| table::table_changes(active, table_id, holder, &structure))?; - } - Ok(()) - } - - fn delete( - &mut self, - old: &View<'_>, - new: &View<'_>, - consumed: &BTreeSet, - ) -> Result<(), Error> { - let removed_outline = - |id: ExGuid| old.outlines.contains_key(&id) && !new.outlines.contains_key(&id); - for id in old.paragraphs.keys() { - if new.paragraphs.contains_key(id) || consumed.contains(id) { - continue; - } - let mut ancestor = old.container[id]; - let mut covered = false; - // A surviving ancestor is placed where the model says, carrying this paragraph along. - while !new.paragraphs.contains_key(&ancestor) { - if removed_outline(ancestor) - || (old.paragraphs.contains_key(&ancestor) && !consumed.contains(&ancestor)) - || (old.children.contains_key(&ancestor) - && !old.paragraphs.contains_key(&ancestor) - && !old.outlines.contains_key(&ancestor) - && !new.children.contains_key(&ancestor)) - { - covered = true; - break; - } - match old.container.get(&ancestor) { - Some(parent) => ancestor = *parent, - None => break, - } - } - if covered { - continue; - } - let edit = TreeEdit::delete(*id, self.author)?; - self.write(|active| edit.changes(active))?; - } - let kept: BTreeSet = page_images(new.page) - .map(|(id, _)| id) - .chain(page_ink(new.page).map(|(id, _)| id)) - .collect(); - for id in page_images(old.page) - .map(|(id, _)| id) - .chain(page_ink(old.page).map(|(id, _)| id)) - { - if !kept.contains(&id) { - let edit = TreeEdit::delete(id, self.author)?; - self.write(|active| edit.changes(active))?; - } - } - for id in old.outlines.keys() { - if removed_outline(*id) { - if old.title_outlines.contains(id) { - return Err(invalid("Title outlines cannot be removed")); - } - let edit = TreeEdit::delete(*id, self.author)?; - self.write(|active| edit.changes(active))?; - } - } - Ok(()) - } - - /// Rewrites every equation paragraph whose stored text object differs from the model. - fn edit_equations(&mut self, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let current = View::new(¤t, false)?; - for (id, paragraph) in &new.paragraphs { - let Some(text) = paragraph.text() else { - continue; - }; - let stored = current.text(self.id(*id)); - // Deleting an equation leaves no math in the model, but its stored runs are still math. - if !super::Math::is_equation(&text.text) - && !stored.is_some_and(|stored| super::Math::is_equation(&stored.text)) - { - continue; - } - let stored = stored - .ok_or_else(|| invalid("An equation paragraph is missing after placement"))?; - if stored.text == text.text { - continue; - } - let object = stored.id; - self.write(|active| content::equation_changes(active, object, &text.text))?; - } - Ok(()) - } - - /// Lifts the paragraphs of outline groups into their container wherever this save changes - /// the container's children or their depths, for placement to see them; `edit_levels` - /// groups them again. - fn ungroup(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - for (container, children) in &old.children { - let Some(after) = new.children.get(container) else { - continue; - }; - let grouped = self.active.view.nodes[container] - .children - .iter() - .any(|id| matches!(self.active.view.nodes[id].kind, Kind::OutlineGroup)); - if !grouped - || (after == children - && old.depths(*container, children) == new.depths(*container, after)) - { - continue; - } - let object = *container; - self.write(|active| levels::ungroup_changes(active, object))?; - } - Ok(()) - } - - /// Indents each container's children to the depths the model gives them. - fn edit_levels(&mut self, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let stored = View::new(¤t, false)?; - for (container, children) in &new.children { - let depths = new - .depths(*container, children) - .ok_or_else(|| invalid("A paragraph lies no deeper than its parent"))?; - let object = self.id(*container); - let written: Vec = children.iter().map(|id| self.id(*id)).collect(); - if children.is_empty() || stored.depths(object, &written) == Some(depths.clone()) { - continue; - } - let is_cell = - !new.paragraphs.contains_key(container) && !new.outlines.contains_key(container); - let (level, runs) = levels::runs(&written, &depths, is_cell)?; - self.write(|active| levels::regroup_changes(active, object, level, &runs))?; - } - Ok(()) - } - - /// Gives a paragraph the text object its model names when a split and a join in one save - /// moved text between paragraphs: a stored object is taken over, as OneNote moves a lower - /// paragraph's text into an emptied upper one; a new one starts as a copy of the text - /// the paragraph holds. - fn edit_text_identities(&mut self, new: &View<'_>) -> Result<(), Error> { - for (id, paragraph) in &new.paragraphs { - let Some(text) = paragraph.text() else { - continue; - }; - let object = self.id(*id); - let stored = self.active.view.nodes[&object].content[0]; - if stored == self.id(text.id) { - continue; - } - let target = if self.active.live.revision.objects.contains_key(&text.id) { - text.id - } else { - self.allocate(text.id)? - }; - let unstyled = paragraph.style.is_none(); - self.write(|active| { - let parents = active.editable_parents(object)?; - let modified = crate::create::current_timestamps()?.0.to_le_bytes(); - let raw = &active.live.revision; - let mut changed = BTreeMap::new(); - if !raw.objects.contains_key(&target) { - let mut copy = PropertyObject::from_object(&raw.objects[&stored])?; - copy.remove(&[0x1c001c98, 0x14001c99])?; - if unstyled { - copy.remove(&[0x2000342c])?; - } - copy.reference(target)?; - changed.insert(target, copy); - } - let mut holder = PropertyObject::from_object(&raw.objects[&object])?; - let reference = holder.reference(target)?; - holder.set(&[(0x24001c1f, &reference), (0x14001d7a, &modified)])?; - changed.insert(object, holder); - crate::formatting::touch_ancestors(raw, parents, object, &modified, &mut changed)?; - Ok(changed) - })?; - } - Ok(()) - } - - fn edit_text(&mut self, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let current = View::new(¤t, false)?; - for (id, paragraph) in &new.paragraphs { - let Some(text) = paragraph.text() else { - continue; - }; - let image_id = self.id(*id); - let Some(stored) = current.text(image_id) else { - return Err(invalid("A paragraph is missing after structural edits")); - }; - if stored.id != self.id(text.id) { - return Err(invalid("Text object identities cannot change")); - } - if stored.text.text() == text.text.text() { - continue; - } - let (range, replacement) = text_edit(stored.text.text(), text.text.text())?; - let object = stored.id; - self.write(|active| crate::edit::text_changes(active, object, range, &replacement))?; - } - Ok(()) - } - - /// Gives each paragraph the list nodes its model references: a definition new to the - /// section becomes a list node carrying the model's identity, a changed one is rewritten - /// in place, a definition another paragraph already owns is copied because native list - /// nodes belong to one paragraph, and a dropped reference leaves the node unreferenced. - fn edit_lists(&mut self, after: &Page, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let stored = View::new(¤t, false)?; - let mut owners: BTreeMap = BTreeMap::new(); - for (id, paragraph) in &stored.paragraphs { - for list in ¶graph.lists { - owners.insert(*list, *id); - } - } - for (id, paragraph) in &new.paragraphs { - let image_id = self.id(*id); - let previous = stored - .paragraphs - .get(&image_id) - .ok_or_else(|| invalid("A paragraph is missing after text edits"))?; - let same = paragraph.lists.len() == previous.lists.len() - && paragraph - .lists - .iter() - .zip(&previous.lists) - .all(|(model, node)| { - self.id(*model) == *node - && after.definitions.get(model) == current.definitions.get(node) - }); - if same { - continue; - } - let mut nodes = Vec::new(); - for list in ¶graph.lists { - // Writer-allocated identities keep a nonzero sequence number so the node's - // compact identity is never the null identity; squash renames them to the - // model's. A copy for a second owner keeps its allocated identity. - let node_id = match owners.get(&self.id(*list)) { - Some(owner) if *owner == image_id => self.id(*list), - Some(_) => ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }, - // A node its paragraph let go of in this save serves the model again. - None if self.active.live.revision.objects.contains_key(list) => *list, - None => { - let written = ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }; - self.alias.insert(*list, written); - written - } - }; - let definition = after - .definitions - .get(list) - .ok_or_else(|| invalid("A paragraph references a missing list definition"))?; - nodes.push((node_id, properties::list_values(definition)?)); - } - let object = self.id(*id); - self.write(|active| properties::list_changes(active, object, &nodes))?; - } - Ok(()) - } - - /// Rewrites the note tags of paragraphs and text objects whose model tags differ from - /// the stored ones. A tag definition new to the section becomes a definition object - /// carrying the model's identity after squash. - fn edit_tags(&mut self, after: &Page, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let stored = View::new(¤t, false)?; - for (id, paragraph) in &new.paragraphs { - let image_id = self.id(*id); - let previous = stored - .paragraphs - .get(&image_id) - .ok_or_else(|| invalid("A paragraph is missing after text edits"))?; - let mut targets = vec![(image_id, ¶graph.tags, &previous.tags)]; - if let (Some(text), Some(before)) = (paragraph.text(), previous.text()) { - targets.push((self.id(text.id), &text.tags, &before.tags)); - } - for (object, tags, stored_tags) in targets { - if crate::op::lower::same_tags(tags, stored_tags) { - continue; - } - let mut entries = Vec::new(); - for tag in tags { - let definition = tag - .definition - .ok_or_else(|| invalid("A note tag names its definition"))?; - let known = self.active.live.revision.objects.contains_key(&definition) - || self.alias.contains_key(&definition); - let written = if known { - self.id(definition) - } else { - let allocated = ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }; - self.alias.insert(definition, allocated); - allocated - }; - entries.push((written, tag, after.definitions.get(&definition))); - } - self.write(|active| properties::tag_changes(active, object, &entries))?; - } - } - Ok(()) - } - - /// A new paragraph, or one whose style changed, references its paragraph style from - /// its text object; a style the page has not stored yet is created from its - /// definition, as OneNote keeps quick styles. - fn edit_paragraph_styles( - &mut self, - old: &View<'_>, - new: &View<'_>, - after: &Page, - ) -> Result<(), Error> { - for (id, paragraph) in &new.paragraphs { - let Some(definition) = paragraph.style else { - continue; - }; - if old - .paragraphs - .get(id) - .is_some_and(|previous| previous.style == paragraph.style) - { - continue; - } - let Some(text) = paragraph.text() else { - return Err(invalid("Only text paragraphs take a paragraph style")); - }; - let known = self.active.live.revision.objects.contains_key(&definition) - || self.alias.contains_key(&definition); - let style_id = if known { - self.id(definition) - } else { - let allocated = ExGuid { - guid: crate::write::fresh_guid()?, - n: 1, - }; - self.alias.insert(definition, allocated); - allocated - }; - let object = self.id(text.id); - let definition = after.definitions.get(&definition); - self.write(|active| properties::style_changes(active, object, style_id, definition))?; - } - Ok(()) - } - - fn edit_paragraph_formatting(&mut self, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let current = View::new(¤t, false)?; - for (id, paragraph) in &new.paragraphs { - let Some(text) = paragraph.text() else { - continue; - }; - let stored = current - .text(self.id(*id)) - .ok_or_else(|| invalid("A paragraph is missing after text edits"))?; - let values = crate::op::lower::paragraph_change(&stored.text, &text.text)?; - if values.is_empty() { - continue; - } - if text.date_field.is_some() { - return Err(invalid( - "Generated title fields cannot be formatted as ordinary text", - )); - } - let object = stored.id; - self.write(|active| properties::paragraph_format_changes(active, object, &values))?; - } - Ok(()) - } - - fn edit_formatting(&mut self, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let current = View::new(¤t, false)?; - for (id, paragraph) in &new.paragraphs { - let Some(text) = paragraph.text() else { - continue; - }; - let stored = current - .text(self.id(*id)) - .ok_or_else(|| invalid("A paragraph is missing after text edits"))?; - if stored.text.text() != text.text.text() { - return Err(invalid("Text edits did not converge on the model")); - } - let fresh = self.alias.contains_key(&text.id); - for (range, set, clear) in crate::op::lower::format_edits(&stored.text, &text.text, fresh)? { - let object = stored.id; - self.write(|active| { - crate::formatting::format_changes(active, object, range, &set, &clear) - })?; - } - } - Ok(()) - } - - fn edit_layout(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { - let current = self.current()?; - let current = View::new(¤t, false)?; - for (id, paragraph) in &new.paragraphs { - let stored = current - .paragraphs - .get(&self.id(*id)) - .ok_or_else(|| invalid("A paragraph is missing after text edits"))?; - if stored.collapsed != paragraph.collapsed { - let object = self.id(*id); - let edit = OutlineEdit::Collapsed(paragraph.collapsed); - self.write(|active| edit.changes(active, object))?; - } - } - for (id, outline) in &new.outlines { - if new.title_outlines.contains(id) { - continue; - } - let stored = current - .outlines - .get(&self.id(*id)) - .ok_or_else(|| invalid("An outline is missing after text edits"))?; - let object = self.id(*id); - // A new outline takes the model's indentation table (a copied outline keeps - // its levels' offsets); a stored table stays as it is. - if !old.outlines.contains_key(id) - && !outline.indents.is_empty() - && outline.indents != stored.indents - { - let indents = content::measurement_bytes(&outline.indents, 4)?; - self.write(|active| { - let raw = &active.live.revision; - let mut node = PropertyObject::from_object(&raw.objects[&object])?; - node.set(&[(0x1c001c12, &indents)])?; - Ok(BTreeMap::from([(object, node)])) - })?; - } - if (outline.layout.x, outline.layout.y) != (stored.layout.x, stored.layout.y) { - let (Some(x), Some(y)) = (outline.layout.x, outline.layout.y) else { - return Err(invalid("An outline position needs both coordinates")); - }; - let edit = OutlineEdit::Position { x, y }; - self.write(|active| edit.changes(active, object))?; - } - if (outline.layout.max_width, outline.layout.width_set_by_user) - != (stored.layout.max_width, stored.layout.width_set_by_user) - && (old.outlines.contains_key(id) || outline.layout.max_width.is_some()) - { - let Some(points) = outline.layout.max_width else { - return Err(invalid("An outline width cannot be removed")); - }; - let edit = OutlineEdit::Width { - points, - user_set: outline.layout.width_set_by_user == Some(true), - }; - self.write(|active| edit.changes(active, object))?; - } - } - Ok(()) - } -} - -/// Payload identities the file-data store of `store` declares, in order. -pub(crate) fn declared_payloads(store: &Store<'_>) -> Vec<[u8; 16]> { - store - .lists - .values() - .flat_map(|list| &list.nodes) - .filter(|node| node.id == 0x94) - .filter_map(|node| node.payload.get(..16).and_then(|g| g.try_into().ok())) - .collect() -} - -/// Writes the spaces the typed writers `edited` as one transaction on the validated -/// `source`, renaming writer-allocated identities to the model's and embedding the -/// `payloads` it lacks. A protected `source` takes the revisions its plaintext twin gained. -pub(crate) fn squash( - source: &RevisionIndex<'_>, - edited: &[(ExGuid, &crate::ResolvedRevision<'_>)], - payloads: &[([u8; 16], &[u8])], - alias: &BTreeMap, - protection: Option<&dyn crate::write::Protection>, -) -> Result, Error> { - let rename: BTreeMap = alias - .iter() - .map(|(model, image)| (*image, *model)) - .collect(); - let existing = declared_payloads(source.store); - let payloads: Vec<_> = payloads - .iter() - .filter(|(guid, _)| !existing.contains(guid)) - .copied() - .collect(); - let edit = |index: &RevisionIndex<'_>| { - let mut changes = BTreeMap::new(); - for (sid, after) in edited { - let before = match protection { - Some(protection) => protection.resolve(*sid, index.active(*sid)?)?, - None => index.resolve_active(*sid)?, - }; - if before.roots != after.roots { - return Err(invalid("Page edits cannot change revision roots")); - } - // Retired styles and deleted content stay in history; only live objects are written. - let live = after.reachable()?; - let mut changed = BTreeMap::new(); - for (id, object) in &after.objects { - if !live.contains(id) { - continue; - } - // Stored tables keep the entries an object names, so those decide. - if let Some(previous) = before.objects.get(id) - && previous.jcid == object.jcid - && previous.data == object.data - { - let entries = match object.data { - ObjectData::Properties(bytes) => crate::write::table_entries(bytes)?, - _ => Vec::new(), - }; - if entries - .iter() - .all(|entry| previous.global_ids.get(entry) == object.global_ids.get(entry)) - { - continue; - } - } - let id = rename.get(id).copied().unwrap_or(*id); - let mut replacement = match object.data { - ObjectData::Properties(_) => { - let mut replacement = PropertyObject::from_object(object)?; - remap(&mut replacement, &rename)?; - replacement - } - ObjectData::File { - reference, - extension, - } => { - let text = |bytes: &[u8]| { - String::from_utf16( - &bytes - .chunks_exact(2) - .map(|pair| u16::from_le_bytes([pair[0], pair[1]])) - .collect::>(), - ) - .map_err(|_| invalid("Invalid UTF-16 file-data declaration")) - }; - let mut replacement = - PropertyObject::file(id, &text(reference)?, &text(extension)?)?; - replacement.jcid = object.jcid; - replacement - } - ObjectData::Encrypted(_) => { - return Err(invalid("Page edits only produce property objects")); - } - }; - replacement.reference(id)?; - if before.objects.contains_key(&id) && rename.values().any(|model| *model == id) { - return Err(invalid( - "A new model identity already exists in the section", - )); - } - changed.insert(id, replacement); - } - changes.insert(*sid, RevisionEdit::Update(changed)); - } - Ok(changes) - }; - let output = crate::write::build_on(source, &payloads, protection, edit)?; - crate::write::check(&output, protection.is_none())?; - Ok(output) -} - -fn remap(object: &mut PropertyObject, rename: &BTreeMap) -> Result<(), Error> { - let mut remapped = Vec::new(); - for property in PropertySets::parse(&object.bytes)?.sets.iter().flatten() { - if let Value::References { - stream: crate::IdStream::Objects, - compact_ids, - } = property.value - { - for bytes in compact_ids.chunks_exact(4) { - let offset = bytes.as_ptr().addr() - object.bytes.as_ptr().addr(); - let id = crate::bytes::Cursor { bytes, offset }.compact(&object.global_ids)?; - if let Some(model) = rename.get(&id) { - remapped.push((offset, *model)); - } - } - } - } - for (offset, id) in remapped { - let reference = object.reference(id)?; - object.bytes[offset..offset + 4].copy_from_slice(&reference); - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - document::Format, - page::{PageParagraph, Paragraph, TextObject, text::new_id}, - }; - - /// A new page in a new section and the page with `count` paragraphs added in an outline. - fn generated(count: usize) -> (Vec, ExGuid, Page) { - let section = crate::create_section("big.one", "First", "Author").unwrap(); - let creation = crate::PageCreation::new(None, Some("Big"), "Author").unwrap(); - let source = crate::PreparedEdit::create_page(§ion, &creation) - .unwrap() - .as_bytes() - .to_vec(); - let space = creation.space(); - let store = Store::parse(&source).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let mut page = - Page::from_space(&crate::document::Document::parse(&index).unwrap(), space).unwrap(); - let paragraph = |i: usize| PageParagraph { - id: new_id().unwrap(), - parent: None, - level: 1, - style: None, - format: Format::default(), - content: ParagraphContent::Text(TextObject { - id: new_id().unwrap(), - date_field: None, - text: Paragraph::new(format!("Paragraph {i}"), Format::default()), - tags: Vec::new(), - }), - lists: Vec::new(), - tags: Vec::new(), - media: Default::default(), - collapsed: false, - }; - page.objects.push(PageObject::Outline(Outline { - id: new_id().unwrap(), - title: false, - min_width: None, - layout: crate::document::Layout { - x: Some(36.0), - y: Some(86.4), - ..Default::default() - }, - indents: Vec::new(), - paragraphs: (0..count).map(paragraph).collect(), - unsupported: Vec::new(), - })); - (source, space, page) - } - - #[test] - fn a_page_write_builds_one_revision_however_many_paragraphs_it_adds() { - for count in [10, 40] { - let (source, space, after) = generated(count); - let builds = crate::write::BUILDS.with(std::cell::Cell::get); - let written = write_page(&source, space, &after, "Author").unwrap(); - assert_eq!(crate::write::BUILDS.with(std::cell::Cell::get) - builds, 1); - let store = Store::parse(&written).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let read = Page::from_space(&crate::document::Document::parse(&index).unwrap(), space) - .unwrap(); - let texts = |page: &Page| -> Vec { - page.objects - .iter() - .filter_map(|object| match object { - PageObject::Outline(outline) => Some(outline), - _ => None, - }) - .flat_map(|outline| &outline.paragraphs) - .filter_map(|paragraph| Some(paragraph.text()?.text.text().to_owned())) - .collect() - }; - assert_eq!(texts(&read), texts(&after)); - } - } -} diff --git a/crates/onestore/src/pages.rs b/crates/onestore/src/pages.rs index dcb87cb8868234d9523e7fd08f093fa329a490b0..d0a6782091ab9b112f18f5d3750c6f8861dde5ef 100644 --- a/crates/onestore/src/pages.rs +++ b/crates/onestore/src/pages.rs @@ -1,9 +1,9 @@ use crate::{ Error, ExGuid, create::{current_timestamps, properties, string}, - document::{Document, FieldValue, Kind}, + document::{FieldValue, Kind}, op::content::{NATIVE_INDENTS, measurement_bytes}, - write::{PropertyObject, RevisionEdit, fresh_guid, write_revisions}, + write::{PropertyObject, fresh_guid}, }; use serde::{Deserialize, Serialize}; use std::{ @@ -15,7 +15,7 @@ fn invalid(message: &'static str) -> Error { Error { offset: 0, message } } -fn metadata_id(space: ExGuid) -> ExGuid { +pub(crate) fn metadata_id(space: ExGuid) -> ExGuid { let mut id = ExGuid { guid: space.guid, n: 1, @@ -28,7 +28,7 @@ fn metadata_id(space: ExGuid) -> ExGuid { id } -fn metadata_guid(metadata: &crate::document::Element<'_>) -> Result<[u8; 16], Error> { +pub(crate) fn metadata_guid(metadata: &crate::document::Element<'_>) -> Result<[u8; 16], Error> { metadata.extra[0] .iter() .find_map(|field| match field.value { @@ -38,7 +38,7 @@ fn metadata_guid(metadata: &crate::document::Element<'_>) -> Result<[u8; 16], Er .ok_or_else(|| invalid("Page metadata needs its page identifier")) } -fn set_references(object: &mut PropertyObject, property: u32, ids: &[ExGuid]) -> Result<(), Error> { +pub(crate) fn set_references(object: &mut PropertyObject, property: u32, ids: &[ExGuid]) -> Result<(), Error> { let mut references = Vec::new(); for id in ids { references.extend_from_slice(&object.reference(*id)?); @@ -51,6 +51,20 @@ fn set_references(object: &mut PropertyObject, property: u32, ids: &[ExGuid]) -> object.copy_property(&source, property) } +/// A conflict page (MS-ONE 2.1.2): the version of a page a merge could not take, kept +/// read-only under the page it conflicts with. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConflictPage { + pub space: ExGuid, + pub title: String, + /// Whose version it is (`ConflictingUserName`). + pub user: String, + /// FILETIME of its `TopologyCreationTimeStamp`, when the merge made it. + pub created: Option, + /// The conflict objects on it (`IsConflictObjectForRender`), which OneNote highlights. + pub objects: Vec, +} + /// Whether an edit preserves position or moves before a page (None appends). #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -138,42 +152,7 @@ pub(crate) struct PageParts<'a> { pub stored_metadata: (ExGuid, PropertyObject), } -pub(crate) fn edit_pages( - source: &[u8], - edits: &[PageEdit], - removals: &[ExGuid], -) -> Result, Error> { - write_revisions(source, |index| { - let document = Document::parse(index)?; - let pages: Vec = document.pages()?.into_iter().map(|(sid, _)| sid).collect(); - let root = document.active(document.root)?; - let raw = index.resolve_active(document.root)?; - let parts = |sid: ExGuid| -> Result, Error> { - let page = document.active(sid)?; - let revision = index.resolve_active(sid)?; - let metadata = page - .roots - .get(&2) - .and_then(|id| page.nodes.get(id)) - .ok_or_else(|| invalid("Page metadata is unavailable"))?; - let (manifest, stored) = (revision.roots[&1], revision.roots[&2]); - Ok(PageParts { - metadata: metadata.clone(), - manifest: (manifest, PropertyObject::from_object(&revision.objects[&manifest])?), - stored_metadata: (stored, PropertyObject::from_object(&revision.objects[&stored])?), - }) - }; - let Some(changes) = page_changes(document.root, root, &raw, &pages, parts, edits, removals)? else { - return Ok(BTreeMap::new()); - }; - Ok(changes - .into_iter() - .map(|(space, changes)| (space, RevisionEdit::Update(changes))) - .collect()) - }) -} - -/// `edit_pages` on a section kept open: the objects each changed space stores. +/// Page moves and removals on a section kept open: the objects each changed space stores. pub(crate) fn section_changes( section: &mut crate::Section<'_>, edits: &[PageEdit], @@ -197,12 +176,18 @@ pub(crate) fn section_changes( let parts = |sid: ExGuid| -> Result, Error> { let revision = section.revision(sid)?; let (manifest, stored) = (revision.roots[&1], revision.roots[&2]); - Ok(PageParts { - metadata: crate::document::Element::parse_with( - &revision.objects[&stored], + let element = |id| { + crate::document::Element::parse_with( + &revision.objects[id], crate::FileType::Section, &mut |_| Err(invalid("Page metadata holds no payload")), - )?, + ) + }; + if element(&manifest)?.content.len() != 1 { + return Err(invalid("Each page space must contain one page with a valid level")); + } + Ok(PageParts { + metadata: element(&stored)?, manifest: (manifest, PropertyObject::from_object(&revision.objects[&manifest])?), stored_metadata: (stored, PropertyObject::from_object(&revision.objects[&stored])?), }) @@ -491,6 +476,12 @@ pub struct PageCreation { title: Option, author: String, created: u32, + /// The title's date and time fields' text, for a titled page. + #[serde(default, skip_serializing_if = "Option::is_none")] + date: Option<[String; 2]>, + /// The page identity and creation time (FILETIME) a page keeps from elsewhere. + #[serde(default, skip_serializing_if = "Option::is_none")] + kept: Option<([u8; 16], u64)>, } impl PageCreation { @@ -505,11 +496,57 @@ impl PageCreation { title: title.map(str::to_owned), author: author.to_owned(), created: current_timestamps()?.0, + date: None, + kept: None, }; page.validate()?; Ok(page) } + /// Gives a titled page's title OneNote 2010's date and time fields, showing `date` and + /// `time`: the creation time (`created`) as the user's long date and short time. + pub fn dated(mut self, date: &str, time: &str) -> Result { + if self.title.is_none() { + return Err(invalid("Only a titled page shows its date")); + } + self.date = Some([date.to_owned(), time.to_owned()]); + self.validate()?; + Ok(self) + } + + /// Keeps `identity`, the page identity internal links name, and `created`, as OneNote + /// 2010 keeps both for a page it moves to the notebook's recycle bin. + pub fn keeping(mut self, identity: [u8; 16], created: u64) -> Result { + if identity == [0; 16] { + return Err(invalid("Keep an existing page identity")); + } + let seconds = (created / 10_000_000) + .checked_sub(11_644_473_600 + 315_532_800) + .and_then(|seconds| u32::try_from(seconds).ok()) + .ok_or_else(|| invalid("The kept creation time is outside OneNote's range"))?; + self.created = seconds; + self.kept = Some((identity, created)); + Ok(self) + } + + /// Who creates the page. + pub fn author(&self) -> &str { + &self.author + } + + /// When the page was created, FILETIME, which its date and time fields show. + pub fn created(&self) -> u64 { + match self.kept { + Some((_, created)) => created, + None => (u64::from(self.created) + 315_532_800 + 11_644_473_600) * 10_000_000, + } + } + + /// The page identity internal links name as `page-id`. + fn identity(&self) -> [u8; 16] { + self.kept.map_or(self.guid, |(identity, _)| identity) + } + pub fn space(&self) -> ExGuid { ExGuid { guid: self.guid, @@ -531,6 +568,11 @@ impl PageCreation { }) } + /// The page the new page goes before; `None` appends it. + pub fn before(&self) -> Option { + self.before + } + /// Changes the insertion anchor while retaining all identities and creation metadata. pub fn reposition(&self, before: Option) -> Result { let mut page = self.clone(); @@ -547,8 +589,9 @@ impl PageCreation { || self.before.is_some_and(|id| id.guid == [0; 16]) || self .title - .as_ref() - .is_some_and(|title| title.contains(['\0', '\r', '\n', '\u{fffc}', '\u{fddf}'])) + .iter() + .chain(self.date.iter().flatten()) + .any(|text| text.contains(['\0', '\r', '\n', '\u{fffc}', '\u{fddf}'])) { return Err(invalid( "Use a new page identity, an existing page anchor, and ordinary single-line title text", @@ -557,28 +600,7 @@ impl PageCreation { Ok(()) } - pub(crate) fn apply(&self, source: &[u8]) -> Result, Error> { - self.validate()?; - write_revisions(source, |index| { - if index.spaces.contains_key(&self.space()) { - return Err(invalid( - "This page identity already exists; reconcile the original creation", - )); - } - let document = Document::parse(index)?; - document.pages()?; - let view = document.active(document.root)?; - let raw = index.resolve_active(document.root)?; - let (root, roots, objects) = self.creation(view, &raw)?; - Ok(BTreeMap::from([ - (document.root, RevisionEdit::Update(root)), - (self.space(), RevisionEdit::Create { roots, objects }), - ])) - }) - } - - /// `apply` on a section kept open: the root space's changes, and the new space's roots - /// and objects. + /// The root space's changes creating this page, and the new space's roots and objects. pub(crate) fn changes( &self, section: &mut crate::Section<'_>, @@ -594,6 +616,133 @@ impl PageCreation { self.creation(&page.view, &page.live.revision) } + /// The object space of a conflict page this creation makes, as OneNote 2010 stores one: + /// the page's space with `jcidConflictPageMetaData` (MS-ONE 2.2.35) as its metadata, + /// naming this creation's author as the conflicting user and `title` as the page's. + /// Returns the roots, the objects and the metadata, which the conflicting page's + /// manifest also keeps a copy of. + pub(crate) fn conflict( + &self, + section: &mut crate::Section<'_>, + title: &str, + level: u32, + ) -> Result<(BTreeMap, crate::active::Changes, PropertyObject), Error> { + if self.before.is_some() { + return Err(invalid("A conflict page is not placed in the page list")); + } + let (_, roots, mut objects) = self.changes(section)?; + let timestamp = self.created(); + let initials = crate::create::initials(&self.author); + let metadata = PropertyObject { + jcid: 0x20038, + bytes: properties(&[ + (0x1c001cf3, string(title)), + (0x1c001c30, self.identity().to_vec()), + (0x14001dff, level.to_le_bytes().to_vec()), + (0x14001d82, 40_u32.to_le_bytes().to_vec()), + (0x1400348b, 40_u32.to_le_bytes().to_vec()), + (0x18001c65, timestamp.to_le_bytes().to_vec()), + (0x1c001d9e, string(&self.author)), + (0x1c001d9f, string(&initials)), + ])?, + global_ids: Arc::new(BTreeMap::from([(0, self.guid)])), + }; + objects.insert(roots[&2], metadata.clone()); + Ok((roots, objects, metadata)) + } + + /// The title's second outline as OneNote 2010 writes it for a new page (object numbers, + /// jcids and properties): a read-only outline of two paragraphs, the date then the + /// time, in the `PageDateTime` style. + fn date_fields(&self, date: &str, time: &str) -> Vec<(u32, u32, Vec<(u32, Vec)>)> { + let reference = |n: u32| n.to_le_bytes().to_vec(); + let modified = || (0x14001d7a, self.created.to_le_bytes().to_vec()); + let yes = |id: u32| (id | 1 << 31, Vec::new()); + let paragraph = |text: u32| { + vec![ + modified(), + (0x20001d79, reference(17)), + (0x20001d78, reference(17)), + (0x14001d09, self.created.to_le_bytes().to_vec()), + (0x0c001c03, vec![1]), + (0x24001c1f, reference(text)), + yes(0x08001cb2), + yes(0x08001d0c), + (0x08001c34, Vec::new()), + ] + }; + let text = |value: &str, role: u32| { + // OneNote keeps Latin-1 text as TextExtendedAscii, other text as Unicode. + let content = match value.chars().map(|c| u8::try_from(c).ok()).collect::>>() { + Some(latin) => (0x1c003498, latin), + None => (0x1c001c22, string(value)), + }; + vec![ + modified(), + (0x2000342c, reference(23)), + yes(0x08001cde), + yes(0x08001c00), + yes(0x08001c88), + yes(0x08001d0c), + yes(role), + (0x10001cfe, 0x409_u16.to_le_bytes().to_vec()), + (0x1c001cc8, vec![0, 0, 0, 0, 3]), + content, + (0x24001e13, reference(19)), + yes(0x080034dd), + ] + }; + let no = |id: u32| (id, Vec::new()); + vec![ + ( + 20, + 0x6000c, + vec![ + modified(), + (0x24001c20, [reference(21), reference(24)].concat()), + ( + 0x1c001c12, + measurement_bytes(&NATIVE_INDENTS, 4).expect("Native indents encode"), + ), + (0x0c001c13, vec![0]), + (0x0c001c03, vec![1]), + (0x14001c1c, 0.6_f32.to_le_bytes().to_vec()), + yes(0x08001cb5), + yes(0x08001c00), + yes(0x08001cb2), + yes(0x08001cde), + yes(0x08001d0c), + (0x14001c3e, reference(0)), + (0x14001c84, reference(0xc)), + ], + ), + (21, 0x6000d, paragraph(22)), + (22, 0x6000e, text(date, 0x08001cb5)), + ( + 23, + 0x12004d, + vec![ + (0x1c00345a, string("PageDateTime")), + no(0x08001c04), + no(0x08001c05), + no(0x08001c06), + (0x14001c0c, 0x0080_8080_u32.to_le_bytes().to_vec()), + (0x1c001c0a, string("Calibri")), + (0x10001c0b, 20_u16.to_le_bytes().to_vec()), + (0x1400342e, 0_f32.to_le_bytes().to_vec()), + (0x1400342f, 0_f32.to_le_bytes().to_vec()), + (0x14003430, 0_f32.to_le_bytes().to_vec()), + no(0x08001c07), + (0x14001c0d, 0xff00_0000_u32.to_le_bytes().to_vec()), + no(0x08001c09), + no(0x08001c08), + ], + ), + (24, 0x6000d, paragraph(25)), + (25, 0x6000e, text(time, 0x08001c87)), + ] + } + fn creation( &self, view: &crate::document::Revision<'_>, @@ -621,9 +770,7 @@ impl PageCreation { }; let id = |n| ExGuid { guid: self.guid, n }; let reference = |n: u32| n.to_le_bytes().to_vec(); - let timestamp = ((u64::from(self.created) + 315532800 + 11644473600) * 10000000) - .to_le_bytes() - .to_vec(); + let timestamp = self.created().to_le_bytes().to_vec(); let modified = || (0x14001d7a, self.created.to_le_bytes().to_vec()); let mut table = BTreeMap::from([(0, self.guid)]); let metadata_id = metadata_id(self.space()); @@ -638,7 +785,7 @@ impl PageCreation { }; let title = self.title.as_deref().unwrap_or_default().trim_start(); let metadata = vec![ - (0x1c001c30, self.guid.to_vec()), + (0x1c001c30, self.identity().to_vec()), (0x1c001cf3, string(title)), (0x14001d82, reference(40)), (0x1400348b, reference(40)), @@ -649,6 +796,7 @@ impl PageCreation { vec![ modified(), (0x1c001d75, string(&self.author)), + (0x1c001df8, string(&crate::create::initials(&self.author))), (0x1c001d3c, string("")), ], crate::create::page_margins(), @@ -669,7 +817,11 @@ impl PageCreation { 0x6002c, vec![ modified(), - (0x24001c20, reference(14)), + ( + 0x24001c20, + [reference(14), self.date.iter().flat_map(|_| reference(20)).collect()] + .concat(), + ), (0x14001c14, 0_f32.to_le_bytes().to_vec()), (0x14001c15, 0_f32.to_le_bytes().to_vec()), (0x14001c3e, reference(0x0009_000c)), @@ -723,7 +875,7 @@ impl PageCreation { (0x88001cb4, vec![]), ], ), - (17, 0x120001, vec![(0x1c001d75, string(&self.author))]), + (17, 0x120001, crate::create::author_properties(&self.author)), ( 18, 0x12004d, @@ -737,6 +889,11 @@ impl PageCreation { ] { objects.insert(id(n), object(jcid, values)?); } + if let Some([date, time]) = &self.date { + for (n, jcid, values) in self.date_fields(date, time) { + objects.insert(id(n), object(jcid, values)?); + } + } } let series = object( 0x60008, @@ -774,7 +931,25 @@ impl PageCreation { #[cfg(test)] mod tests { use super::*; - use crate::{RevisionIndex, Store}; + use crate::{ + RevisionIndex, Store, + document::Document, + op::{Edit, Op, OpError, SectionOp}, + write::{RevisionEdit, write_revisions}, + }; + + /// Page moves and removals through a section, sealed: the image they leave. + fn edit_pages(source: &[u8], edits: &[PageEdit], removals: &[ExGuid]) -> Result, OpError> { + let arena = crate::Arena::default(); + let mut section = crate::Section::open(&arena, source.to_vec()).map_err(OpError::Failed)?; + let mut ops = vec![Op::Section(SectionOp::Pages(edits.to_vec()))]; + if !removals.is_empty() { + ops.push(Op::Section(SectionOp::Delete(removals.to_vec()))); + } + section.apply("Author", &Edit { at: 133_700_000_000_000_000, ops })?; + section.seal().map_err(OpError::Failed)?; + Ok(section.image()) + } #[test] fn page_movement_restores_an_optional_metadata_cache_from_history() { diff --git a/crates/onestore/src/paragraph.rs b/crates/onestore/src/paragraph.rs index 10430ec625669a99979faa780694bbd4c1e936c9..b202513488732130869e9136c509e7277f999b02 100644 --- a/crates/onestore/src/paragraph.rs +++ b/crates/onestore/src/paragraph.rs @@ -6,7 +6,6 @@ use crate::{ edit::update_title, write::{PropertyObject, fresh_guid}, }; -use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, ops::Range, @@ -17,11 +16,10 @@ fn invalid(message: &'static str) -> Error { Error { offset: 0, message } } -/// Splits ordinary paragraph text while retaining the new objects' identities across retries. -/// The original paragraph/text remain on the left; nested children move to the right. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct ParagraphSplit { +/// Splits ordinary paragraph text. The original paragraph/text remain on the left; nested +/// children move to the right. +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct ParagraphSplit { guid: [u8; 16], text: ExGuid, offset: u32, @@ -31,7 +29,7 @@ pub struct ParagraphSplit { impl ParagraphSplit { /// The offset is measured in UTF-16 code units and must lie between Unicode scalars. - pub fn new(text: ExGuid, offset: u32, author: &str) -> Result { + pub(crate) fn new(text: ExGuid, offset: u32, author: &str) -> Result { if text.guid == [0; 16] || author.contains('\0') { return Err(invalid( "Choose paragraph text and an author name without NUL", @@ -46,64 +44,6 @@ impl ParagraphSplit { }) } - /// Identity of the new right paragraph. - pub fn object(&self) -> ExGuid { - ExGuid { - guid: self.guid, - n: 1, - } - } - - /// Identity of the new right paragraph's text. - pub fn text_object(&self) -> ExGuid { - ExGuid { - guid: self.guid, - n: 2, - } - } - - /// Original text identity and UTF-16 split boundary. - pub fn position(&self) -> (ExGuid, u32) { - (self.text, self.offset) - } - - /// Changes the split boundary while retaining allocated identities and creation time. - /// Preparing the edit validates the offset against the supplied text. - pub fn reposition(&self, offset: u32) -> Self { - let mut split = self.clone(); - split.offset = offset; - split - } - - pub(crate) fn apply(&self, source: &[u8], space: ExGuid) -> Result, Error> { - if self.guid == [0; 16] || self.text.guid == [0; 16] || self.author.contains('\0') { - return Err(invalid( - "Choose paragraph text and an author name without NUL", - )); - } - crate::active::write(source, space, |active| self.changes(active)) - } - - pub(crate) fn changes(&self, active: &ActivePage<'_>) -> Result { - let lists = (5..256) - .map(|n| ExGuid { - guid: self.guid, - n, - }) - .collect::>(); - let changes = self.changes_as(active, self.object(), self.text_object(), &lists)?; - let raw = &active.live.revision; - if changes - .keys() - .any(|id| id.guid == self.guid && raw.objects.contains_key(id)) - { - return Err(invalid( - "A split identity already exists; reconcile the existing edit", - )); - } - Ok(changes) - } - /// `changes` creating the right paragraph `paragraph` with text `right`, and a copy of /// the left paragraph's list nodes under `lists`, in order. pub(crate) fn changes_as( @@ -244,7 +184,7 @@ impl ParagraphSplit { author_id, PropertyObject { jcid: 0x120001, - bytes: properties(&[(0x1c001d75, string(&self.author))])?, + bytes: properties(&crate::create::author_properties(&self.author))?, global_ids: Arc::new(BTreeMap::from([(0, self.guid)])), }, ); @@ -411,22 +351,16 @@ fn ordinary_text<'a>( /// The left paragraph survives. Empty left text adopts the right text identity; /// otherwise the left text survives. The left paragraph's tags are retained. /// Right-side tags are removed from active text, including when the left text is empty. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct ParagraphJoin { +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct ParagraphJoin { left: ExGuid, right: ExGuid, author: String, } impl ParagraphJoin { - /// Ordered left and right text identities. - pub fn texts(&self) -> [ExGuid; 2] { - [self.left, self.right] - } - /// Select the preceding leaf paragraph's text and the following paragraph's text. - pub fn new(left: ExGuid, right: ExGuid, author: &str) -> Result { + pub(crate) fn new(left: ExGuid, right: ExGuid, author: &str) -> Result { if left == right || left.guid == [0; 16] || right.guid == [0; 16] || author.contains('\0') { return Err(invalid( "Choose two distinct text objects and an author name without NUL", @@ -439,19 +373,6 @@ impl ParagraphJoin { }) } - pub(crate) fn apply(&self, source: &[u8], space: ExGuid) -> Result, Error> { - if self.left == self.right - || self.left.guid == [0; 16] - || self.right.guid == [0; 16] - || self.author.contains('\0') - { - return Err(invalid( - "Choose two distinct text objects and an author name without NUL", - )); - } - crate::active::write(source, space, |active| self.changes(active)) - } - pub(crate) fn changes(&self, active: &ActivePage<'_>) -> Result { let pages = &active.pages; if pages.len() != 1 { @@ -672,7 +593,7 @@ impl ParagraphJoin { author, PropertyObject { jcid: 0x120001, - bytes: properties(&[(0x1c001d75, string(&self.author))])?, + bytes: properties(&crate::create::author_properties(&self.author))?, global_ids: Arc::new(BTreeMap::from([(0, author.guid)])), }, ); diff --git a/crates/onestore/src/protected/crypto.rs b/crates/onestore/src/protected/crypto.rs index fef881d6e82f13387a652a6e1af9fecaf23959e5..fa5863eab5211ac43249755892eac6b2997890b1 100644 --- a/crates/onestore/src/protected/crypto.rs +++ b/crates/onestore/src/protected/crypto.rs @@ -8,6 +8,7 @@ use zeroize::{Zeroize, Zeroizing}; const NS: &str = "http://schemas.microsoft.com/office/2006/encryption"; const PASSWORD_NS: &str = "http://schemas.microsoft.com/office/2006/keyEncryptor/password"; +#[derive(Clone)] pub(super) struct Key { value: Zeroizing<[u8; 16]>, file_iv: [u8; 16], diff --git a/crates/onestore/src/protected/mod.rs b/crates/onestore/src/protected/mod.rs index 24dee8209d7be545a26fb7990c69d923d4c33503..a2907077ff91a737b696a52eeb8fad2bb49397ac 100644 --- a/crates/onestore/src/protected/mod.rs +++ b/crates/onestore/src/protected/mod.rs @@ -7,7 +7,11 @@ mod crypto; -use crate::{ExGuid, FileDataReference, ObjectData, Reference, RevisionIndex, document::Document}; +use crate::{ + ExGuid, FileDataReference, ObjectData, Reference, RevisionIndex, Transaction, + document::Document, + op::{Edit, Op, OpError}, +}; use std::{ collections::{BTreeMap, BTreeSet}, fmt, @@ -107,7 +111,18 @@ pub struct UnlockedSection<'a> { impl<'a> UnlockedSection<'a> { /// Verifies the password and every labeled revision before exposing a view. /// Metadata and password input are each bounded to 64 KiB. - pub fn open(index: &'a RevisionIndex<'a>, password: &str, mut limits: Limits) -> Result { + pub fn open(index: &'a RevisionIndex<'a>, password: &str, limits: Limits) -> Result { + Self::unlock(index, limits, |metadata, rounds| { + crypto::Key::open(metadata, password, rounds) + }) + } + + /// `open` with each distinct encryption metadata's key from `key`. + fn unlock( + index: &'a RevisionIndex<'a>, + mut limits: Limits, + mut key: impl FnMut(&[u8], &mut u64) -> Result, + ) -> Result { if index.store.header.file_type != crate::FileType::Section { return Err(Error::Unsupported); } @@ -163,10 +178,7 @@ impl<'a> UnlockedSection<'a> { let metadata = metadata.ok_or_else(|| invalid("Protected object space has no revision"))?; if !keys.contains_key(metadata) { - keys.insert( - metadata, - crypto::Key::open(metadata, password, &mut limits.kdf_rounds)?, - ); + keys.insert(metadata, key(metadata, &mut limits.kdf_rounds)?); } let key = &keys[metadata]; for rid in space.labels.values().copied().collect::>() { @@ -294,8 +306,10 @@ impl UnlockedSection<'_> { use crate::write::{PropertyObject, RevisionEdit, append_revisions}; let copy = |space: ExGuid, revision: ExGuid| { let revision = self.resolve(space, revision)?; + // A revision still declares objects its edits detached; a new one holds live ones. + let live = revision.reachable()?; let mut objects = BTreeMap::new(); - for (id, object) in &revision.objects { + for (id, object) in revision.objects.iter().filter(|(id, _)| live.contains(id)) { let copy = match object.data { ObjectData::File { reference, @@ -390,60 +404,66 @@ impl UnlockedSection<'_> { } } -/// An edited page of a protected section as one stored revision per changed space, the -/// protected counterpart of [`crate::PreparedEdit::page`]. -pub(crate) fn write_page( - source: &[u8], - password: &str, - space: ExGuid, - page: &crate::page::Page, - author: &str, -) -> Result> { - let store = crate::Store::parse(source)?; - let index = RevisionIndex::parse(&store)?; - let unlocked = UnlockedSection::open(&index, password, Limits::default())?; - if unlocked.keys.len() != 1 { - return Err(Error::Unsupported); - } - let twin = unlocked.twin()?; - let applied = Zeroizing::new(crate::page::write::write_page(&twin, space, page, author)?); - let applied_store = crate::Store::parse(&applied)?; - let applied_index = RevisionIndex::parse(&applied_store)?; - let twin_store = crate::Store::parse(&twin)?; - if RevisionIndex::parse(&twin_store)? - .spaces - .keys() - .ne(applied_index.spaces.keys()) - { - return Err(invalid("Page edits cannot create object spaces")); - } - // The twin's scaffold spaces are not the section's. - let mut edited = Vec::new(); - for (sid, space) in &index.spaces { - let rid = applied_index.active(*sid)?; - if space.labels.get(&(ExGuid::default(), 1)) != Some(&rid) { - edited.push((*sid, applied_index.resolve(*sid, rid)?)); +impl UnlockedSection<'_> { + /// Applies `edit` to this section as `Section::apply` applies it to an ordinary one and + /// seals the result as one revision per changed space, stored under the section's key. + /// Page ops only: a protected section's page list is not edited. + pub fn apply(&self, author: &str, edit: &Edit) -> std::result::Result { + let failed = |error: crate::Error| OpError::Failed(error); + if self.keys.len() != 1 { + return Err(OpError::Unsupported( + "Only a section under one key takes edits", + )); + } + if edit.ops.iter().any(|op| matches!(op, Op::Section(_))) { + return Err(OpError::Unsupported("A protected section's page list is not edited")); } + let twin = self.twin().map_err(failed)?; + let arena = crate::Arena::default(); + let mut section = crate::Section::open(&arena, twin.to_vec()).map_err(failed)?; + section.apply(author, edit)?; + section.seal().map_err(failed)?; + let applied = Zeroizing::new(section.image()); + drop(section); + let applied_store = crate::Store::parse(&applied).map_err(failed)?; + let applied_index = RevisionIndex::parse(&applied_store).map_err(failed)?; + // The twin's scaffold spaces are not the section's. + let edited = self + .index + .spaces + .keys() + .map(|space| Ok((*space, applied_index.resolve_active(*space)?))) + .collect::, crate::Error>>() + .map_err(failed)?; + let edited: Vec<_> = edited.iter().map(|(space, after)| (*space, after)).collect(); + let payloads = crate::write::declared_payloads(&applied_store) + .into_iter() + .map(|guid| Ok((guid, applied_store.file_data(guid)?))) + .collect::, crate::Error>>() + .map_err(failed)?; + let transaction = crate::write::squash(self.index, &edited, &payloads, Some(self)) + .map_err(failed)? + .ok_or(OpError::Unsupported("The edit changes nothing"))?; + let written = crate::write::applied(self.index.store.data, Some(&transaction)) + .map_err(failed)?; + let store = crate::Store::parse(&written).map_err(failed)?; + let index = RevisionIndex::parse(&store).map_err(failed)?; + UnlockedSection::unlock(&index, Limits::default(), |metadata, _| { + self.keys.get(metadata).cloned().ok_or(Error::PasswordMismatch) + }) + .map_err(|error| match error { + Error::Invalid(error) => failed(error), + _ => failed(crate::Error { + offset: 0, + message: "The written section does not unlock under its key", + }), + })?; + Ok(transaction) } - let edited: Vec<_> = edited.iter().map(|(sid, after)| (*sid, after)).collect(); - let payloads = crate::page::write::declared_payloads(&applied_store) - .into_iter() - .map(|guid| Ok((guid, applied_store.file_data(guid)?))) - .collect::, crate::Error>>()?; - let written = crate::page::write::squash( - &index, - &edited, - &payloads, - &BTreeMap::new(), - Some(&unlocked), - )?; - let store = crate::Store::parse(&written)?; - UnlockedSection::open(&RevisionIndex::parse(&store)?, password, Limits::default())?; - Ok(written) } impl UnlockedSection<'_> { - /// The section key; `write_page` admits sections with exactly one. + /// The section key; `apply` admits sections with exactly one. fn key(&self) -> &crypto::Key { self.keys.values().next().expect("one section key") } diff --git a/crates/onestore/src/section.rs b/crates/onestore/src/section.rs index f0892fb238f7fc462d88948b5d5114e1d11da097..7588ac5631b6cd8d064121e160ff21d50d3af7e7 100644 --- a/crates/onestore/src/section.rs +++ b/crates/onestore/src/section.rs @@ -226,15 +226,108 @@ impl<'a> Section<'a> { } } + /// The conflict pages of each page that has them, in section order, each page's in the + /// order OneNote 2010 lists them under it: the last stored first. Read from the pages' + /// manifests and the conflict pages' metadata alone. + pub fn conflicts(&mut self) -> Result)>> { + let listed: Vec = self.pages()?.into_iter().map(|(space, ..)| space).collect(); + let element = |object: &crate::Object<'a>| { + crate::document::Element::parse_with(object, FileType::Section, &mut |_| { + Err(Error { + offset: 0, + message: "Page metadata holds no payload", + }) + }) + }; + let mut conflicts = Vec::new(); + for page in listed { + let revision = self.revision(page)?; + let Some(manifest) = revision + .roots + .get(&1) + .and_then(|id| revision.objects.get(id)) + else { + continue; + }; + let mut pages = Vec::new(); + for space in element(manifest)?.spaces.into_iter().rev() { + let Some(metadata) = self.revision(space).ok().and_then(|revision| { + revision + .roots + .get(&2) + .and_then(|id| revision.objects.get(id)) + }) else { + continue; + }; + let metadata = element(metadata)?; + let crate::document::Kind::ConflictMetadata { title, author, .. } = metadata.kind + else { + continue; + }; + let created = metadata.extra[0] + .iter() + .find_map(|field| match field.value { + crate::document::FieldValue::Bytes(bytes) if field.id == 0x18001c65 => { + bytes.try_into().ok().map(u64::from_le_bytes) + } + _ => None, + }); + let mut objects = Vec::new(); + for (id, object) in &self.revision(space)?.objects { + let ObjectData::Properties(bytes) = object.data else { + continue; + }; + if crate::PropertySets::parse(bytes)?.sets[0] + .iter() + .any(|property| property.id == 0x88001d96) + { + objects.push(*id); + } + } + pages.push(crate::ConflictPage { + space, + title: title.unwrap_or_default(), + user: author.unwrap_or_default(), + created, + objects, + }); + } + if !pages.is_empty() { + conflicts.push((page, pages)); + } + } + Ok(conflicts) + } + + /// The page series holding each listed page. Moving a page gives it a series of its + /// own, so a page in another series than before was moved. + pub fn series(&mut self) -> Result> { + let view = &self.active(self.root)?.view; + let section = view.roots.get(&1).and_then(|id| view.nodes.get(id)).ok_or(Error { + offset: 0, + message: "Section root is unavailable", + })?; + Ok(section + .children + .iter() + .filter_map(|series| Some((*series, view.nodes.get(series)?))) + .flat_map(|(series, node)| node.spaces.iter().map(move |page| (*page, series))) + .collect()) + } + /// Page spaces in section order with the title and outline level (1 at the top) the /// page list shows, read from each page's metadata alone. pub fn pages(&mut self) -> Result> { let root = self.root; let view = &self.active(root)?.view; - let section = view.roots.get(&1).and_then(|id| view.nodes.get(id)).ok_or(Error { - offset: 0, - message: "Section root is unavailable", - })?; + let section = view + .roots + .get(&1) + .and_then(|id| view.nodes.get(id)) + .ok_or(Error { + offset: 0, + message: "Section root is unavailable", + })?; let spaces: Vec = section .children .iter() @@ -253,7 +346,10 @@ impl<'a> Section<'a> { for space in spaces { let revision = self.revision(space)?; let (mut title, mut level) = (None, 1); - if let Some(metadata) = revision.roots.get(&2).and_then(|id| revision.objects.get(id)) + if let Some(metadata) = revision + .roots + .get(&2) + .and_then(|id| revision.objects.get(id)) && let crate::document::Kind::Metadata { title: stored, level: stored_level, @@ -263,7 +359,10 @@ impl<'a> Section<'a> { level = stored_level.unwrap_or(1); } if title.is_none() { - let manifest = revision.roots.get(&1).and_then(|id| revision.objects.get(id)); + let manifest = revision + .roots + .get(&1) + .and_then(|id| revision.objects.get(id)); if let Some(manifest) = manifest && let Some(page) = element(manifest)? .content @@ -695,6 +794,17 @@ mod tests { result } + /// The image the image writer leaves: the `edited` revisions squashed onto the source + /// `index` parsed. + fn squashed( + index: &RevisionIndex<'_>, + edited: &[(ExGuid, &ResolvedRevision<'_>)], + payloads: &[([u8; 16], &[u8])], + ) -> Vec { + let transaction = crate::write::squash(index, edited, payloads, None).unwrap(); + crate::write::applied(index.store.data, transaction.as_ref()).unwrap() + } + /// The page spaces of an image, fullest first. fn page_spaces(image: &[u8]) -> Vec { let store = Store::parse(image).unwrap(); @@ -729,65 +839,62 @@ mod tests { GUIDS.set(Some(1 << 62 | seed << 40)); let (mut sealed, mut checkpoints) = (0, 0); for batch in 0..batches { - let space = spaces[batch % spaces.len().min(pages)]; - let store = Store::parse(&image).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let bump = Bump::new(); - let mut legacy = ActivePage::parse(&index, space).unwrap(); - for _ in 0..1 + (writes.0 as usize >> 40) % largest { - let Ok(changes) = writes.next(section.active(space).unwrap()) else { - continue; - }; - let stored = legacy.write(&bump, &[], changes.clone()).unwrap(); - assert_eq!( - section.apply_changes(space, &[], changes).unwrap(), - stored, - "batch {batch}" - ); - } - let guids = (batch as u64 + 1) << 32; - let expected = seeded(guids, || { - crate::page::write::squash( - &index, - &[(space, &legacy.live.revision)], - &legacy.payloads, - &BTreeMap::new(), - None, - ) - .unwrap() - }); - let before = section.stamp().clone(); - let chains = depths(§ion); - let transaction = seeded(guids, || section.seal().unwrap()); - checkpoints += depths(§ion) - .iter() - .filter(|(id, depth)| chains.get(id).is_some_and(|before| before > depth)) - .count(); - let mut written = image.clone(); - if let Some(transaction) = &transaction { - assert_eq!(transaction.base, before); - transaction.apply(&mut written).unwrap(); - sealed += 1; - } - assert!(written == expected, "batch {batch}"); - assert_eq!(Stamp::of(&written).unwrap(), *section.stamp()); - drop(legacy); - drop(index); - drop(store); - image = expected; - if batch % 25 == 0 || batch + 1 == batches { - assert!(section.image() == image); + // The writers stamp modification times; a fixed clock keeps the batches alike. + let at = 134_000_000_000_000_000 + batch as u64 * 10_000_000; + crate::create::at(at, || { + let space = spaces[batch % spaces.len().min(pages)]; let store = Store::parse(&image).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); - index.validate_current().unwrap(); - let document = Document::parse(&index).unwrap(); - for space in &spaces { + let bump = Bump::new(); + let mut legacy = ActivePage::parse(&index, space).unwrap(); + for _ in 0..1 + (writes.0 as usize >> 40) % largest { + let Ok(changes) = writes.next(section.active(space).unwrap()) else { + continue; + }; + let stored = legacy.write(&bump, &[], changes.clone()).unwrap(); assert_eq!( - section.page(*space).unwrap(), - Page::from_space(&document, *space).unwrap() + section.apply_changes(space, &[], changes).unwrap(), + stored, + "batch {batch}" ); } - } + let guids = (batch as u64 + 1) << 32; + let expected = seeded(guids, || { + squashed(&index, &[(space, &legacy.live.revision)], &legacy.payloads) + }); + let before = section.stamp().clone(); + let chains = depths(§ion); + let transaction = seeded(guids, || section.seal().unwrap()); + checkpoints += depths(§ion) + .iter() + .filter(|(id, depth)| chains.get(id).is_some_and(|before| before > depth)) + .count(); + let mut written = image.clone(); + if let Some(transaction) = &transaction { + assert_eq!(transaction.base, before); + transaction.apply(&mut written).unwrap(); + sealed += 1; + } + assert!(written == expected, "batch {batch}"); + assert_eq!(Stamp::of(&written).unwrap(), *section.stamp()); + drop(legacy); + drop(index); + drop(store); + image = expected; + if batch % 25 == 0 || batch + 1 == batches { + assert!(section.image() == image); + let store = Store::parse(&image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + index.validate_current().unwrap(); + let document = Document::parse(&index).unwrap(); + for space in &spaces { + assert_eq!( + section.page(*space).unwrap(), + Page::from_space(&document, *space).unwrap() + ); + } + } + }); } GUIDS.set(None); assert!(sealed * 3 > batches, "{sealed} of {batches} batches sealed"); @@ -932,14 +1039,7 @@ mod tests { legacy.write(&bump, &[], changes).unwrap(); let expected = seeded(guids, || { let revision = &legacy.live.revision; - crate::page::write::squash( - &index, - &[(space, revision)], - &[], - &BTreeMap::new(), - None, - ) - .unwrap() + squashed(&index, &[(space, revision)], &[]) }); drop(legacy); drop(index); @@ -971,14 +1071,7 @@ mod tests { let legacy = ActivePage::parse(&index, space).unwrap(); let expected = seeded(seed, || { let revision = &legacy.live.revision; - crate::page::write::squash( - &index, - &[(space, revision)], - &[payload], - &BTreeMap::new(), - None, - ) - .unwrap() + squashed(&index, &[(space, revision)], &[payload]) }); let transaction = seeded(seed, || section.seal().unwrap()); assert_eq!(transaction.is_some(), expected != image); @@ -1036,8 +1129,12 @@ mod tests { && page.parents.contains_key(id) }) .find_map(|(outline, _)| { + let paragraph = crate::page::text::new_id().ok()?; + let text = crate::page::text::new_id().ok()?; Insertion::paragraph(*outline, None, "Tampered text", "Author") - .and_then(|insertion| insertion.changes(page)) + .and_then(|insertion| { + insertion.changes_as(page, paragraph, paragraph, text) + }) .ok() }) .unwrap(); @@ -1127,8 +1224,7 @@ mod probe { (space, texts[texts.len() / 2], texts.len()) } - /// One-character edits of a large page through the image writer and through a - /// `Section`: `SECTION_PROBE=path cargo test --release -p onestore --lib probe -- --ignored --nocapture`. + /// One-character edits of a large page through a `Section`: `SECTION_PROBE=path cargo test --release -p onestore --lib probe -- --ignored --nocapture`. #[test] #[ignore] fn keystrokes() { @@ -1140,57 +1236,6 @@ mod probe { image.len() ); - let mut samples = Vec::new(); - let mut written = 0; - for _ in 0..9 { - let start = Instant::now(); - let edit = crate::PreparedEdit::text(&image, space, text, 0..0, "x").unwrap(); - samples.push(start.elapsed()); - let transaction = edit.transaction(); - written = transaction.append.len() - + transaction - .patches - .iter() - .map(|(_, bytes)| bytes.len()) - .sum::(); - } - println!( - "image writer, one keystroke: {:?}, {written} bytes written", - median(samples) - ); - - let before = { - let store = Store::parse(&image).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - Page::from_space(&crate::document::Document::parse(&index).unwrap(), space).unwrap() - }; - let mut after = before.clone(); - let edited = after - .objects - .iter_mut() - .filter_map(|object| match object { - crate::page::PageObject::Outline(outline) => Some(outline), - _ => None, - }) - .flat_map(|outline| &mut outline.paragraphs) - .find_map(|paragraph| paragraph.text_mut().filter(|object| object.id == text)) - .unwrap(); - let format = edited.text.format_at(0).unwrap().clone(); - edited - .text - .apply(crate::page::text::Edit { - range: 0..0, - replacement: crate::page::Paragraph::new("x".into(), format), - }) - .unwrap(); - let mut samples = Vec::new(); - for _ in 0..9 { - let start = Instant::now(); - crate::PreparedEdit::page(&image, space, &after, "Probe").unwrap(); - samples.push(start.elapsed()); - } - println!("page-model writer, one keystroke: {:?}", median(samples)); - let arena = Arena::default(); let start = Instant::now(); let mut section = Section::open(&arena, image.clone()).unwrap(); diff --git a/crates/onestore/src/toc.rs b/crates/onestore/src/toc.rs index 68db7ee3ff4f7dfdcb6d6e08fc7346a4dbe1bdb7..72c120ec39e40ddfeb012d6d0edb60d9cff1eb15 100644 --- a/crates/onestore/src/toc.rs +++ b/crates/onestore/src/toc.rs @@ -1,14 +1,15 @@ //! Edits to a notebook's table of contents: the documented `jcidPersistablePropertyContainerForTOC` -//! root lists section and section-group entries, each carrying a file identity, an ordering -//! number, a filename and (for sections) a colour. +//! root carries the notebook's colour and lists section and section-group entries, each +//! carrying a file identity, an ordering number and a filename. A section's colour lives in +//! its own metadata (`op::SectionOp::Color`); its entry keeps 0xffffffff, as OneNote writes. use crate::{ - Error, ExGuid, PropertySets, Store, Value, + Error, ExGuid, PropertySets, Store, Transaction, Value, document::{Document, Kind}, revisions::RevisionIndex, - write::{PropertyObject, fresh_guid, write_revision}, + write::{PropertyObject, RevisionEdit, applied, build_on, check, fresh_guid}, }; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; fn invalid(message: &'static str) -> Error { Error { offset: 0, message } @@ -27,11 +28,8 @@ pub enum TocEdit { identity: [u8; 16], filename: String, }, - /// Section colour as COLORREF; `None` restores OneNote's "undefined" 0xffffffff. - Color { - identity: [u8; 16], - color: Option, - }, + /// The notebook's colour as COLORREF. + Color(u32), /// Every entry, in the wanted order; entries left out keep their relative order after these. Order(Vec<[u8; 16]>), Remove { @@ -43,15 +41,19 @@ fn component(name: &str) -> bool { !name.is_empty() && !name.contains(['/', '\\', '\0']) && name != "." && name != ".." } -/// The table of contents with `edits` applied in order. -type PropertyChange = (u32, Option>); - -pub(crate) fn edit_table_of_contents(source: &[u8], edits: &[TocEdit]) -> Result, Error> { +/// Applies table-of-contents edits (sections and section groups: add, rename, order, +/// remove; the notebook's colour) in order as one revision of a `.onetoc2` file; none when they change +/// nothing. +pub fn edit_table_of_contents( + source: &[u8], + edits: &[TocEdit], +) -> Result, Error> { let store = Store::parse(source)?; if store.header.file_type != crate::FileType::TableOfContents { return Err(invalid("Choose a table-of-contents file")); } let index = RevisionIndex::parse(&store)?; + index.validate_current()?; let document = Document::parse(&index)?; let space = document.root; let revision = document.active(space)?; @@ -76,8 +78,8 @@ pub(crate) fn edit_table_of_contents(source: &[u8], edits: &[TocEdit]) -> Result listed.push((*id, *identity, filename.clone().unwrap_or_default())); } let mut created: BTreeMap = BTreeMap::new(); - // Property values to set (or remove, `None`) on each existing entry. - let mut changes: BTreeMap> = BTreeMap::new(); + let mut renamed = BTreeSet::new(); + let mut color = None; for edit in edits { let position = |identity: &[u8; 16]| { listed @@ -143,18 +145,9 @@ pub(crate) fn edit_table_of_contents(source: &[u8], edits: &[TocEdit]) -> Result } let id = listed[at].0; listed[at].2 = filename.clone(); - changes - .entry(id) - .or_default() - .push((0x1c001d6b, Some(crate::create::string(filename)))); - } - TocEdit::Color { identity, color } => { - let id = listed[position(identity)?].0; - changes.entry(id).or_default().push(( - 0x14001cbe, - Some(color.unwrap_or(0xffff_ffff).to_le_bytes().to_vec()), - )); + renamed.insert(id); } + TocEdit::Color(value) => color = Some(*value), TocEdit::Order(wanted) => { let mut ordered: Vec<(ExGuid, [u8; 16], String)> = Vec::new(); for identity in wanted { @@ -175,17 +168,17 @@ pub(crate) fn edit_table_of_contents(source: &[u8], edits: &[TocEdit]) -> Result TocEdit::Remove { identity } => { let at = position(identity)?; let (id, _, _) = listed.remove(at); - changes.remove(&id); created.remove(&id); } } } let listed = listed; - write_revision(source, space, |raw| { + let transaction = build_on(&index, &[], None, |index| { + let raw = index.resolve_active(space)?; let mut changed = BTreeMap::new(); let mut root_object = PropertyObject::from_object(&raw.objects[&root])?; let mut references = Vec::new(); - for (order, (id, _, _)) in listed.iter().enumerate() { + for (order, (id, _, filename)) in listed.iter().enumerate() { let mut object = match created.remove(id) { Some(object) => object, None => PropertyObject::from_object(&raw.objects[id])?, @@ -201,11 +194,8 @@ pub(crate) fn edit_table_of_contents(source: &[u8], edits: &[TocEdit]) -> Result if stored_order != Some(order as u32 + 1) { updates.push((0x14001cb9, (order as u32 + 1).to_le_bytes().to_vec())); } - for (property, value) in changes.get(id).into_iter().flatten() { - match value { - Some(bytes) => updates.push((*property, bytes.clone())), - None => object.remove(&[*property])?, - } + if renamed.contains(id) { + updates.push((0x1c001d6b, crate::create::string(filename))); } let updates: Vec<(u32, &[u8])> = updates .iter() @@ -220,7 +210,12 @@ pub(crate) fn edit_table_of_contents(source: &[u8], edits: &[TocEdit]) -> Result } } root_object.set(&[(0x24001cf6, &references)])?; + if let Some(color) = color { + root_object.set(&[(0x14001cbe, &color.to_le_bytes())])?; + } changed.insert(root, root_object); - Ok(changed) - }) + Ok(BTreeMap::from([(space, RevisionEdit::Update(changed))])) + })?; + check(&applied(source, transaction.as_ref())?, true)?; + Ok(transaction) } diff --git a/crates/onestore/src/tree.rs b/crates/onestore/src/tree.rs index 577dc9d74f9959421e2ff32b6ed61e1c320b87ad..8b1d66d7c86f9008fecbb7e207c64894fda92e64 100644 --- a/crates/onestore/src/tree.rs +++ b/crates/onestore/src/tree.rs @@ -1,12 +1,11 @@ use crate::{ Error, ExGuid, active::{ActivePage, Changes}, - create::{current_timestamps, properties, string}, + create::{current_timestamps, properties}, document::{Kind, Revision}, edit::{editable_parents, update_title}, write::{PropertyObject, fresh_guid}, }; -use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, sync::Arc, @@ -16,8 +15,7 @@ fn invalid(message: &'static str) -> Error { Error { offset: 0, message } } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, Clone, PartialEq)] enum Placement { Delete, Move { @@ -27,40 +25,25 @@ enum Placement { } /// An atomic subtree move or deletion on one active page. -/// Retain the intent across retries so an emptied cell's replacement keeps its identity. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct TreeEdit { +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct TreeEdit { guid: [u8; 16], object: ExGuid, placement: Placement, author: String, - created: u32, } impl TreeEdit { - pub fn object(&self) -> ExGuid { - self.object - } - - /// Move destination and direct sibling anchor; deletion has no destination. - pub fn destination(&self) -> Option<(ExGuid, Option)> { - match self.placement { - Placement::Delete => None, - Placement::Move { parent, before } => Some((parent, before)), - } - } - /// Removes a paragraph or ordinary outline and its descendants from the active tree. - /// Historical objects remain available; an emptied table cell receives an empty paragraph. - pub fn delete(object: ExGuid, author: &str) -> Result { + /// Historical objects remain available. + pub(crate) fn delete(object: ExGuid, author: &str) -> Result { Self::new(object, Placement::Delete, author) } /// Moves before a direct child, or appends when `before` is None. /// Paragraph destinations are outlines, groups, paragraphs or cells on the same page. /// Outlines remain direct page children. Content and explicit list formatting are preserved. - pub fn move_to( + pub(crate) fn move_to( object: ExGuid, parent: ExGuid, before: Option, @@ -75,7 +58,6 @@ impl TreeEdit { object, placement, author: author.to_owned(), - created: current_timestamps()?.0, }; intent.validate()?; Ok(intent) @@ -95,11 +77,6 @@ impl TreeEdit { Ok(()) } - pub(crate) fn apply(&self, source: &[u8], space: ExGuid) -> Result, Error> { - self.validate()?; - crate::active::write(source, space, |active| self.changes(active)) - } - pub(crate) fn changes(&self, active: &ActivePage<'_>) -> Result { let pages = &active.pages; let [page] = pages.as_slice() else { @@ -164,7 +141,6 @@ impl TreeEdit { .retain(|id| *id != self.object); } - let mut empty_cell = None; let mut at = source_parent; loop { let node = &view.nodes[&at]; @@ -174,8 +150,8 @@ impl TreeEdit { break; } if node.children.is_empty() { + // `Section::apply` refuses an edit that ends with the cell still empty. if matches!(node.kind, Kind::Cell { .. }) { - empty_cell = Some(at); break; } if !matches!(node.kind, Kind::Outline { .. } | Kind::OutlineGroup) { @@ -278,53 +254,14 @@ impl TreeEdit { guid: self.guid, n: 3, }; - if let Some(cell) = empty_cell { - let paragraph = ExGuid { - guid: self.guid, - n: 1, - }; - let text = ExGuid { - guid: self.guid, - n: 2, - }; - view.nodes.get_mut(&cell).unwrap().children.push(paragraph); - for (id, jcid, values) in [ - ( - paragraph, - 0x6000d, - vec![ - (0x24001c1f, 2_u32.to_le_bytes().to_vec()), - (0x0c001c03, vec![1]), - (0x14001d09, self.created.to_le_bytes().to_vec()), - (0x14001d7a, modified.to_vec()), - (0x20001d78, 3_u32.to_le_bytes().to_vec()), - (0x20001d79, 3_u32.to_le_bytes().to_vec()), - ], - ), - ( - text, - 0x6000e, - vec![(0x1c001c22, Vec::new()), (0x14001d7a, modified.to_vec())], - ), - ] { - changed.insert( - id, - PropertyObject { - jcid, - bytes: properties(&values)?, - global_ids: Arc::new(BTreeMap::from([(0, self.guid)])), - }, - ); - } - } let moved_paragraph = matches!(self.placement, Placement::Move { .. }) && matches!(view.nodes[&self.object].kind, Kind::Paragraph { .. }); - if empty_cell.is_some() || moved_paragraph { + if moved_paragraph { changed.insert( author_id, PropertyObject { jcid: 0x120001, - bytes: properties(&[(0x1c001d75, string(&self.author))])?, + bytes: properties(&crate::create::author_properties(&self.author))?, global_ids: Arc::new(BTreeMap::from([(0, self.guid)])), }, ); @@ -413,47 +350,15 @@ fn checked_path( #[cfg(test)] mod tests { use super::*; - use crate::write::write_revision; - use crate::{Insertion, PreparedEdit, RevisionIndex, Store, document::Document}; - - #[test] - fn a_retained_move_intent_can_reuse_its_immutable_author_after_another_move() { - let source = crate::create_section("move.one", "First", "Author").unwrap(); - let store = Store::parse(&source).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let document = Document::parse(&index).unwrap(); - let (sid, page) = document.pages().unwrap()[0]; - let view = document.active(sid).unwrap(); - let outline = *view.nodes[&page] - .children - .iter() - .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) - .unwrap(); - let first = view.nodes[&outline].children[0]; - let second = Insertion::paragraph(outline, None, "Second", "Author").unwrap(); - let source = PreparedEdit::insert(&source, sid, &second) - .unwrap() - .as_bytes() - .to_vec(); - let retained = TreeEdit::move_to(first, outline, None, "Tree author").unwrap(); - let moved = PreparedEdit::tree(&source, sid, &retained).unwrap(); - let reversed = - TreeEdit::move_to(first, outline, Some(second.object()), "Tree author").unwrap(); - let reversed = PreparedEdit::tree(moved.as_bytes(), sid, &reversed).unwrap(); - let repeated = PreparedEdit::tree(reversed.as_bytes(), sid, &retained).unwrap(); - let store = Store::parse(repeated.as_bytes()).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let document = Document::parse(&index).unwrap(); - let view = document.active(sid).unwrap(); - assert_eq!(view.nodes[&outline].children, [second.object(), first]); - assert_eq!( - view.nodes[&first].latest_author, - Some(ExGuid { - guid: retained.guid, - n: 3 - }) - ); - } + use crate::{ + RevisionIndex, Store, + document::{Document, Format}, + op::{ + PageOp, + tests::{edited, text_paragraph}, + }, + write::write_revision, + }; #[test] fn group_normalization_preserves_unequal_indentation_and_overlapping_moves() { @@ -469,22 +374,23 @@ mod tests { .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) .unwrap(); let first = view.nodes[&outline].children[0]; - let second = Insertion::paragraph(outline, None, "Second", "Author").unwrap(); - let third = Insertion::paragraph(outline, None, "Third", "Author").unwrap(); - let source = PreparedEdit::insert(&source, sid, &second) - .unwrap() - .as_bytes() - .to_vec(); - let source = PreparedEdit::insert(&source, sid, &third) - .unwrap() - .as_bytes() - .to_vec(); + let paragraphs = vec![ + text_paragraph("Second", Format::default()), + text_paragraph("Third", Format::default()), + ]; + let (second, third) = (paragraphs[0].id, paragraphs[1].id); + let insert = PageOp::Insert { + container: outline, + before: None, + paragraphs, + }; + let source = edited(&source, sid, vec![insert]).unwrap(); let guid = fresh_guid().unwrap(); let a = ExGuid { guid, n: 1 }; let b = ExGuid { guid, n: 2 }; let source = write_revision(&source, sid, |raw| { let mut changed = BTreeMap::new(); - for (id, child, level) in [(a, first, 2), (b, second.object(), 1)] { + for (id, child, level) in [(a, first, 2), (b, second, 1)] { let mut group = PropertyObject { jcid: 0x60019, bytes: properties(&[(0x0c001c03, vec![level])])?, @@ -496,7 +402,7 @@ mod tests { } let mut object = PropertyObject::from_object(&raw.objects[&outline])?; let mut references = Vec::new(); - for id in [a, b, third.object()] { + for id in [a, b, third] { references.extend_from_slice(&object.reference(id)?); } object.set(&[(0x24001c20, &references)])?; @@ -504,38 +410,37 @@ mod tests { Ok(changed) }) .unwrap(); - for moving in [false, true] { - let intent = if moving { - TreeEdit::move_to(third.object(), a, Some(first), "Author") - } else { - TreeEdit::delete(third.object(), "Author") - } - .unwrap(); - let edit = PreparedEdit::tree(&source, sid, &intent).unwrap(); - let store = Store::parse(edit.as_bytes()).unwrap(); + let page = |image: &[u8]| { + let store = Store::parse(image).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); index.validate_current().unwrap(); - let document = Document::parse(&index).unwrap(); - let view = document.active(sid).unwrap(); - assert_eq!(view.nodes[&outline].children, [a, second.object()]); - assert_eq!(view.nodes[&outline].child_level, Some(2)); - assert_eq!(view.nodes[&a].child_level, Some(1)); - assert_eq!( - view.nodes[&a].children, - if moving { - vec![third.object(), first] - } else { - vec![first] - } - ); - if !moving && let Some(output) = std::env::var_os("ONESTORE_TREE_OUTPUT") { - let output = std::path::PathBuf::from(output).join("unequal-groups"); - assert!(output.is_absolute()); - std::fs::create_dir_all(output.parent().unwrap()).unwrap(); - std::fs::create_dir(&output).unwrap(); - std::fs::write(output.join("groups.one"), edit.as_bytes()).unwrap(); - } + crate::page::Page::from_space(&Document::parse(&index).unwrap(), sid).unwrap() + }; + let written = edited(&source, sid, vec![PageOp::Delete { object: third }]).unwrap(); + let store = Store::parse(&written).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let view = document.active(sid).unwrap(); + assert_eq!(view.nodes[&outline].children, [a, second]); + assert_eq!(view.nodes[&outline].child_level, Some(2)); + assert_eq!(view.nodes[&a].child_level, Some(1)); + assert_eq!(view.nodes[&a].children, [first]); + if let Some(output) = std::env::var_os("ONESTORE_TREE_OUTPUT") { + let output = std::path::PathBuf::from(output).join("unequal-groups"); + assert!(output.is_absolute()); + std::fs::create_dir_all(output.parent().unwrap()).unwrap(); + std::fs::create_dir(&output).unwrap(); + std::fs::write(output.join("groups.one"), &written).unwrap(); } + // A move keeps the paragraph's level; the writers regroup the outline around it. + let moved = PageOp::Move { + object: third, + parent: Some(outline), + before: Some(first), + }; + let mut predicted = page(&source); + crate::op::predict(&mut predicted, &moved).unwrap(); + assert_eq!(page(&edited(&source, sid, vec![moved]).unwrap()), predicted); } #[test] @@ -553,7 +458,9 @@ mod tests { .unwrap(); let paragraph = view.nodes[&outline].children[0]; let text = view.nodes[¶graph].content[0]; - let deletion = TreeEdit::delete(paragraph, "Author").unwrap(); + let deleted = |bytes: &[u8]| { + edited(bytes, sid, vec![PageOp::Delete { object: paragraph }]).is_ok() + }; for target in [page, outline, paragraph, text] { for property in [0x08001cde, 0x08001cb4, 0x08001cf9, 0x08001cb2] { for enabled in [false, true] { @@ -563,11 +470,7 @@ mod tests { Ok(BTreeMap::from([(target, object)])) }) .unwrap(); - assert_eq!( - PreparedEdit::tree(&bytes, sid, &deletion).is_err(), - enabled, - "{target}: {property:#x}" - ); + assert_eq!(deleted(&bytes), !enabled, "{target}: {property:#x}"); } } } @@ -578,7 +481,7 @@ mod tests { Ok(BTreeMap::from([(protected, object)])) }) .unwrap(); - assert!(PreparedEdit::tree(&bytes, sid, &deletion).is_err()); + assert!(!deleted(&bytes)); } let bytes = write_revision(&source, sid, |raw| { let mut object = PropertyObject::from_object(&raw.objects[&outline])?; @@ -587,10 +490,6 @@ mod tests { Ok(BTreeMap::from([(outline, object)])) }) .unwrap(); - assert!(PreparedEdit::tree(&bytes, sid, &deletion).is_err()); - let mut value = serde_json::to_value(&deletion).unwrap(); - value["guid"] = serde_json::to_value([0_u8; 16]).unwrap(); - let restored = serde_json::from_value(value).unwrap(); - assert!(PreparedEdit::tree(&source, sid, &restored).is_err()); + assert!(!deleted(&bytes)); } } diff --git a/crates/onestore/src/write.rs b/crates/onestore/src/write.rs index 69f67f124e1242bb8fb39f3616abee8fecbca39b..fd5d62e231a8aa97f353d618bf16c049e483de34 100644 --- a/crates/onestore/src/write.rs +++ b/crates/onestore/src/write.rs @@ -323,45 +323,6 @@ pub(crate) struct ObjectEdit<'a> { pub inserts: &'a [(u32, &'a [u8])], } -/// Replaces a root-level scalar byte property in the default active revision. -/// The caller supplies its encoded value and is responsible for MS-ONE semantics. -/// Returns a complete file image without I/O; use `commit_file_property` to update an existing file. -pub fn replace_property_bytes( - source: &[u8], - space: ExGuid, - object_id: ExGuid, - property: u32, - value: &[u8], -) -> Result> { - if !(3..=7).contains(&((property >> 26) & 31)) { - return Err(Error { - offset: 0, - message: "Property does not contain scalar bytes", - }); - } - let store = Store::parse(source)?; - let index = RevisionIndex::parse(&store)?; - index.validate_current()?; - replace_objects( - &index, - space, - &[ObjectEdit { - object: object_id, - updates: &[(property, value)], - inserts: &[], - }], - ) -} - -/// Patches objects of a source the caller has parsed and validated. -pub(crate) fn replace_objects( - index: &RevisionIndex<'_>, - space: ExGuid, - edits: &[ObjectEdit<'_>], -) -> Result> { - write_revision_on(index, space, |revision| patched(revision, edits)) -} - /// Objects of `revision` with `edits` applied. pub(crate) fn patched( revision: &crate::ResolvedRevision<'_>, @@ -511,6 +472,7 @@ pub(crate) struct PropertyObject { pub(crate) enum RevisionEdit { Update(BTreeMap), + #[cfg(any(test, feature = "protected"))] Create { roots: BTreeMap, objects: BTreeMap, @@ -1269,24 +1231,18 @@ pub(crate) fn chain_depth(index: &RevisionIndex<'_>, space: ExGuid, rid: ExGuid) .count() } +/// A test fixture: `source` with one revision of `space` storing `edit`'s objects. +#[cfg(test)] pub(crate) fn write_revision( source: &[u8], space: ExGuid, edit: impl FnOnce(&crate::ResolvedRevision<'_>) -> Result>, ) -> Result> { - write_revision_with_payloads(source, space, &[], edit) -} - -pub(crate) fn write_revision_with_payloads( - source: &[u8], - space: ExGuid, - payloads: &[([u8; 16], &[u8])], - edit: impl FnOnce(&crate::ResolvedRevision<'_>) -> Result>, -) -> Result> { - write_revisions_with_payloads(source, payloads, update(space, edit)) + write_revisions(source, update(space, edit)) } /// One space's active revision edited into its update. +#[cfg(test)] fn update( space: ExGuid, edit: impl FnOnce(&crate::ResolvedRevision<'_>) -> Result>, @@ -1300,26 +1256,30 @@ fn update( } } +/// A test fixture: the transaction appending `edit`'s revisions to `source`. +#[cfg(test)] +pub(crate) fn revisions( + source: &[u8], + edit: impl FnOnce(&RevisionIndex<'_>) -> Result>, +) -> Result> { + let store = Store::parse(source)?; + let index = RevisionIndex::parse(&store)?; + index.validate_current()?; + build_on(&index, &[], None, edit) +} + +/// A test fixture: `source` with `edit`'s revisions appended. +#[cfg(test)] pub(crate) fn write_revisions( source: &[u8], edit: impl FnOnce(&RevisionIndex<'_>) -> Result>, ) -> Result> { - write_revisions_with_payloads(source, &[], edit) + publish(source, &[], None, true, edit) } -/// `write_revisions` that also stores embedded payloads: each becomes a file-data store -/// object referenced from the root file node list under its identity, as OneNote embeds -/// pictures and attachments. -pub(crate) fn write_revisions_with_payloads( - source: &[u8], - payloads: &[([u8; 16], &[u8])], - edit: impl FnOnce(&RevisionIndex<'_>) -> Result>, -) -> Result> { - publish(source, payloads, None, true, edit) -} - -/// `write_revisions_with_payloads` without validating that current revisions are -/// complete: a protected section is validated by unlocking it, through `protection`. +/// `source` with `edit`'s revisions and the `payloads` appended, without validating that +/// current revisions are complete: a protected section is validated by unlocking it, +/// through `protection`. #[cfg(feature = "protected")] pub(crate) fn append_revisions( source: &[u8], @@ -1330,6 +1290,7 @@ pub(crate) fn append_revisions( publish(source, payloads, protection, false, edit) } +#[cfg(any(test, feature = "protected"))] fn publish( source: &[u8], payloads: &[([u8; 16], &[u8])], @@ -1355,7 +1316,8 @@ pub(crate) fn check(output: &[u8], validate: bool) -> Result<()> { /// The written image, unchecked: `check` follows once the caller has released whatever /// `edit` borrowed. -pub(crate) fn build( +#[cfg(any(test, feature = "protected"))] +fn build( source: &[u8], payloads: &[([u8; 16], &[u8])], protection: Option<&dyn Protection>, @@ -1367,16 +1329,20 @@ pub(crate) fn build( if validate { index.validate_current()?; } - build_on(&index, payloads, protection, edit) + applied(source, build_on(&index, payloads, protection, edit)?.as_ref()) } /// `write_revision` on a source the caller has parsed and validated. +#[cfg(test)] pub(crate) fn write_revision_on( index: &RevisionIndex<'_>, space: ExGuid, edit: impl FnOnce(&crate::ResolvedRevision<'_>) -> Result>, ) -> Result> { - let output = build_on(index, &[], None, update(space, edit))?; + let output = applied( + index.store.data, + build_on(index, &[], None, update(space, edit))?.as_ref(), + )?; check(&output, true)?; Ok(output) } @@ -1387,12 +1353,14 @@ thread_local! { pub(crate) static BUILDS: std::cell::Cell = const { std::cell::Cell::new(0) }; } +/// The transaction appending `edit`'s revisions and the `payloads` to the parsed store; +/// none when nothing changes. pub(crate) fn build_on( index: &RevisionIndex<'_>, payloads: &[([u8; 16], &[u8])], protection: Option<&dyn Protection>, edit: impl FnOnce(&RevisionIndex<'_>) -> Result>, -) -> Result> { +) -> Result> { #[cfg(test)] BUILDS.with(|builds| builds.set(builds.get() + 1)); let store = index.store; @@ -1411,13 +1379,13 @@ pub(crate) fn build_on( let changes = edit(index)?; let mut appending = Appending::new(store.state()?); for (space, change) in changes { - let new_space = matches!(change, RevisionEdit::Create { .. }); let current = (ExGuid::default(), 1_u32); - let (rid, label, revision, replacements) = match change { + let (rid, label, revision, replacements, new_space) = match change { RevisionEdit::Update(objects) => { let rid = index.active(space)?; - (Some(rid), current, resolve(space, rid)?, objects) + (Some(rid), current, resolve(space, rid)?, objects, false) } + #[cfg(any(test, feature = "protected"))] RevisionEdit::Create { roots, objects } => { if !is_section || space.guid == [0; 16] || index.spaces.contains_key(&space) { return Err(Error { @@ -1439,6 +1407,7 @@ pub(crate) fn build_on( objects: BTreeMap::new(), }, objects, + true, ) } #[cfg(feature = "protected")] @@ -1462,6 +1431,7 @@ pub(crate) fn build_on( objects: BTreeMap::new(), }, objects, + false, ) } }; @@ -1525,13 +1495,78 @@ pub(crate) fn build_on( )?; } appending.payloads(payloads, protection)?; - Ok(match appending.finish()? { - Some((transaction, _)) => { - let mut output = source.to_vec(); - transaction.apply(&mut output)?; - output + Ok(appending.finish()?.map(|(transaction, _)| transaction)) +} + +/// `source` with `transaction` applied. +pub(crate) fn applied(source: &[u8], transaction: Option<&crate::Transaction>) -> Result> { + let mut output = source.to_vec(); + if let Some(transaction) = transaction { + transaction.apply(&mut output)?; + } + Ok(output) +} + +/// Payload identities the file-data store of `store` declares, in order. +#[cfg(any(test, feature = "protected"))] +pub(crate) fn declared_payloads(store: &Store<'_>) -> Vec<[u8; 16]> { + store + .lists + .values() + .flat_map(|list| &list.nodes) + .filter(|node| node.id == 0x94) + .filter_map(|node| node.payload.get(..16).and_then(|g| g.try_into().ok())) + .collect() +} + +/// Writes the live objects of each `edited` revision that differ from its space's active +/// revision in the validated `source` as one revision per space, embedding the `payloads` +/// it lacks. A protected `source` takes the revisions its plaintext twin gained. +#[cfg(any(test, feature = "protected"))] +pub(crate) fn squash( + source: &RevisionIndex<'_>, + edited: &[(ExGuid, &crate::ResolvedRevision<'_>)], + payloads: &[([u8; 16], &[u8])], + protection: Option<&dyn Protection>, +) -> Result> { + let existing = declared_payloads(source.store); + let payloads: Vec<_> = payloads + .iter() + .filter(|(guid, _)| !existing.contains(guid)) + .copied() + .collect(); + build_on(source, &payloads, protection, |index| { + let mut changes = BTreeMap::new(); + for (space, after) in edited { + let before = match protection { + Some(protection) => protection.resolve(*space, index.active(*space)?)?, + None => index.resolve_active(*space)?, + }; + if before.roots != after.roots { + return Err(Error { + offset: 0, + message: "Page edits cannot change revision roots", + }); + } + // Retired styles and deleted content stay in history; only live objects are written. + let live = after.reachable()?; + let mut changed = BTreeMap::new(); + for (id, object) in &after.objects { + if !live.contains(id) + || before + .objects + .get(id) + .map(|previous| unchanged(previous, object)) + .transpose()? + == Some(true) + { + continue; + } + changed.insert(*id, replacement(*id, object)?); + } + changes.insert(*space, RevisionEdit::Update(changed)); } - None => source.to_vec(), + Ok(changes) }) } diff --git a/crates/onestore/src/write/tests.rs b/crates/onestore/src/write/tests.rs index a187c505bfb35df2aefbc374f5d52c877210520f..7f9f138491c89743808db459b9c598f3615d9dfa 100644 --- a/crates/onestore/src/write/tests.rs +++ b/crates/onestore/src/write/tests.rs @@ -72,7 +72,15 @@ fn copying_reference_arrays_remaps_every_entry_in_each_stream() { #[test] fn document_insertions_and_formatting_respect_readonly_ancestors() { use super::{PropertyObject, write_revision}; - use crate::{ExGuid, Insertion, PreparedEdit, RevisionIndex, Store}; + use crate::{ + ExGuid, RevisionIndex, Store, TextAttribute, + document::{Format, Layout}, + op::{ + PageOp, + tests::{edited, text_paragraph}, + }, + page::{Outline, PageObject, text::new_id}, + }; use std::collections::BTreeMap; let source = crate::create_section("readonly.one", "Original", "Author").unwrap(); let store = Store::parse(&source).unwrap(); @@ -97,9 +105,23 @@ fn document_insertions_and_formatting_respect_readonly_ancestors() { )) }) .unwrap(); - let right = Insertion::paragraph(outline, None, "Right", "Author").unwrap(); - let expanded = PreparedEdit::insert(&source, sid, &right).unwrap(); - let source = expanded.as_bytes(); + let right = text_paragraph("Right", Format::default()); + let right_text = right.text().unwrap().id; + let insert = |container, paragraph| PageOp::Insert { + container, + before: None, + paragraphs: vec![paragraph], + }; + let expanded = edited(&source, sid, vec![insert(outline, right.clone())]).unwrap(); + let source = expanded.as_slice(); + let raw = index + .resolve(sid, index.spaces[&sid].labels[&(ExGuid::default(), 1)]) + .unwrap(); + let text = raw + .objects + .iter() + .find_map(|(id, object)| (object.jcid == 0x6000e).then_some(*id)) + .unwrap(); for blocked in [page, outline, paragraph] { let protected = write_revision(source, sid, |raw| { let mut object = PropertyObject::from_object(&raw.objects[&blocked])?; @@ -107,47 +129,55 @@ fn document_insertions_and_formatting_respect_readonly_ancestors() { Ok(BTreeMap::from([(blocked, object)])) }) .unwrap(); - let child = Insertion::paragraph(paragraph, None, "Nested", "Author").unwrap(); - assert!(PreparedEdit::insert(&protected, sid, &child).is_err()); - let raw = index - .resolve(sid, index.spaces[&sid].labels[&(ExGuid::default(), 1)]) - .unwrap(); - let text = raw - .objects - .iter() - .find_map(|(id, object)| (object.jcid == 0x6000e).then_some(*id)) - .unwrap(); - let split = crate::ParagraphSplit::new(text, 1, "Author").unwrap(); - assert!(PreparedEdit::split(&protected, sid, &split).is_err()); - let join = crate::ParagraphJoin::new(text, right.text_object(), "Author").unwrap(); - assert!(PreparedEdit::join(&protected, sid, &join).is_err()); - assert!( - PreparedEdit::format( - &protected, - sid, - text, - 1..3, - &[crate::TextAttribute::Bold(true)] - ) - .is_err() - ); + let refused = |op| edited(&protected, sid, vec![op]).is_err(); + assert!(refused(insert(paragraph, text_paragraph("Nested", Format::default())))); + assert!(refused(PageOp::Split { + text, + at: 1, + paragraph: new_id().unwrap(), + right: new_id().unwrap(), + lists: Vec::new(), + })); + assert!(refused(PageOp::Join { + left: text, + right: right_text, + })); + assert!(refused(PageOp::Format { + text, + range: 1..3, + set: vec![TextAttribute::Bold(true)], + clear: Vec::new(), + })); if blocked == page { - let outline = Insertion::outline(page, 36.0, 36.0, "Outline", "Author").unwrap(); - assert!(PreparedEdit::insert(&protected, sid, &outline).is_err()); + assert!(refused(PageOp::Add { + object: PageObject::Outline(Outline { + id: new_id().unwrap(), + title: false, + min_width: None, + layout: Layout { + x: Some(36.0), + y: Some(36.0), + ..Default::default() + }, + indents: Vec::new(), + paragraphs: vec![text_paragraph("Outline", Format::default())], + unsupported: Vec::new(), + }), + before: None, + })); } } let protected = write_revision(source, sid, |raw| { - let mut object = PropertyObject::from_object(&raw.objects[&right.object()])?; + let mut object = PropertyObject::from_object(&raw.objects[&right.id])?; object.set(&[(0x88001cde, &[])])?; - Ok(BTreeMap::from([(right.object(), object)])) + Ok(BTreeMap::from([(right.id, object)])) }) .unwrap(); - let document = crate::document::Document::parse(&index).unwrap(); - let space = &document.spaces[&sid]; - let view = space.active().unwrap(); - let text = view.nodes[¶graph].content[0]; - let join = crate::ParagraphJoin::new(text, right.text_object(), "Author").unwrap(); - assert!(PreparedEdit::join(&protected, sid, &join).is_err()); + let join = PageOp::Join { + left: text, + right: right_text, + }; + assert!(edited(&protected, sid, vec![join]).is_err()); } fn add_paragraph(source: &[u8], number: u32) -> Vec { @@ -774,8 +804,9 @@ fn invalid_property_splices_are_rejected() { fn character_formatting_preserves_inheritance_and_associated_data() { use super::{PropertyObject, write_revision}; use crate::{ - ExGuid, PreparedEdit, RevisionIndex, Store, TextAttribute, + ExGuid, RevisionIndex, Store, TextAttribute, document::{Document, Kind}, + op::{PageOp, tests::edited}, }; use std::collections::BTreeMap; let source = crate::create_section("inherited.one", "abcdef", "Author").unwrap(); @@ -858,13 +889,29 @@ fn character_formatting_preserves_inheritance_and_associated_data() { Ok(BTreeMap::from([(text, target)])) }) .unwrap(); + let text_edit = |range, with: &str| { + let with = with.to_owned(); + edited(&fixture, sid, vec![PageOp::Text { text, range, with }]) + }; + let bold = |range| { + edited( + &fixture, + sid, + vec![PageOp::Format { + text, + range, + set: vec![TextAttribute::Bold(true)], + clear: Vec::new(), + }], + ) + }; if variant == 9 { - assert!(PreparedEdit::text(&fixture, sid, text, 0..0, "x").is_err()); + assert!(text_edit(0..0, "x").is_err()); continue; } if variant >= 7 { - let edit = PreparedEdit::text(&fixture, sid, text, 1..3, "longer").unwrap(); - let [before, after] = [&fixture, edit.as_bytes()].map(|bytes| { + let edit = text_edit(1..3, "longer").unwrap(); + let [before, after] = [&fixture, &edit].map(|bytes| { let store = Store::parse(bytes).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); @@ -877,24 +924,20 @@ fn character_formatting_preserves_inheritance_and_associated_data() { format!("{:?}", view.nodes[&text].extra) }); assert_eq!(before, after); - assert!(PreparedEdit::text(&fixture, sid, text, 0..4, "x").is_err()); - assert_eq!( - PreparedEdit::text(&fixture, sid, text, 3..5, "x").is_ok(), - variant == 7 - ); + assert!(text_edit(0..4, "x").is_err()); + assert_eq!(text_edit(3..5, "x").is_ok(), variant == 7); continue; } // Hidden and hyperlink runs are ordinary text with flags; associated data, // equations and embedded objects are not. - let text_edit = PreparedEdit::text(&fixture, sid, text, 1..5, "x"); - assert_eq!(text_edit.is_ok(), matches!(variant, 0 | 1 | 4)); - let edit = PreparedEdit::format(&fixture, sid, text, 0..6, &[TextAttribute::Bold(true)]); + assert_eq!(text_edit(1..5, "x").is_ok(), matches!(variant, 0 | 1 | 4)); + let edit = bold(0..6); if matches!(variant, 2 | 5 | 6) { assert!(edit.is_err()); continue; } let edit = edit.unwrap(); - let store = Store::parse(edit.as_bytes()).unwrap(); + let store = Store::parse(&edit).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let doc = Document::parse(&index).unwrap(); let s = &doc.spaces[&sid]; @@ -911,10 +954,7 @@ fn character_formatting_preserves_inheritance_and_associated_data() { let data = &view.nodes[&text].extra[runs[0].extra_set.unwrap()]; assert_eq!(data.len(), 1); assert_eq!(data[0].id, 0x14001234); - assert!( - PreparedEdit::format(&fixture, sid, text, 1..3, &[TextAttribute::Bold(true)]) - .is_err() - ); + assert!(bold(1..3).is_err()); let previous = Store::parse(&fixture).unwrap(); let previous = RevisionIndex::parse(&previous).unwrap(); let previous_doc = Document::parse(&previous).unwrap(); diff --git a/crates/onestore/src/write/tests/multi.rs b/crates/onestore/src/write/tests/multi.rs index 17ff70a0194c7b1cdcc6655af43640929d298f39..1680b1e3f9c4140ea33d2c5eae62ffc445d196ac 100644 --- a/crates/onestore/src/write/tests/multi.rs +++ b/crates/onestore/src/write/tests/multi.rs @@ -1,7 +1,8 @@ use crate::{ ExGuid, RevisionIndex, Store, document::{Document, Kind}, - write::{PropertyObject, RevisionEdit, write_revisions}, + Transaction, + write::{PropertyObject, RevisionEdit, applied, revisions, write_revisions}, }; use std::collections::BTreeMap; @@ -18,8 +19,8 @@ mod current { const SOURCE: &[u8] = include_bytes!("../../../../../corpus/page-lifecycle/03-renamed/notebook/Lifecycle.one"); -fn nest(source: &[u8]) -> Vec { - write_revisions(source, |index| { +fn nest(source: &[u8]) -> Option { + revisions(source, |index| { let document = Document::parse(index)?; let pages = document.pages()?; assert_eq!(pages.len(), 9); @@ -100,7 +101,7 @@ fn nest(source: &[u8]) -> Vec { #[test] fn nesting_publishes_section_order_and_page_levels_in_one_transaction() { - let written = nest(SOURCE); + let written = applied(SOURCE, nest(SOURCE).as_ref()).unwrap(); let before = Store::parse(SOURCE).unwrap(); let after = Store::parse(&written).unwrap(); assert_eq!( @@ -160,7 +161,7 @@ fn nesting_publishes_section_order_and_page_levels_in_one_transaction() { .collect::>(), [1, 1, 1, 3, 1, 1, 1] ); - assert_eq!(nest(&written), written); + assert_eq!(nest(&written), None); if let Some(output) = std::env::var_os("ONESTORE_PAGE_BATCH_OUTPUT") { std::fs::create_dir(&output).unwrap(); std::fs::write( @@ -173,7 +174,8 @@ fn nesting_publishes_section_order_and_page_levels_in_one_transaction() { #[test] fn interrupted_multi_space_publication_never_exposes_a_partial_nesting() { - let written = nest(SOURCE); + let transaction = nest(SOURCE).unwrap(); + let written = applied(SOURCE, Some(&transaction)).unwrap(); let old = current::current(SOURCE); let new = current::current(&written); assert_ne!(old, new); @@ -186,9 +188,7 @@ fn interrupted_multi_space_publication_never_exposes_a_partial_nesting() { write_limit, random: 1951, }; - crate::commit::Transaction::between(SOURCE, &written) - .commit(&mut complete) - .unwrap(); + transaction.commit(&mut complete).unwrap(); assert_eq!(complete.durable, written); for fail_at in 1..=complete.operation { let mut interrupted = disk::Disk { @@ -199,9 +199,7 @@ fn interrupted_multi_space_publication_never_exposes_a_partial_nesting() { write_limit, random: 1951 + fail_at as u64, }; - crate::commit::Transaction::between(SOURCE, &written) - .commit(&mut interrupted) - .unwrap_err(); + transaction.commit(&mut interrupted).unwrap_err(); let observed = current::current(&interrupted.durable); assert!( observed == old || observed == new, @@ -234,7 +232,7 @@ fn repeated_multi_space_edits_cross_counter_carries_and_checkpoint_each_space() let initial_index = RevisionIndex::parse(&initial_store).unwrap(); let mut checkpoints = BTreeMap::new(); for step in 1_u32..=514 { - let written = write_revisions(&source, |index| { + let transaction = revisions(&source, |index| { let mut changes = BTreeMap::new(); for (sid, space) in &index.spaces { let raw = index.resolve(*sid, space.labels[&(ExGuid::default(), 1)])?; @@ -259,7 +257,9 @@ fn repeated_multi_space_edits_cross_counter_carries_and_checkpoint_each_space() .map(|(sid, objects)| (sid, RevisionEdit::Update(objects))) .collect()) }) + .unwrap() .unwrap(); + let written = applied(&source, Some(&transaction)).unwrap(); let store = Store::parse(&written).unwrap(); assert_eq!( store.header.transaction_count, @@ -290,9 +290,7 @@ fn repeated_multi_space_edits_cross_counter_carries_and_checkpoint_each_space() write_limit: 4096, random: 1952, }; - crate::commit::Transaction::between(&source, &written) - .commit(&mut complete) - .unwrap(); + transaction.commit(&mut complete).unwrap(); for fail_at in 1..=complete.operation { let mut interrupted = disk::Disk { visible: source.clone(), @@ -302,9 +300,7 @@ fn repeated_multi_space_edits_cross_counter_carries_and_checkpoint_each_space() write_limit: 4096, random: 1952 + fail_at as u64, }; - crate::commit::Transaction::between(&source, &written) - .commit(&mut interrupted) - .unwrap_err(); + transaction.commit(&mut interrupted).unwrap_err(); let observed = current::current(&interrupted.durable); assert!(observed == old || observed == new, "{step}:{fail_at}"); } diff --git a/crates/onestore/tests/commit.rs b/crates/onestore/tests/commit.rs index 0c21e0adae4f18bd9b4a672a1c5c974d51cfa5ce..0b7ea4099d653c3bc903383fa0202943b600d64e 100644 --- a/crates/onestore/tests/commit.rs +++ b/crates/onestore/tests/commit.rs @@ -2,104 +2,94 @@ mod disk; use disk::Disk; use onestore::{ - ExGuid, ObjectData, PropertySets, RevisionIndex, Store, Value, replace_property_bytes, + Arena, ExGuid, RevisionIndex, Section, Store, Transaction, + document::{Document, Kind}, + op::{Edit, Op, PageOp}, }; use std::fs; #[test] -fn incomplete_native_save_is_rejected_before_storage_io() { +fn incomplete_native_save_is_rejected_before_an_edit() { let source = fs::read("../../corpus/malformed/native-inflight.one").unwrap(); let store = Store::parse(&source).unwrap(); + let graph_error = RevisionIndex::parse(&store) + .unwrap() + .validate_current() + .unwrap_err(); + let arena = Arena::default(); + let error = Section::open(&arena, source.clone()).err().unwrap(); + assert_eq!(error.to_string(), graph_error.to_string()); +} + +/// The text object holding `text` and its page space. +fn target(source: &[u8], text: &str) -> (ExGuid, ExGuid) { + let store = Store::parse(source).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); - let graph_error = index.validate_current().unwrap_err(); - let mut checked = 0; - for (osid, space) in &index.spaces { - let revision = index - .resolve(*osid, space.labels[&(ExGuid::default(), 1)]) - .unwrap(); - for oid in revision.reachable().unwrap() { - let object = &revision.objects[&oid]; - if object.jcid != 0x6000e { - continue; - } - let ObjectData::Properties(data) = object.data else { - continue; - }; - for property in &PropertySets::parse(data).unwrap().sets[0] { - if property.value != Value::Bytes(b"Rust same paragraph.") { - continue; - } - let mut disk = Disk { - visible: source.clone(), - durable: source.clone(), - operation: 0, - fail_at: None, - write_limit: 1024, - random: 1, - }; - let error = onestore::commit_property_bytes( - &mut disk, - &source, - *osid, - oid, - property.id, - b"Rust same paragraph.", - ) - .unwrap_err(); - assert_eq!(error.state, onestore::CommitState::NotCommitted); - assert_eq!(error.error.to_string(), graph_error.to_string()); - assert_eq!(disk.operation, 0); - checked += 1; - } - } - } - assert_eq!(checked, 1); + let document = Document::parse(&index).unwrap(); + document + .spaces + .iter() + .find_map(|(space, view)| { + view.active()?.nodes.iter().find_map(|(id, node)| { + matches!(&node.kind, Kind::RichText { text: stored, .. } if stored == text) + .then_some((*space, *id)) + }) + }) + .unwrap() +} + +/// The text `object` holds in `image`, which must be valid. +fn text_of(image: &[u8], space: ExGuid, object: ExGuid) -> String { + let store = Store::parse(image).unwrap(); + assert!(store.checksum_mismatches.is_empty()); + let index = RevisionIndex::parse(&store).unwrap(); + index.validate_current().unwrap(); + let document = Document::parse(&index).unwrap(); + let Kind::RichText { text, .. } = &document.spaces[&space].active().unwrap().nodes[&object].kind + else { + panic!() + }; + text.clone() } #[test] fn repeated_appends_cross_log_fragments_and_expose_counter_tears() { - let mut source = + let initial = fs::read("../../corpus/native/20260905-05/snapshots/02-text/notebook/synthetic.one") .unwrap(); - let store = Store::parse(&source).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let mut target = None; - for (osid, space) in &index.spaces { - let revision = index - .resolve(*osid, space.labels[&(ExGuid::default(), 1)]) - .unwrap(); - for (oid, object) in revision.objects { - if let ObjectData::Properties(data) = object.data { - for property in &PropertySets::parse(data).unwrap().sets[0] { - if property.value == Value::Bytes(b"Fictitious plain text.") { - target = Some((*osid, oid, property.id)); - } - } - } - } - } - let (osid, oid, property) = target.unwrap(); + let store = Store::parse(&initial).unwrap(); + let (space, object) = target(&initial, "Fictitious plain text."); let root = store.header.root; let log = store.header.transaction_log; let initial_count = store.header.transaction_count; + let arena = Arena::default(); + let mut section = Section::open(&arena, initial.clone()).unwrap(); + let mut source = initial.clone(); + let mut length = "Fictitious plain text.".encode_utf16().count() as u32; let mut saw_counter_tear = false; - for transaction in initial_count + 1..=258 { - let value = format!("Transaction {transaction}"); - let written = - replace_property_bytes(&source, osid, oid, property, value.as_bytes()).unwrap(); + for count in initial_count + 1..=258 { + let value = format!("Transaction {count}"); + let op = PageOp::Text { + text: object, + range: 0..length, + with: value.clone(), + }; + length = value.encode_utf16().count() as u32; + section + .apply("Author", &Edit { at: 133_700_000_000_000_000 + u64::from(count), ops: vec![Op::Page { space, op }] }) + .unwrap(); + let transaction = section.seal().unwrap().unwrap(); + let mut written = source.clone(); + transaction.apply(&mut written).unwrap(); let store = Store::parse(&written).unwrap(); assert_eq!(store.header.root, root); assert_eq!(store.header.transaction_log, log); - assert_eq!(store.header.transaction_count, transaction); - assert!(store.checksum_mismatches.is_empty()); - RevisionIndex::parse(&store) - .unwrap() - .validate_current() - .unwrap(); + assert_eq!(store.header.transaction_count, count); + assert_eq!(text_of(&written, space, object), value); let mut previous = written.clone(); previous[..1024].copy_from_slice(&source[..1024]); let old = Store::parse(&previous).unwrap(); - assert_eq!(old.header.transaction_count, transaction - 1); + assert_eq!(old.header.transaction_count, count - 1); assert!(old.checksum_mismatches.is_empty()); RevisionIndex::parse(&old) .unwrap() @@ -110,17 +100,17 @@ fn repeated_appends_cross_log_fragments_and_expose_counter_tears() { torn[prefix..1024].copy_from_slice(&source[prefix..1024]); let result = Store::parse(&torn) .and_then(|store| RevisionIndex::parse(&store)?.validate_current()); - if transaction == 256 && prefix == 97 { + if count == 256 && prefix == 97 { assert!(result.is_err()); saw_counter_tear = true; } else { result.unwrap(); } } - if transaction == initial_count + 1 || transaction == 256 { - check_crashes(&source, osid, oid, property, value.as_bytes()); + if count == initial_count + 1 || count == 256 { + check_crashes(&source, &transaction, space, object, &value); } - if transaction == initial_count + 1 { + if count == initial_count + 1 { let path = std::env::temp_dir().join(format!( "onestore-commit-{}-{}.one", std::process::id(), @@ -130,61 +120,29 @@ fn repeated_appends_cross_log_fragments_and_expose_counter_tears() { .as_nanos() )); fs::write(&path, &source).unwrap(); - onestore::commit_file_property(&path, &source, osid, oid, property, value.as_bytes()) - .unwrap(); + transaction.commit_file(&path).unwrap(); let committed = fs::read(&path).unwrap(); - let parsed = Store::parse(&committed).unwrap(); - assert_eq!(parsed.header.transaction_count, transaction); - RevisionIndex::parse(&parsed) - .unwrap() - .validate_current() - .unwrap(); - let stale = - onestore::commit_file_property(&path, &source, osid, oid, property, b"stale") - .unwrap_err(); + assert_eq!(committed, written); + // The file moved past the transaction's base: nothing is written again. + let stale = transaction.commit_file(&path).unwrap_err(); assert_eq!(stale.state, onestore::CommitState::NotCommitted); assert_eq!(fs::read(&path).unwrap(), committed); - onestore::commit_file_property( - &path, - &committed, - osid, - oid, - property, - value.as_bytes(), - ) - .unwrap(); - assert_eq!(fs::read(&path).unwrap(), committed); fs::remove_file(path).unwrap(); } source = written; } assert!(saw_counter_tear); + assert!(section.image() == source); } -fn check_crashes(source: &[u8], osid: ExGuid, oid: ExGuid, property: u32, value: &[u8]) { - let current_value = |data: &[u8]| { - let store = Store::parse(data).unwrap(); - assert!(store.checksum_mismatches.is_empty()); - let index = RevisionIndex::parse(&store).unwrap(); - index.validate_current().unwrap(); - let revision = index - .resolve(osid, index.spaces[&osid].labels[&(ExGuid::default(), 1)]) - .unwrap(); - let ObjectData::Properties(blob) = revision.objects[&oid].data else { - panic!() - }; - let properties = PropertySets::parse(blob).unwrap(); - let Value::Bytes(value) = properties.sets[0] - .iter() - .find(|p| p.id == property) - .unwrap() - .value - else { - panic!() - }; - value.to_vec() - }; - let before = current_value(source); +fn check_crashes( + source: &[u8], + transaction: &Transaction, + space: ExGuid, + object: ExGuid, + value: &str, +) { + let before = text_of(source, space, object); for limit in [17, 1024] { let disk = |fail_at, random| Disk { visible: source.to_vec(), @@ -195,22 +153,13 @@ fn check_crashes(source: &[u8], osid: ExGuid, oid: ExGuid, property: u32, value: random, }; let mut successful = disk(None, 1); - onestore::commit_property_bytes(&mut successful, source, osid, oid, property, value) - .unwrap(); - assert_eq!(current_value(&successful.durable), value); + transaction.commit(&mut successful).unwrap(); + assert_eq!(text_of(&successful.durable, space, object), value); for at in 1..=successful.operation { for seed in [0, 1, 42, u64::MAX] { let mut interrupted = disk(Some(at), seed); - let failure = onestore::commit_property_bytes( - &mut interrupted, - source, - osid, - oid, - property, - value, - ) - .unwrap_err(); - let persisted = current_value(&interrupted.durable); + let failure = transaction.commit(&mut interrupted).unwrap_err(); + let persisted = text_of(&interrupted.durable, space, object); match failure.state { onestore::CommitState::NotCommitted => assert_eq!(persisted, before), onestore::CommitState::Committed => assert_eq!(persisted, value), @@ -222,6 +171,7 @@ fn check_crashes(source: &[u8], osid: ExGuid, oid: ExGuid, property: u32, value: } } } + #[test] #[cfg(any(unix, windows))] fn bounded_file_reads_reject_partial_images_and_release_the_owner() { diff --git a/crates/onestore/tests/conflict.rs b/crates/onestore/tests/conflict.rs new file mode 100644 index 0000000000000000000000000000000000000000..9d42f3773a391a3f67b4ba647d5f516e8858718d --- /dev/null +++ b/crates/onestore/tests/conflict.rs @@ -0,0 +1,357 @@ +use onestore::{ + Arena, ConflictPage, ExGuid, ObjectData, PageCreation, PropertySets, RevisionIndex, Section, + Store, Value, + op::{Edit, Op, PageOp, SectionOp}, + page::{Page, PageObject}, +}; +use std::collections::BTreeMap; + +const AT: u64 = 134_030_000_000_000_000; + +fn read(path: &str) -> Vec { + std::fs::read(format!("{}/../../{path}", env!("CARGO_MANIFEST_DIR"))).unwrap() +} + +/// Each object's jcid and root-set property identities, by space and object. +fn objects(image: &[u8]) -> BTreeMap)>> { + let store = Store::parse(image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + index + .spaces + .keys() + .map(|space| { + let revision = index.resolve_active(*space).unwrap(); + let objects = revision + .objects + .iter() + .map(|(id, object)| { + let ids = match object.data { + ObjectData::Properties(bytes) => PropertySets::parse(bytes).unwrap().sets + [0] + .iter() + .map(|property| property.id) + .collect(), + _ => Vec::new(), + }; + (*id, (object.jcid, ids)) + }) + .collect(); + (*space, objects) + }) + .collect() +} + +/// The object-space references a property holds. +fn spaces(image: &[u8], space: ExGuid, object: ExGuid, property: u32) -> usize { + let store = Store::parse(image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let revision = index.resolve_active(space).unwrap(); + let ObjectData::Properties(bytes) = revision.objects[&object].data else { + panic!() + }; + PropertySets::parse(bytes).unwrap().sets[0] + .iter() + .find(|field| field.id == property) + .map_or(0, |field| match &field.value { + Value::References { compact_ids, .. } => compact_ids.len() / 4, + _ => panic!(), + }) +} + +fn texts(page: &Page) -> Vec<(ExGuid, String)> { + page.objects + .iter() + .flat_map(|object| match object { + PageObject::Outline(outline) => outline.paragraphs.clone(), + PageObject::Title(title) => title + .outlines + .iter() + .flat_map(|outline| outline.paragraphs.clone()) + .collect(), + _ => Vec::new(), + }) + .filter_map(|paragraph| { + paragraph + .text() + .map(|text| (text.id, text.text.text().to_owned())) + }) + .collect() +} + +#[test] +fn native_conflict_pages_are_listed_under_their_page_last_first() { + let arena = Arena::default(); + let mut section = Section::open( + &arena, + read("corpus/collaboration/round-01/offline/notebook/synthetic.one"), + ) + .unwrap(); + let pages = section.pages().unwrap(); + let conflicts = section.conflicts().unwrap(); + assert_eq!(conflicts.len(), 1); + let (page, listed) = &conflicts[0]; + assert_eq!(*page, pages[0].0); + let [ConflictPage { + space, + title, + user, + created, + objects, + }] = listed.as_slice() + else { + panic!("{listed:?}") + }; + assert_eq!( + (title.as_str(), user.as_str(), *created), + ( + "Native edited while disconnected.", + "snow", + Some(0x01dd3d10d7afd9e0) + ) + ); + let conflict = section.page(*space).unwrap(); + let marked: Vec<_> = texts(&conflict) + .into_iter() + .filter(|(id, _)| objects.contains(id)) + .map(|(_, text)| text) + .collect(); + assert_eq!(marked, ["Native edited while disconnected."]); + + // Three clients' conflict pages: OneNote lists the one stored last first. + let arena = Arena::default(); + let mut section = Section::open( + &arena, + read("corpus/conflict-page/native-three/notebook/synthetic.one"), + ) + .unwrap(); + let users: Vec<_> = section.conflicts().unwrap()[0] + .1 + .iter() + .map(|page| page.user.clone()) + .collect(); + assert_eq!( + users, + ["ONE-M6-053BD357", "ONE-M6-9451B6C6", "ONE-M6-DDE10E0D"] + ); +} + +/// The conflict page a merge makes of `local`, the version of `space`'s page it could not +/// take, marking the text object `conflicting`. +fn conflict(space: ExGuid, local: &Page, conflicting: ExGuid, user: &str) -> Op { + let titled = local + .objects + .iter() + .any(|object| matches!(object, PageObject::Title(_))); + let mut objects = [conflicting]; + let page = local.copy_with(&mut objects).unwrap(); + Op::Section(SectionOp::Conflict { + of: space, + creation: PageCreation::new(None, titled.then_some(local.title.as_str()), user).unwrap(), + page, + objects: objects.to_vec(), + }) +} + +/// A conflict page written through ops stores what OneNote 2010 writes for one +/// (`corpus/collaboration/round-01/offline`): its own space under the page's manifest, with +/// conflict metadata, a read-only deletable conflict page and the conflicting text marked. +/// `ONESTORE_CONFLICT_EXPORT` names a directory to write the notebook to, for a cold read. +#[test] +fn a_conflict_page_is_stored_as_onenote_stores_one() { + let notebook = "corpus/native-ink/cold-ui-ink/notebook"; + let source = read(&format!("{notebook}/synthetic.one")); + let arena = Arena::default(); + let mut section = Section::open(&arena, source.clone()).unwrap(); + let space = section.pages().unwrap()[0].0; + let base = section.page(space).unwrap(); + let (text, _) = texts(&base) + .into_iter() + .find(|(_, text)| text.starts_with("Fictitious: café")) + .unwrap(); + // Both machines renamed the paragraph's first word, within its Latin run. + let replace = |with: &str| Edit { + at: AT, + ops: vec![Op::Page { + space, + op: PageOp::Text { + text, + range: 0..10, + with: with.into(), + }, + }], + }; + section.apply("Remote", &replace("Remote")).unwrap(); + let scratch = Arena::default(); + let mut local = Section::open(&scratch, source).unwrap(); + local.apply("Clover Snow", &replace("Snowbound")).unwrap(); + let local = local.page(space).unwrap(); + let op = conflict(space, &local, text, "Clover Snow"); + let Op::Section(SectionOp::Conflict { creation, .. }) = &op else { + unreachable!() + }; + let created = creation.space(); + section + .apply( + "Clover Snow", + &Edit { + at: AT + 10_000_000, + ops: vec![op.clone()], + }, + ) + .unwrap(); + section.seal().unwrap(); + let image = section.image(); + + let arena = Arena::default(); + let mut reopened = Section::open(&arena, image.clone()).unwrap(); + assert_eq!(reopened.pages().unwrap().len(), 1); + let listed = reopened.conflicts().unwrap(); + assert_eq!(listed.len(), 1); + assert_eq!(listed[0].0, space); + assert_eq!( + listed[0].1, + [ConflictPage { + space: created, + title: "Snowbound: café, 東京, مرحبا".into(), + user: "Clover Snow".into(), + created: listed[0].1[0].created, + objects: listed[0].1[0].objects.clone(), + }] + ); + let kept = reopened.page(created).unwrap(); + let marked: Vec<_> = texts(&kept) + .into_iter() + .filter(|(id, _)| listed[0].1[0].objects.contains(id)) + .map(|(_, text)| text) + .collect(); + assert_eq!(marked, ["Snowbound: café, 東京, مرحبا"]); + assert!( + texts(&reopened.page(space).unwrap()) + .iter() + .any(|(_, text)| text == "Remote: café, 東京, مرحبا") + ); + + // The stored form, property by property, as OneNote writes it. + let stored = objects(&image); + let conflict_space = &stored[&created]; + let store = Store::parse(&image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let roots = index.resolve_active(created).unwrap().roots; + let (jcid, metadata) = &conflict_space[&roots[&2]]; + assert_eq!(*jcid, 0x20038); + assert_eq!( + metadata, + &[ + 0x1c001cf3, 0x1c001c30, 0x14001dff, 0x14001d82, 0x1400348b, 0x18001c65, 0x1c001d9e, + 0x1c001d9f + ] + ); + let page_node = conflict_space + .values() + .find(|(jcid, _)| *jcid == 0x6000b) + .unwrap(); + for flag in [0x88001cde, 0x88001d0c, 0x88001d7c] { + assert!(page_node.1.contains(&flag), "{flag:#x}"); + } + let marked: Vec<_> = conflict_space + .iter() + .filter(|(_, (_, ids))| ids.contains(&0x88001d96)) + .map(|(_, (jcid, ids))| (*jcid, ids.contains(&0x88001ddb))) + .collect(); + assert_eq!(marked, [(0x6000e, true)]); + let main = &stored[&space]; + let main_roots = index.resolve_active(space).unwrap().roots; + assert_eq!(spaces(&image, space, main_roots[&1], 0x2c001d63), 1); + assert!(main[&main_roots[&2]].1.contains(&0x88001d97)); + assert_eq!( + main.values() + .filter(|(jcid, ids)| *jcid == 0x20038 && ids.contains(&0x1c001d9e)) + .count(), + 1 + ); + assert_eq!( + stored[&index.root] + .values() + .filter(|(jcid, ids)| *jcid == 0x20030 && ids.contains(&0x88001d97)) + .count(), + 1 + ); + + if let Some(output) = std::env::var_os("ONESTORE_CONFLICT_EXPORT") { + let output = std::path::Path::new(&output); + std::fs::create_dir_all(output).unwrap(); + std::fs::write(output.join("synthetic.one"), &image).unwrap(); + std::fs::copy( + format!("{}/../../{notebook}/Open Notebook.onetoc2", env!("CARGO_MANIFEST_DIR")), + output.join("Open Notebook.onetoc2"), + ) + .unwrap(); + } +} + +/// Deleting a conflict page stores what OneNote 2010's Delete Conflict Page does +/// (`corpus/conflict-page/native-delete`): the page's manifest stops listing the conflict +/// page, keeping its metadata copy, and the page and the section's copy of its metadata no +/// longer say it has conflict pages; the conflict page's space is left alone. +/// `ONESTORE_CONFLICT_DELETE_EXPORT` names a directory to write the notebook to. +#[test] +fn deleting_a_conflict_page_stores_what_onenote_stores() { + let notebook = "corpus/conflict-page/native/conflict/notebook"; + let arena = Arena::default(); + let mut section = Section::open(&arena, read(&format!("{notebook}/synthetic.one"))).unwrap(); + let (page, listed) = section.conflicts().unwrap().remove(0); + let conflict = listed[0].space; + section + .apply( + "Clover Snow", + &Edit { + at: AT, + ops: vec![Op::Section(SectionOp::Delete(vec![conflict]))], + }, + ) + .unwrap(); + section.seal().unwrap(); + let image = section.image(); + let arena = Arena::default(); + let mut reopened = Section::open(&arena, image.clone()).unwrap(); + assert!(reopened.conflicts().unwrap().is_empty()); + assert_eq!(reopened.pages().unwrap().len(), 1); + + // Each space's objects and their property identities, as OneNote left them. + let native = read("corpus/conflict-page/native-delete/notebook/synthetic.one"); + let shapes = |image: &[u8]| { + let store = Store::parse(image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let root = index.root; + let roots = index.resolve_active(page).unwrap().roots; + let objects = objects(image); + let manifest = objects[&page][&roots[&1]].clone(); + let metadata = objects[&page][&roots[&2]].clone(); + let flagged = objects[&root] + .values() + .filter(|(_, ids)| ids.contains(&0x88001d97)) + .count(); + (manifest, metadata, flagged, objects[&conflict].clone()) + }; + let (manifest, metadata, flagged, space) = shapes(&image); + let (native_manifest, native_metadata, native_flagged, native_space) = shapes(&native); + assert_eq!(manifest.1.iter().filter(|id| **id != 0x3400347b).collect::>(), [&0x24001c1f, &0x24003442]); + assert_eq!( + native_manifest.1.iter().filter(|id| **id != 0x3400347b).collect::>(), + [&0x24001c1f, &0x24003442] + ); + assert!(!metadata.1.contains(&0x88001d97) && !native_metadata.1.contains(&0x88001d97)); + assert_eq!((flagged, native_flagged), (0, 0)); + assert_eq!(space, native_space); + + if let Some(output) = std::env::var_os("ONESTORE_CONFLICT_DELETE_EXPORT") { + let output = std::path::Path::new(&output); + std::fs::create_dir_all(output).unwrap(); + std::fs::write(output.join("synthetic.one"), &image).unwrap(); + std::fs::copy( + format!("{}/../../{notebook}/Open Notebook.onetoc2", env!("CARGO_MANIFEST_DIR")), + output.join("Open Notebook.onetoc2"), + ) + .unwrap(); + } +} diff --git a/crates/onestore/tests/document.rs b/crates/onestore/tests/document.rs index 20874875d3826865e851767aff8651715db7d7ca..ccc831b773d2d740ebe68d73d528750dbaf828cc 100644 --- a/crates/onestore/tests/document.rs +++ b/crates/onestore/tests/document.rs @@ -482,10 +482,6 @@ fn rust_unicode_and_native_latin1_use_utf16_run_offsets() { }; assert_eq!(actual, text); assert_eq!(runs[0].end, 7); - let bytes = fs::read("../../corpus/m6/ansi/notebook/synthetic.one").unwrap(); - let store = Store::parse(&bytes).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let document = Document::parse(&index).unwrap(); let expected = format!( "Extended bytes: {}", (128u8..=255) @@ -493,14 +489,23 @@ fn rust_unicode_and_native_latin1_use_utf16_run_offsets() { .collect::>() .join(" ") ); - assert!( - document - .spaces - .values() - .flat_map(|s| s.revisions.values()) - .flat_map(|r| r.nodes.values()) - .any(|n| matches!(&n.kind, Kind::RichText { text, .. } if text == &expected)) - ); + // OneNote 2010 reads TextExtendedAscii as Latin-1 whatever the run's language + // (corpus/extended-ascii/{en,ru,ja}/cold). + for language in ["en", "ru", "ja"] { + let path = format!("../../corpus/extended-ascii/{language}/candidate/synthetic.one"); + let bytes = fs::read(path).unwrap(); + let store = Store::parse(&bytes).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + assert!( + document + .spaces + .values() + .flat_map(|s| s.revisions.values()) + .flat_map(|r| r.nodes.values()) + .any(|n| matches!(&n.kind, Kind::RichText { text, .. } if text == &expected)) + ); + } } #[test] @@ -717,11 +722,11 @@ fn malformed_run_boundaries_fail_after_valid_storage_decoding() { else { unreachable!() }; + // Damage the stored boundaries where they lie, as no writer would store them. + let at = boundaries.as_ptr().addr() - bytes.as_ptr().addr(); for invalid in [5u32, u32::MAX] { - let mut changed = boundaries.to_vec(); - changed[..4].copy_from_slice(&invalid.to_le_bytes()); - let mutated = - onestore::replace_property_bytes(&bytes, sid, oid, 0x1c001e12, &changed).unwrap(); + let mut mutated = bytes.to_vec(); + mutated[at..at + 4].copy_from_slice(&invalid.to_le_bytes()); let store = Store::parse(&mutated).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); index.validate_current().unwrap(); diff --git a/crates/onestore/tests/edit.rs b/crates/onestore/tests/edit.rs index bf22275a152972be7cada34f6e97b62f3fea4835..6d9f87595221dfafcc8f86ab14d1ca980fcc9625 100644 --- a/crates/onestore/tests/edit.rs +++ b/crates/onestore/tests/edit.rs @@ -2,14 +2,18 @@ mod checkpoint; #[path = "support/disk.rs"] mod disk; +#[path = "support/ops.rs"] +mod ops; #[path = "support/trace.rs"] mod trace; use disk::Disk; use onestore::{ - CommitState, ExGuid, RevisionIndex, Store, + CommitError, CommitIo, CommitState, ExGuid, RevisionIndex, Store, Transaction, document::{Document, Kind}, + op::{Op, OpError, PageOp}, }; +use std::ops::Range; const SOURCE: &[u8] = include_bytes!( "../../../corpus/native/20260905-05/snapshots/03-format-unicode/notebook/synthetic.one" @@ -31,6 +35,55 @@ fn target(source: &[u8]) -> (ExGuid, ExGuid) { panic!("Missing native text fixture") } +fn typed(space: ExGuid, text: ExGuid, range: Range, with: &str) -> Vec { + let op = PageOp::Text { + text, + range, + with: with.into(), + }; + vec![Op::Page { space, op }] +} + +/// `source` with `range` of the text object replaced. +fn replace_text( + source: &[u8], + space: ExGuid, + text: ExGuid, + range: Range, + with: &str, +) -> Result, OpError> { + ops::edited(source, typed(space, text, range, with)) +} + +/// The transaction replacing `range` of the text object in `source`. +fn text_transaction( + source: &[u8], + space: ExGuid, + text: ExGuid, + range: Range, + with: &str, +) -> Result { + let refused = |error: String| CommitError { + state: CommitState::NotCommitted, + error: std::io::Error::new(std::io::ErrorKind::InvalidData, error), + }; + ops::transaction(source, "Author", typed(space, text, range, with)) + .map_err(|error| refused(error.to_string()))? + .ok_or_else(|| refused("The edit changes nothing".into())) +} + +/// `text_transaction` committed under caller-held exclusion. +fn commit_text( + io: &mut impl CommitIo, + source: &[u8], + space: ExGuid, + text: ExGuid, + range: Range, + with: &str, +) -> Result<(), CommitError> { + text_transaction(source, space, text, range, with)?.commit(io) +} + fn text_runs(source: &[u8], sid: ExGuid, oid: ExGuid) -> serde_json::Value { let store = Store::parse(source).unwrap(); assert!(store.checksum_mismatches.is_empty()); @@ -61,9 +114,11 @@ fn prepared_publication_preserves_its_identity_through_every_io_failure() { let source = onestore::create_section("prepared.one", "Fictitious: café 🦀", "Fixture").unwrap(); let (sid, oid) = target(&source); - let source = checkpoint::pending(&source, sid, oid, 0x14001d7a); - let edit = onestore::PreparedEdit::text(&source, sid, oid, 0..0, "Prepared 🐈 ").unwrap(); - let store = Store::parse(edit.as_bytes()).unwrap(); + let source = checkpoint::pending(&source, sid, oid); + let edit = text_transaction(&source, sid, oid, 0..0, "Prepared 🐈 ").unwrap(); + let mut written = source.clone(); + edit.apply(&mut written).unwrap(); + let store = Store::parse(&written).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let planned = index.spaces[&sid].labels[&(ExGuid::default(), 1)]; let persisted: ExGuid = @@ -72,7 +127,7 @@ fn prepared_publication_preserves_its_identity_through_every_io_failure() { let before_index = RevisionIndex::parse(&before_store).unwrap(); assert!(!before_index.spaces[&sid].revisions.contains_key(&persisted)); let before = text_runs(&source, sid, oid); - let after = text_runs(edit.as_bytes(), sid, oid); + let after = text_runs(&written, sid, oid); for write_limit in [17, 4096] { let disk = |fail_at| Disk { visible: source.clone(), @@ -84,12 +139,12 @@ fn prepared_publication_preserves_its_identity_through_every_io_failure() { }; let mut success = disk(None); edit.commit(&mut success).unwrap(); - assert_eq!(success.durable, edit.as_bytes()); + assert_eq!(success.durable, written); let operations = success.operation; let error = edit.commit(&mut success).unwrap_err(); assert_eq!(error.state, CommitState::NotCommitted); assert_eq!(error.error.kind(), std::io::ErrorKind::ResourceBusy); - assert_eq!(success.durable, edit.as_bytes()); + assert_eq!(success.durable, written); for at in 1..=operations { let mut interrupted = disk(Some(at)); let error = edit.commit(&mut interrupted).unwrap_err(); @@ -108,7 +163,8 @@ fn prepared_publication_preserves_its_identity_through_every_io_failure() { interrupted.visible.clone_from(&snapshot); interrupted.fail_at = None; interrupted.write_limit = 17; - onestore::confirm_snapshot(&mut interrupted, &snapshot).unwrap(); + onestore::confirm(&mut interrupted, &onestore::Stamp::of(&snapshot).unwrap()) + .unwrap(); assert_refreshed(&snapshot, &interrupted.durable); } } @@ -126,9 +182,9 @@ fn snapshot_confirmation_needs_no_surviving_edit_target() { random: 911, }; assert!( - onestore::replace_text(SOURCE, ExGuid::default(), ExGuid::default(), 0..0, "").is_err() + replace_text(SOURCE, ExGuid::default(), ExGuid::default(), 0..0, "").is_err() ); - onestore::confirm_snapshot(&mut disk, SOURCE).unwrap(); + onestore::confirm(&mut disk, &onestore::Stamp::of(SOURCE).unwrap()).unwrap(); assert_refreshed(SOURCE, &disk.durable); let flush = disk.operation; for (failure, state) in [ @@ -140,7 +196,7 @@ fn snapshot_confirmation_needs_no_surviving_edit_target() { disk.operation = 0; disk.fail_at = Some(failure); assert_eq!( - onestore::confirm_snapshot(&mut disk, SOURCE) + onestore::confirm(&mut disk, &onestore::Stamp::of(SOURCE).unwrap()) .unwrap_err() .state, state @@ -153,7 +209,7 @@ fn confirmation_notifies_cached_readers_after_interrupted_version_publication() let source = onestore::create_section("confirmation.one", "Fictitious: before", "Fixture").unwrap(); let (sid, oid) = target(&source); - let mut snapshot = onestore::replace_text(&source, sid, oid, 0..0, "Recovered ").unwrap(); + let mut snapshot = replace_text(&source, sid, oid, 0..0, "Recovered ").unwrap(); snapshot[212..252].copy_from_slice(&source[212..252]); let expected = text_runs(&snapshot, sid, oid); assert_ne!(expected, text_runs(&source, sid, oid)); @@ -167,11 +223,13 @@ fn confirmation_notifies_cached_readers_after_interrupted_version_publication() random: 911, }; let mut success = disk(None); - onestore::confirm_snapshot(&mut success, &snapshot).unwrap(); + onestore::confirm(&mut success, &onestore::Stamp::of(&snapshot).unwrap()).unwrap(); assert_refreshed(&snapshot, &success.durable); for failure in 1..=success.operation { let mut interrupted = disk(Some(failure)); - let error = onestore::confirm_snapshot(&mut interrupted, &snapshot).unwrap_err(); + let error = + onestore::confirm(&mut interrupted, &onestore::Stamp::of(&snapshot).unwrap()) + .unwrap_err(); assert_ne!(error.state, CommitState::Committed); assert_eq!(text_runs(&interrupted.durable, sid, oid), expected); assert_eq!(&interrupted.durable[..212], &snapshot[..212]); @@ -183,51 +241,53 @@ fn confirmation_notifies_cached_readers_after_interrupted_version_publication() #[test] fn confirming_visible_text_requires_flush_without_another_revision() { let (sid, oid) = target(SOURCE); - let visible = onestore::replace_text(SOURCE, sid, oid, 0..0, "Recovered ").unwrap(); + let visible = replace_text(SOURCE, sid, oid, 0..0, "Recovered ").unwrap(); + let base = onestore::Stamp::of(&visible).unwrap(); let mut disk = Disk { visible: visible.clone(), durable: SOURCE.to_vec(), operation: 0, fail_at: None, - write_limit: 0, + write_limit: 64, random: 1, }; - onestore::commit_text(&mut disk, &visible, sid, oid, 0..0, "").unwrap(); - assert_eq!(disk.visible, visible); - assert_eq!(disk.durable, visible); + onestore::confirm(&mut disk, &base).unwrap(); + assert_refreshed(&visible, &disk.durable); let flush = disk.operation; for (failure, state) in [ (1, CommitState::NotCommitted), (flush, CommitState::Unknown), ] { + disk.visible.clone_from(&visible); disk.durable = SOURCE.to_vec(); disk.operation = 0; disk.fail_at = Some(failure); - let error = onestore::commit_text(&mut disk, &visible, sid, oid, 0..0, "").unwrap_err(); + let error = onestore::confirm(&mut disk, &base).unwrap_err(); assert_eq!(error.state, state); - assert_eq!(disk.visible, visible); + assert_eq!(&disk.visible[..212], &visible[..212]); + assert_eq!(&disk.visible[252..], &visible[252..]); } } #[test] fn confirmation_rejects_a_changed_header_or_length_before_any_write() { - let (sid, oid) = target(SOURCE); let mut source = SOURCE.to_vec(); source.resize(3 * 1024 * 1024 + 131, 0); + let base = onestore::Stamp::of(&source).unwrap(); let mut disk = trace::Trace { bytes: source.clone(), events: Vec::new(), }; - onestore::commit_text(&mut disk, &source, sid, oid, 0..0, "").unwrap(); - assert_eq!(disk.events.len(), 1); - if let trace::Event::Write(offset, bytes) = &disk.events[0] { - panic!("Confirmation wrote {} bytes at {offset}", bytes.len()); - } + let confirmed = |events: &[trace::Event]| { + matches!(events, [trace::Event::Flush, trace::Event::Write(212, version), trace::Event::Flush] if version.len() == 40) + }; + onestore::confirm(&mut disk, &base).unwrap(); + assert!(confirmed(&disk.events)); for changed in [96, 128, 212, 1023] { disk.bytes.clone_from(&source); disk.bytes[changed] ^= 1; disk.events.clear(); - let error = onestore::commit_text(&mut disk, &source, sid, oid, 0..0, "").unwrap_err(); + let error = onestore::confirm(&mut disk, &base).unwrap_err(); assert_eq!(error.state, CommitState::NotCommitted); assert_eq!(error.error.kind(), std::io::ErrorKind::ResourceBusy); assert!(disk.events.is_empty()); @@ -237,14 +297,14 @@ fn confirmation_rejects_a_changed_header_or_length_before_any_write() { disk.bytes.clone_from(&source); disk.bytes[changed] ^= 1; disk.events.clear(); - onestore::commit_text(&mut disk, &source, sid, oid, 0..0, "").unwrap(); - assert!(matches!(disk.events[..], [trace::Event::Flush])); + onestore::confirm(&mut disk, &base).unwrap(); + assert!(confirmed(&disk.events)); } for length in [source.len() - 1, source.len() + 1] { disk.bytes.clone_from(&source); disk.bytes.resize(length, 0); disk.events.clear(); - let error = onestore::commit_text(&mut disk, &source, sid, oid, 0..0, "").unwrap_err(); + let error = onestore::confirm(&mut disk, &base).unwrap_err(); assert_eq!(error.state, CommitState::NotCommitted); assert!(disk.events.is_empty()); } @@ -332,7 +392,7 @@ fn text_splices_preserve_formats_objects_and_history_in_one_transaction() { let old_index = RevisionIndex::parse(&old).unwrap(); let before = text_runs(SOURCE, sid, oid); for replacement in ["", "🦀", "A longer replacement with 日本語 and e\u{301}"] { - let edited = onestore::replace_text(SOURCE, sid, oid, 0..10, replacement).unwrap(); + let edited = replace_text(SOURCE, sid, oid, 0..10, replacement).unwrap(); let store = Store::parse(&edited).unwrap(); assert_eq!( store.header.transaction_count, @@ -379,7 +439,7 @@ fn text_splices_preserve_formats_objects_and_history_in_one_transaction() { #[test] fn invalid_text_edits_never_touch_storage() { let (sid, oid) = target(SOURCE); - let source = onestore::replace_text(SOURCE, sid, oid, 0..0, "🦀").unwrap(); + let source = replace_text(SOURCE, sid, oid, 0..0, "🦀").unwrap(); for (range, replacement) in [ (1..1, "x"), (0..999, "x"), @@ -396,7 +456,7 @@ fn invalid_text_edits_never_touch_storage() { random: 1, }; let error = - onestore::commit_text(&mut disk, &source, sid, oid, range, replacement).unwrap_err(); + commit_text(&mut disk, &source, sid, oid, range, replacement).unwrap_err(); assert_eq!(error.state, CommitState::NotCommitted); assert_eq!(disk.operation, 0); assert_eq!(disk.durable, source); @@ -437,7 +497,7 @@ fn cross_run_splices_match_a_character_model_and_preserve_history() { let mut expected = before.clone(); expected.splice(a..b, replacement.chars().map(|c| (c, before[a].1.clone()))); let edited = - onestore::replace_text(SOURCE, sid, oid, offsets[a]..offsets[b], replacement) + replace_text(SOURCE, sid, oid, offsets[a]..offsets[b], replacement) .unwrap(); assert_eq!(characters(&edited), expected, "characters {a}..{b}"); let current_store = Store::parse(&edited).unwrap(); @@ -460,8 +520,8 @@ fn cross_run_splices_match_a_character_model_and_preserve_history() { } } let cleared = - onestore::replace_text(SOURCE, sid, oid, 0..*offsets.last().unwrap(), "").unwrap(); - let typed = onestore::replace_text(&cleared, sid, oid, 0..0, "new").unwrap(); + replace_text(SOURCE, sid, oid, 0..*offsets.last().unwrap(), "").unwrap(); + let typed = replace_text(&cleared, sid, oid, 0..0, "new").unwrap(); let format = &before.last().unwrap().1; assert_eq!( characters(&typed), @@ -472,7 +532,7 @@ fn cross_run_splices_match_a_character_model_and_preserve_history() { ); let text: String = before[..before.len() - 1].iter().map(|(c, _)| *c).collect(); assert_eq!( - onestore::replace_text(SOURCE, sid, oid, 0..*offsets.last().unwrap(), &text).unwrap(), + replace_text(SOURCE, sid, oid, 0..*offsets.last().unwrap(), &text).unwrap(), SOURCE ); } @@ -520,9 +580,9 @@ fn export_native_cross_run_edits() { .unwrap(); let range = range.start..range.end.min(length); let mut edited = - onestore::replace_text(&source, sid, oid, range.clone(), replacement).unwrap(); + replace_text(&source, sid, oid, range.clone(), replacement).unwrap(); if retype { - edited = onestore::replace_text(&edited, sid, oid, 0..0, "Retyped 🦀").unwrap(); + edited = replace_text(&edited, sid, oid, 0..0, "Retyped 🦀").unwrap(); } let candidate = output.join(name).join("candidate"); fs::create_dir_all(&candidate).unwrap(); @@ -540,7 +600,7 @@ fn export_native_cross_run_edits() { fn insertion_at_a_style_boundary_uses_the_following_style() { let (sid, oid) = target(SOURCE); let before = text_runs(SOURCE, sid, oid); - let edited = onestore::replace_text(SOURCE, sid, oid, 18..18, "🦀").unwrap(); + let edited = replace_text(SOURCE, sid, oid, 18..18, "🦀").unwrap(); let mut expected = before.clone(); expected[1]["text"] = format!("🦀{}", before[1]["text"].as_str().unwrap()).into(); assert_eq!(text_runs(&edited, sid, oid), expected); @@ -588,10 +648,10 @@ fn automatic_titles_follow_native_line_and_utf16_limits() { }; let (sid, oid) = check(&source, &expected); let cleared = - onestore::replace_text(&source, sid, oid, 0..text.encode_utf16().count() as u32, "") + replace_text(&source, sid, oid, 0..text.encode_utf16().count() as u32, "") .unwrap(); check(&cleared, ""); - let written = onestore::replace_text(&cleared, sid, oid, 0..0, " new 🦀 name ").unwrap(); + let written = replace_text(&cleared, sid, oid, 0..0, " new 🦀 name ").unwrap(); check(&written, "new 🦀 name"); } } @@ -641,7 +701,7 @@ fn automatic_titles_follow_native_rtl_and_attachment_order() { if node.extra[0].iter().any(|field| field.id == 0x88001cb4) { continue; } - let changed = onestore::replace_text(&source, sid, *oid, 0..0, "Edited ").unwrap(); + let changed = replace_text(&source, sid, *oid, 0..0, "Edited ").unwrap(); let store = Store::parse(&changed).unwrap(); let current = RevisionIndex::parse(&store).unwrap(); let after = Document::parse(¤t).unwrap(); @@ -675,8 +735,8 @@ fn automatic_titles_follow_native_rtl_and_attachment_order() { fn an_emptied_final_run_retains_its_insertion_style() { let (sid, oid) = target(SOURCE); let before = text_runs(SOURCE, sid, oid); - let erased = onestore::replace_text(SOURCE, sid, oid, 22..27, "").unwrap(); - let edited = onestore::replace_text(&erased, sid, oid, 22..22, "a").unwrap(); + let erased = replace_text(SOURCE, sid, oid, 22..27, "").unwrap(); + let edited = replace_text(&erased, sid, oid, 22..22, "a").unwrap(); let mut expected = before.clone(); expected[3]["text"] = "a".into(); assert_eq!(text_runs(&edited, sid, oid), expected); @@ -739,12 +799,12 @@ fn title_text_and_navigation_caches_publish_together() { .collect::>(), ]) }; - let checkpoint = checkpoint::pending(&source, sid, *oid, 0x14001d7a); + let checkpoint = checkpoint::pending(&source, sid, *oid); for (source, write_limit) in [(source.as_slice(), 17), (checkpoint.as_slice(), 257)] { let before = state(source); for replacement in ["Renamed 🦀 日本語", ""] { let edit = - onestore::PreparedEdit::text(source, sid, *oid, 0..end, replacement).unwrap(); + text_transaction(source, sid, *oid, 0..end, replacement).unwrap(); let disk = |fail_at| Disk { visible: source.to_vec(), durable: source.to_vec(), @@ -790,7 +850,7 @@ fn title_text_and_navigation_caches_publish_together() { ); } assert_other_objects_preserved(&old, ¤t, *oid); - for at in source.len().div_ceil(193)..=success.operation { + for at in 1..=success.operation { let mut interrupted = disk(Some(at)); let error = edit.commit(&mut interrupted).unwrap_err(); let observed = state(&interrupted.durable); @@ -827,7 +887,7 @@ fn native_conflict_pages_are_readable_but_not_random_edit_targets() { if !matches!(node.kind, Kind::RichText { .. }) { continue; } - assert!(onestore::replace_text(&source, *sid, *oid, 0..0, "edit").is_err()); + assert!(replace_text(&source, *sid, *oid, 0..0, "edit").is_err()); conflicts += 1; } } @@ -837,7 +897,7 @@ fn native_conflict_pages_are_readable_but_not_random_edit_targets() { #[test] fn interrupted_text_commits_never_publish_mismatched_run_boundaries() { let (sid, oid) = target(SOURCE); - let checkpoint = checkpoint::pending(SOURCE, sid, oid, 0x14001d7a); + let checkpoint = checkpoint::pending(SOURCE, sid, oid); for source in [SOURCE, checkpoint.as_slice()] { let make_disk = |fail_at, random| Disk { visible: source.to_vec(), @@ -849,14 +909,14 @@ fn interrupted_text_commits_never_publish_mismatched_run_boundaries() { }; let before = text_runs(source, sid, oid); let mut success = make_disk(None, 1); - onestore::commit_text(&mut success, source, sid, oid, 0..23, "🐈 mixed edit").unwrap(); + commit_text(&mut success, source, sid, oid, 0..23, "🐈 mixed edit").unwrap(); let after = text_runs(&success.durable, sid, oid); assert_ne!(before, after); - for at in source.len().div_ceil(193)..=success.operation { + for at in 1..=success.operation { for seed in [1, 42] { let mut disk = make_disk(Some(at), seed); let error = - onestore::commit_text(&mut disk, source, sid, oid, 0..23, "🐈 mixed edit") + commit_text(&mut disk, source, sid, oid, 0..23, "🐈 mixed edit") .unwrap_err(); let observed = text_runs(&disk.durable, sid, oid); match error.state { @@ -906,7 +966,7 @@ fn empty_and_legacy_native_text_gain_unicode_without_losing_existing_properties( let properties = PropertySets::parse(blob).unwrap(); assert!(!properties.sets[0].iter().any(|p| p.id == 0x1c001c22)); let expected = format!("🦀 café {original}"); - let written = onestore::replace_text(&source, sid, oid, 0..0, "🦀 café ").unwrap(); + let written = replace_text(&source, sid, oid, 0..0, "🦀 café ").unwrap(); let after_store = Store::parse(&written).unwrap(); assert!(after_store.checksum_mismatches.is_empty()); let after = RevisionIndex::parse(&after_store).unwrap(); @@ -953,12 +1013,12 @@ fn empty_and_legacy_native_text_gain_unicode_without_losing_existing_properties( random, }; let mut success = disk(None, 1); - onestore::commit_text(&mut success, &source, sid, oid, 0..0, "🦀 café ").unwrap(); - for at in source.len().div_ceil(193)..=success.operation { + commit_text(&mut success, &source, sid, oid, 0..0, "🦀 café ").unwrap(); + for at in 1..=success.operation { for seed in [1, 42] { let mut interrupted = disk(Some(at), seed); let error = - onestore::commit_text(&mut interrupted, &source, sid, oid, 0..0, "🦀 café ") + commit_text(&mut interrupted, &source, sid, oid, 0..0, "🦀 café ") .unwrap_err(); let actual = text_runs(&interrupted.durable, sid, oid); match error.state { diff --git a/crates/onestore/tests/formatting.rs b/crates/onestore/tests/formatting.rs index 2fbdff3dc7eae65977f8049a722c0d9bef34766a..168ea29a9c670856cd3ef4558738e4a3756724f5 100644 --- a/crates/onestore/tests/formatting.rs +++ b/crates/onestore/tests/formatting.rs @@ -1,10 +1,13 @@ +#[path = "support/ops.rs"] +mod ops; #[path = "support/current.rs"] mod current; #[path = "support/disk.rs"] mod disk; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, TextAttribute as A, + ExGuid, RevisionIndex, Store, TextAttribute as A, document::{Document, Kind}, + op::PageOp, }; use serde_json::{Value, json}; fn target(source: &[u8]) -> (ExGuid, ExGuid) { @@ -85,13 +88,7 @@ fn overlapping_unicode_format_edits_match_an_independent_character_model() { json!(0x00ff00_u32), ), }; - let edit = PreparedEdit::format( - &source, - sid, - id, - offsets[start]..offsets[end], - std::slice::from_ref(&attribute), - ) + let edit = ops::page_op(&source, sid, PageOp::Format { text: id, range: offsets[start]..offsets[end], set: std::slice::from_ref(&attribute).to_vec(), clear: Vec::new() }) .unwrap(); for (_, style) in &mut expected[start..end] { style[key] = value.clone(); @@ -102,13 +99,7 @@ fn overlapping_unicode_format_edits_match_an_independent_character_model() { "seed {seed}, step {step}" ); assert_eq!( - PreparedEdit::format( - edit.as_bytes(), - sid, - id, - offsets[start]..offsets[end], - &[attribute] - ) + ops::page_op(edit.as_bytes(), sid, PageOp::Format { text: id, range: offsets[start]..offsets[end], set: vec![attribute], clear: Vec::new() }) .unwrap() .as_bytes(), edit.as_bytes() @@ -130,21 +121,9 @@ fn overlapping_unicode_format_edits_match_an_independent_character_model() { fn script_positions_are_exclusive_and_explicit_false_overrides_true() { let source = onestore::create_section("script.one", "abc", "Author").unwrap(); let (sid, id) = target(&source); - let superscript = PreparedEdit::format( - &source, - sid, - id, - 0..3, - &[A::Superscript(true), A::Bold(true)], - ) + let superscript = ops::page_op(&source, sid, PageOp::Format { text: id, range: 0..3, set: vec![A::Superscript(true), A::Bold(true)], clear: Vec::new() }) .unwrap(); - let subscript = PreparedEdit::format( - superscript.as_bytes(), - sid, - id, - 1..2, - &[A::Subscript(true), A::Bold(false)], - ) + let subscript = ops::page_op(superscript.as_bytes(), sid, PageOp::Format { text: id, range: 1..2, set: vec![A::Subscript(true), A::Bold(false)], clear: Vec::new() }) .unwrap(); let chars = characters(subscript.as_bytes(), sid, id); assert_eq!(chars[0].1["superscript"], true); @@ -159,15 +138,9 @@ fn script_positions_are_exclusive_and_explicit_false_overrides_true() { fn empty_paragraph_style_is_used_by_later_text_edits() { let source = onestore::create_section("empty.one", "", "Author").unwrap(); let (sid, id) = target(&source); - let formatted = PreparedEdit::format( - &source, - sid, - id, - 0..0, - &[A::Italic(true), A::FontSize(18.0)], - ) + let formatted = ops::page_op(&source, sid, PageOp::Format { text: id, range: 0..0, set: vec![A::Italic(true), A::FontSize(18.0)], clear: Vec::new() }) .unwrap(); - let filled = PreparedEdit::text(formatted.as_bytes(), sid, id, 0..0, "Added 🦀").unwrap(); + let filled = ops::page_op(formatted.as_bytes(), sid, PageOp::Text { text: id, range: 0..0, with: "Added 🦀".into() }).unwrap(); for (_, format) in characters(filled.as_bytes(), sid, id) { assert_eq!(format["italic"], true); assert_eq!(format["font_size"], 18.0); @@ -179,7 +152,7 @@ fn invalid_ranges_attributes_and_fields_are_rejected() { let (sid, id) = target(&source); for (start, end) in [(2, 3), (1, 2), (0, 8), (2, 2), (3, 1), (1, 1)] { let range = start..end; - assert!(PreparedEdit::format(&source, sid, id, range, &[A::Bold(true)]).is_err()); + assert!(ops::page_op(&source, sid, PageOp::Format { text: id, range, set: vec![A::Bold(true)], clear: Vec::new() }).is_err()); } for attributes in [ vec![], @@ -188,24 +161,18 @@ fn invalid_ranges_attributes_and_fields_are_rejected() { vec![A::Font("".into())], vec![A::Font("a\0b".into())], ] { - assert!(PreparedEdit::format(&source, sid, id, 0..4, &attributes).is_err()); + assert!(ops::page_op(&source, sid, PageOp::Format { text: id, range: 0..4, set: attributes.to_vec(), clear: Vec::new() }).is_err()); } for size in [f32::NAN, f32::INFINITY, 0.0, 5.5, 130.5, 144.0, 144.5, 12.1] { - assert!(PreparedEdit::format(&source, sid, id, 0..4, &[A::FontSize(size)]).is_err()); + assert!(ops::page_op(&source, sid, PageOp::Format { text: id, range: 0..4, set: vec![A::FontSize(size)], clear: Vec::new() }).is_err()); } - assert!(PreparedEdit::format(&source, sid, ExGuid::default(), 0..4, &[A::Bold(true)]).is_err()); + assert!(ops::page_op(&source, sid, PageOp::Format { text: ExGuid::default(), range: 0..4, set: vec![A::Bold(true)], clear: Vec::new() }).is_err()); } #[test] fn formatting_publication_faults_preserve_complete_old_or_new_styles() { let source = onestore::create_section("atomic.one", "Before 🦀 after", "Author").unwrap(); let (sid, id) = target(&source); - let edit = PreparedEdit::format( - &source, - sid, - id, - 2..10, - &[A::Bold(true), A::Color(Some([8, 64, 128]))], - ) + let edit = ops::page_op(&source, sid, PageOp::Format { text: id, range: 2..10, set: vec![A::Bold(true), A::Color(Some([8, 64, 128]))], clear: Vec::new() }) .unwrap(); let before = current::current(&source); let after = current::current(edit.as_bytes()); @@ -251,7 +218,7 @@ fn export_native_formatting_candidates() { let mut save = |name: &str, source: &[u8], sid, id, range: std::ops::Range, attributes: &[A]| { let prepared = - PreparedEdit::format(source, sid, id, range.clone(), attributes).unwrap(); + ops::page_op(source, sid, PageOp::Format { text: id, range: range.clone(), set: attributes.to_vec(), clear: Vec::new() }).unwrap(); fs::write(output.join(format!("{name}.one")), prepared.as_bytes()).unwrap(); manifest.push( json!({"name":name,"space":sid,"object":id,"range":range,"attributes":attributes}), @@ -329,21 +296,9 @@ fn export_native_formatting_candidates() { ); let empty = onestore::create_section("empty.one", "", "Author").unwrap(); let (empty_sid, empty_id) = target(&empty); - let formatted = PreparedEdit::format( - &empty, - empty_sid, - empty_id, - 0..0, - &[A::FontSize(18.0), A::Italic(true)], - ) + let formatted = ops::page_op(&empty, empty_sid, PageOp::Format { text: empty_id, range: 0..0, set: vec![A::FontSize(18.0), A::Italic(true)], clear: Vec::new() }) .unwrap(); - let typed = PreparedEdit::text( - formatted.as_bytes(), - empty_sid, - empty_id, - 0..0, - "Typed café 🦀", - ) + let typed = ops::page_op(formatted.as_bytes(), empty_sid, PageOp::Text { text: empty_id, range: 0..0, with: "Typed café 🦀".into() }) .unwrap(); fs::write(output.join("empty-then-type.one"), typed.as_bytes()).unwrap(); let native = include_bytes!("../../../corpus/native-ink/20260905-ui/notebook/synthetic.one"); diff --git a/crates/onestore/tests/insertion.rs b/crates/onestore/tests/insertion.rs index cd04819fdb60c9c6e6adabf559eb69d00f3a7cd0..f24b03b3d26019258ee549e4ebb61c1a75660465 100644 --- a/crates/onestore/tests/insertion.rs +++ b/crates/onestore/tests/insertion.rs @@ -4,11 +4,65 @@ mod checkpoint; mod current; #[path = "support/disk.rs"] mod disk; +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, Insertion, PreparedEdit, RevisionIndex, Store, TextAttribute as A, + ExGuid, RevisionIndex, Store, TextAttribute as A, document::{Document, Kind}, + op::{Edit, Op, PageOp}, }; +use std::ops::Range; + +/// Where an insertion goes: a paragraph in a container before a child of it, or last; or a +/// new outline at a point on the page. +#[derive(Clone, Copy)] +enum Place { + In(ExGuid, Option), + Outline(f32, f32), +} + +/// The ops inserting `text` at `place` with `formats` set over UTF-16 ranges of it, and the +/// identities of the new paragraph (or outline) and text. +fn insertion( + space: ExGuid, + place: Place, + text: &str, + formats: &[(Range, Vec)], +) -> (Vec, ExGuid, ExGuid) { + let paragraph = ops::paragraph(text); + let text = paragraph.text().unwrap().id; + let (op, object) = match place { + Place::In(container, before) => { + let id = paragraph.id; + let paragraphs = vec![paragraph]; + ( + PageOp::Insert { + container, + before, + paragraphs, + }, + id, + ) + } + Place::Outline(x, y) => { + let object = ops::outline(x, y, vec![paragraph]); + let id = object.id(); + (PageOp::Add { object, before: None }, id) + } + }; + let mut result = vec![Op::Page { space, op }]; + for (range, set) in formats { + let op = PageOp::Format { + text, + range: range.clone(), + set: set.clone(), + clear: Vec::new(), + }; + result.push(Op::Page { space, op }); + } + (result, object, text) +} fn targets(source: &[u8]) -> (ExGuid, ExGuid, ExGuid, ExGuid, ExGuid) { let store = Store::parse(source).unwrap(); @@ -34,23 +88,15 @@ fn atomic_formatted_insertions_match_a_character_model_and_preserve_history() { let source = onestore::create_section("rich.one", "Original", "Author").unwrap(); let (sid, page, outline, _, _) = targets(&source); let text = "ab🦀 e\u{301} 東京\rEnd\t!"; - let plain = Insertion::paragraph(outline, None, text, "Author").unwrap(); - assert!( - serde_json::to_value(&plain) - .unwrap() - .get("formats") - .is_none() - ); - let restored: Insertion = serde_json::from_slice(&serde_json::to_vec(&plain).unwrap()).unwrap(); - assert_eq!(plain, restored); - let base = PreparedEdit::insert(&source, sid, &plain).unwrap(); - let base_store = Store::parse(base.as_bytes()).unwrap(); + let (plain, _, plain_text) = insertion(sid, Place::In(outline, None), text, &[]); + let base = ops::edited(&source, plain).unwrap(); + let base_store = Store::parse(&base).unwrap(); let base_index = RevisionIndex::parse(&base_store).unwrap(); let base_doc = Document::parse(&base_index).unwrap(); let base_space = &base_doc.spaces[&sid]; let base_view = &base_space.revisions[&base_space.contexts[&ExGuid::default()]]; let default = - serde_json::to_value(&base_view.text_runs(plain.text_object()).unwrap()[0].format).unwrap(); + serde_json::to_value(&base_view.text_runs(plain_text).unwrap()[0].format).unwrap(); let offsets: Vec = std::iter::once(0) .chain(text.chars().scan(0, |n, c| { *n += c.len_utf16() as u32; @@ -62,12 +108,8 @@ fn atomic_formatted_insertions_match_a_character_model_and_preserve_history() { for seed in 1..=48_u64 { let mut random = seed; let mut expected: Vec<_> = text.chars().map(|c| (c, default.clone())).collect(); - let mut intent = if seed % 2 == 0 { - Insertion::outline(page, 72.0, 144.0, text, "Author").unwrap() - } else { - plain.clone() - }; - // Descending construction also exercises persisted ordering independently of call order. + let mut formats = Vec::new(); + // Descending construction also exercises the order formats apply in. for i in (0..expected.len()).rev() { random ^= random << 13; random ^= random >> 7; @@ -106,18 +148,22 @@ fn atomic_formatted_insertions_match_a_character_model_and_preserve_history() { json!({"bold":false,"color":0xff000000_u32,"highlight":0xff000000_u32}), ), }; - intent = intent - .with_formatting(offsets[i]..offsets[i + 1], &attributes) - .unwrap(); + formats.push((offsets[i]..offsets[i + 1], attributes)); for (key, value) in values.as_object().unwrap() { expected[i].1[key] = value.clone(); } } - let restored: Insertion = - serde_json::from_slice(&serde_json::to_vec(&intent).unwrap()).unwrap(); - assert_eq!(restored, intent); - let edited = PreparedEdit::insert(&source, sid, &restored).unwrap(); - let store = Store::parse(edited.as_bytes()).unwrap(); + let place = if seed % 2 == 0 { + Place::Outline(72.0, 144.0) + } else { + Place::In(outline, None) + }; + let (ops, _, inserted) = insertion(sid, place, text, &formats); + let edit = Edit { at: ops::now(), ops }; + let restored: Edit = serde_json::from_slice(&serde_json::to_vec(&edit).unwrap()).unwrap(); + assert_eq!(restored, edit); + let edited = ops::edited(&source, restored.ops).unwrap(); + let store = Store::parse(&edited).unwrap(); assert_eq!( store.header.transaction_count, old_store.header.transaction_count + 1 @@ -139,8 +185,11 @@ fn atomic_formatted_insertions_match_a_character_model_and_preserve_history() { let document = Document::parse(&index).unwrap(); let space = &document.spaces[&sid]; let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + if let Place::Outline(..) = place { + assert!(view.nodes[&page].children.len() > 1); + } let actual: Vec<_> = view - .text_runs(intent.text_object()) + .text_runs(inserted) .unwrap() .into_iter() .flat_map(|run| { @@ -152,88 +201,34 @@ fn atomic_formatted_insertions_match_a_character_model_and_preserve_history() { } } -#[test] -fn invalid_or_forged_insertion_formats_fail_before_source_parsing() { - use serde_json::json; - let source = onestore::create_section("invalid-rich.one", "Original", "Author").unwrap(); - let (sid, _, outline, _, _) = targets(&source); - let plain = Insertion::paragraph(outline, None, "a🦀e\u{301}b", "Author").unwrap(); - for (start, end) in [(0, 0), (2, 3), (1, 2), (4, 3), (0, 7)] { - let range = start..end; - assert!(plain.with_formatting(range, &[A::Bold(true)]).is_err()); - } - for attributes in [ - vec![], - vec![A::Bold(true), A::Bold(false)], - vec![A::FontSize(f32::NAN)], - vec![A::FontSize(5.5)], - vec![A::Font("a\0b".into())], - vec![A::Superscript(true), A::Subscript(true)], - ] { - assert!(plain.with_formatting(1..3, &attributes).is_err()); - } - let formatted = plain.with_formatting(1..3, &[A::Bold(true)]).unwrap(); - for range in [0..3, 1..3, 1..4] { - assert!( - formatted - .with_formatting(range, &[A::Italic(true)]) - .is_err() - ); - } - let encoded = serde_json::to_value(&formatted).unwrap(); - for mutate in 0..6 { - let mut value = encoded.clone(); - match mutate { - 0 => value["formats"][0]["guid"] = serde_json::to_value([0; 16]).unwrap(), - 1 => value["formats"][0]["guid"] = value["guid"].clone(), - 2 => { - let duplicate = value["formats"][0].clone(); - value["formats"].as_array_mut().unwrap().push(duplicate); - } - 3 => value["formats"][0]["range"] = json!({"start":2,"end":3}), - 4 => value["formats"][0]["attributes"] = json!([]), - _ => value["formats"][0]["range"] = json!({"start":0,"end":u32::MAX}), - } - let forged: Insertion = serde_json::from_value(value).unwrap(); - let error = PreparedEdit::insert(&[], sid, &forged).err().unwrap(); - assert_eq!( - error, - PreparedEdit::insert(&source, sid, &forged).err().unwrap() - ); - } -} - #[test] fn formatted_empty_insertions_preserve_the_insertion_style_for_later_typing() { let source = onestore::create_section("empty-rich.one", "Original", "Author").unwrap(); - let (sid, page, outline, _, _) = targets(&source); - for plain in [ - Insertion::outline(page, 72.0, 144.0, "", "Author").unwrap(), - Insertion::paragraph(outline, None, "", "Author").unwrap(), - ] { - let intent = plain - .with_formatting(0..0, &[A::Italic(true), A::FontSize(18.0)]) - .unwrap(); - assert!(intent.with_formatting(0..0, &[A::Bold(true)]).is_err()); - let inserted = PreparedEdit::insert(&source, sid, &intent).unwrap(); - let typed = PreparedEdit::text( - inserted.as_bytes(), + let (sid, _, outline, _, _) = targets(&source); + for place in [Place::Outline(72.0, 144.0), Place::In(outline, None)] { + let style = [(0..0, vec![A::Italic(true), A::FontSize(18.0)])]; + let (ops, _, text) = insertion(sid, place, "", &style); + let inserted = ops::edited(&source, ops).unwrap(); + let typed = ops::page_edited( + &inserted, sid, - intent.text_object(), - 0..0, - "Typed 🦀", + vec![PageOp::Text { + text, + range: 0..0, + with: "Typed 🦀".into(), + }], ) .unwrap(); - for (bytes, text) in [(inserted.as_bytes(), ""), (typed.as_bytes(), "Typed 🦀")] { + for (bytes, expected) in [(&inserted, ""), (&typed, "Typed 🦀")] { let store = Store::parse(bytes).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); index.validate_current().unwrap(); let document = Document::parse(&index).unwrap(); let space = &document.spaces[&sid]; let view = &space.revisions[&space.contexts[&ExGuid::default()]]; - let runs = view.text_runs(intent.text_object()).unwrap(); + let runs = view.text_runs(text).unwrap(); assert_eq!(runs.len(), 1); - assert_eq!(runs[0].text, text); + assert_eq!(runs[0].text, expected); assert_eq!(runs[0].format.italic, Some(true)); assert_eq!(runs[0].format.font_size, Some(18.0)); } @@ -263,11 +258,9 @@ fn export_native_formatted_insertions() { ("empty", generated.as_slice(), true), ("empty-typed", generated.as_slice(), true), ] { - let (sid, page, outline, paragraph, _) = targets(source); - let insertion = match name { - "native-paragraph" => { - Insertion::paragraph(outline, Some(paragraph), text, "Rich author") - } + let (sid, _, outline, paragraph, _) = targets(source); + let place = match name { + "native-paragraph" => Place::In(outline, Some(paragraph)), "native-cell" => { let store = Store::parse(source).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); @@ -280,24 +273,14 @@ fn export_native_formatted_insertions() { .find(|(_, node)| matches!(node.kind, Kind::Cell { .. })) .unwrap() .0; - Insertion::paragraph(cell, None, text, "Rich author") + Place::In(cell, None) } - _ => Insertion::outline( - page, - 72.0, - 144.0, - if empty { "" } else { text }, - "Rich author", - ), - } - .unwrap(); - let mut intent = insertion; - if empty { - intent = intent - .with_formatting(0..0, &[A::Italic(true), A::FontSize(18.0)]) - .unwrap(); + _ => Place::Outline(72.0, 144.0), + }; + let formats = if empty { + vec![(0..0, vec![A::Italic(true), A::FontSize(18.0)])] } else { - for (label, attributes) in [ + [ ("Bold", vec![A::Bold(true), A::Underline(true)]), ("🦀", vec![A::Font("Arial".into()), A::FontSize(13.5)]), ("italic", vec![A::Italic(true), A::Strike(true)]), @@ -319,36 +302,33 @@ fn export_native_formatted_insertions() { A::Highlight(None), ], ), - ] { + ] + .into_iter() + .map(|(label, attributes)| { let start = text[..text.find(label).unwrap()].encode_utf16().count() as u32; - intent = intent - .with_formatting( - start..start + label.encode_utf16().count() as u32, - &attributes, - ) - .unwrap(); - } - } - let inserted = PreparedEdit::insert(source, sid, &intent).unwrap(); - let bytes = if name == "empty-typed" { - PreparedEdit::text( - inserted.as_bytes(), - sid, - intent.text_object(), - 0..0, - "Typed café 🦀", - ) - .unwrap() - .as_bytes() - .to_vec() - } else { - inserted.as_bytes().to_vec() + (start..start + label.encode_utf16().count() as u32, attributes) + }) + .collect() }; + let (mut ops, object, inserted) = + insertion(sid, place, if empty { "" } else { text }, &formats); + if name == "empty-typed" { + let op = PageOp::Text { + text: inserted, + range: 0..0, + with: "Typed café 🦀".into(), + }; + ops.push(Op::Page { space: sid, op }); + } + let edit = Edit { at: ops::now(), ops }; + let bytes = ops::edited(source, edit.ops.clone()).unwrap(); current::current(&bytes); let notebook = output.join(name); fs::create_dir(¬ebook).unwrap(); fs::write(notebook.join("synthetic.one"), &bytes).unwrap(); - manifest.push(serde_json::json!({"name":name,"space":sid,"intent":intent})); + manifest.push(serde_json::json!({ + "name": name, "space": sid, "object": object, "text": inserted, "edit": edit + })); } fs::write( output.join("manifest.json"), @@ -361,23 +341,15 @@ fn export_native_formatted_insertions() { fn paragraph_and_outline_insertions_publish_metadata_and_references_together() { let source = onestore::create_section("insertion.one", "Original", "Original author").unwrap(); let (sid, page, outline, paragraph, original_text) = targets(&source); - for (intent, expected_parent, expected_x, expected_y) in [ - ( - Insertion::paragraph(outline, Some(paragraph), "First 🦀\rSecond", "New author") - .unwrap(), - outline, - None, - None, - ), - ( - Insertion::outline(page, 144.0, 18.0, "First 🦀\rSecond", "New author").unwrap(), - page, - Some(144.0), - Some(18.0), - ), + for (place, expected_parent, expected_x, expected_y) in [ + (Place::In(outline, Some(paragraph)), outline, None, None), + (Place::Outline(144.0, 18.0), page, Some(144.0), Some(18.0)), ] { - let prepared = PreparedEdit::insert(&source, sid, &intent).unwrap(); - let store = Store::parse(prepared.as_bytes()).unwrap(); + let (ops, object, text) = insertion(sid, place, "First 🦀\rSecond", &[]); + let transaction = ops::transaction(&source, "New author", ops).unwrap().unwrap(); + let mut written = source.clone(); + transaction.apply(&mut written).unwrap(); + let store = Store::parse(&written).unwrap(); assert_eq!( store.header.transaction_count, Store::parse(&source).unwrap().header.transaction_count + 1 @@ -387,18 +359,14 @@ fn paragraph_and_outline_insertions_publish_metadata_and_references_together() { let document = Document::parse(&index).unwrap(); let space = &document.spaces[&sid]; let view = &space.revisions[&space.contexts[&ExGuid::default()]]; - assert!( - view.nodes[&expected_parent] - .children - .contains(&intent.object()) - ); - assert_eq!(view.nodes[&intent.object()].layout.x, expected_x); - assert_eq!(view.nodes[&intent.object()].layout.y, expected_y); + assert!(view.nodes[&expected_parent].children.contains(&object)); + assert_eq!(view.nodes[&object].layout.x, expected_x); + assert_eq!(view.nodes[&object].layout.y, expected_y); assert!( matches!(&view.nodes[&original_text].kind, Kind::RichText { text, .. } if text == "Original") ); assert!( - matches!(&view.nodes[&intent.text_object()].kind, Kind::RichText { text, .. } if text == "First 🦀\rSecond") + matches!(&view.nodes[&text].kind, Kind::RichText { text, .. } if text == "First 🦀\rSecond") ); assert!( matches!(&view.nodes[&page].kind, Kind::Page { alternate_title, .. } if alternate_title.as_deref() == Some("First 🦀")) @@ -411,7 +379,7 @@ fn paragraph_and_outline_insertions_publish_metadata_and_references_together() { .values() .any(|node| matches!(&node.kind, Kind::Author { name } if name.as_deref() == Some("New author"))) ); - let runs = view.text_runs(intent.text_object()).unwrap(); + let runs = view.text_runs(text).unwrap(); assert_eq!(runs.len(), 1); assert_eq!(runs[0].format.font.as_deref(), Some("Calibri")); assert_eq!(runs[0].format.font_size, Some(11.0)); @@ -434,26 +402,20 @@ fn repeated_insertions_and_formatting_share_immutable_objects() { let (sid, _, outline, _, _) = targets(&source); let mut texts = Vec::new(); for _ in 0..12 { - let insertion = - Insertion::paragraph(outline, None, "Repeated paragraph", "Same author").unwrap(); - source = PreparedEdit::insert(&source, sid, &insertion) + let (ops, _, text) = insertion(sid, Place::In(outline, None), "Repeated paragraph", &[]); + let transaction = ops::transaction(&source, "Same author", ops).unwrap().unwrap(); + transaction.apply(&mut source).unwrap(); + let bold = PageOp::Format { + text, + range: 1..8, + set: vec![A::Bold(true), A::FontSize(20.0)], + clear: Vec::new(), + }; + let transaction = ops::transaction(&source, "Same author", vec![Op::Page { space: sid, op: bold }]) .unwrap() - .as_bytes() - .to_vec(); - source = PreparedEdit::format( - &source, - sid, - insertion.text_object(), - 1..8, - &[ - onestore::TextAttribute::Bold(true), - onestore::TextAttribute::FontSize(20.0), - ], - ) - .unwrap() - .as_bytes() - .to_vec(); - texts.push(insertion.text_object()); + .unwrap(); + transaction.apply(&mut source).unwrap(); + texts.push(text); } let store = Store::parse(&source).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); @@ -494,103 +456,54 @@ fn repeated_insertions_and_formatting_share_immutable_objects() { fn serialized_insertions_rebase_with_the_same_objects_and_preserve_remote_edits() { let source = onestore::create_section("rebase.one", "Original", "Author").unwrap(); let (sid, _, outline, paragraph, text) = targets(&source); - let intent = Insertion::paragraph(outline, Some(paragraph), "Inserted", "Author") - .unwrap() - .with_formatting(0..3, &[A::Bold(true), A::Color(Some([12, 34, 56]))]) - .unwrap(); - let encoded = serde_json::to_vec(&intent).unwrap(); - let restored: Insertion = serde_json::from_slice(&encoded).unwrap(); - assert_eq!(intent.object(), restored.object()); - assert_eq!(intent.text_object(), restored.text_object()); - assert_eq!(intent, restored); - let original_preparation = PreparedEdit::insert(&source, sid, &intent).unwrap(); - let remote = PreparedEdit::text(&source, sid, text, 0..0, "Remote ").unwrap(); - let updated = PreparedEdit::insert(remote.as_bytes(), sid, &restored).unwrap(); - let store = Store::parse(updated.as_bytes()).unwrap(); + let formats = [(0..3, vec![A::Bold(true), A::Color(Some([12, 34, 56]))])]; + let (ops, object, inserted) = + insertion(sid, Place::In(outline, Some(paragraph)), "Inserted", &formats); + let encoded = serde_json::to_vec(&ops).unwrap(); + let restored: Vec = serde_json::from_slice(&encoded).unwrap(); + assert_eq!(ops, restored); + let original = ops::edited(&source, ops).unwrap(); + let remote = ops::page_edited( + &source, + sid, + vec![PageOp::Text { + text, + range: 0..0, + with: "Remote ".into(), + }], + ) + .unwrap(); + let updated = ops::edited(&remote, restored.clone()).unwrap(); + let store = Store::parse(&updated).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); let space = &document.spaces[&sid]; let view = &space.revisions[&space.contexts[&ExGuid::default()]]; - assert_eq!( - view.nodes[&outline].children, - [restored.object(), paragraph] - ); + assert_eq!(view.nodes[&outline].children, [object, paragraph]); assert!( matches!(&view.nodes[&text].kind, Kind::RichText { text, .. } if text == "Remote Original") ); assert!( - matches!(&view.nodes[&restored.text_object()].kind, Kind::RichText { text, .. } if text == "Inserted") + matches!(&view.nodes[&inserted].kind, Kind::RichText { text, .. } if text == "Inserted") ); - let runs = view.text_runs(restored.text_object()).unwrap(); + let runs = view.text_runs(inserted).unwrap(); assert_eq!(runs[0].text, "Ins"); assert_eq!(runs[0].format.bold, Some(true)); assert_eq!(runs[0].format.color, Some(0x38220c)); assert_eq!(runs[1].text, "erted"); assert_ne!(runs[1].format.bold, Some(true)); - assert!(PreparedEdit::insert(original_preparation.as_bytes(), sid, &restored).is_err()); - assert!(PreparedEdit::insert(updated.as_bytes(), sid, &restored).is_err()); -} - -#[test] -fn repositioning_preserves_intent_identity_and_kind() { - let source = onestore::create_section("placement.one", "Original", "Author").unwrap(); - let (sid, page, outline, paragraph, _) = targets(&source); - let p = Insertion::paragraph(outline, Some(paragraph), "Inserted", "Author") - .unwrap() - .with_formatting(1..4, &[A::Italic(true)]) - .unwrap(); - let o = Insertion::outline(page, 144.0, 144.0, "Inserted", "Author") - .unwrap() - .with_formatting(0..8, &[A::FontSize(18.0)]) - .unwrap(); - assert!(p.reposition_outline(page, 72.0, 72.0).is_err()); - assert!(o.reposition_paragraph(outline, None).is_err()); - assert!(p.reposition_paragraph(ExGuid::default(), None).is_err()); - assert!(o.reposition_outline(page, f32::NAN, 72.0).is_err()); - for (original, moved) in [ - (&p, p.reposition_paragraph(outline, None).unwrap()), - (&o, o.reposition_outline(page, 288.0, 360.0).unwrap()), - ] { - let mut before = serde_json::to_value(original).unwrap(); - let mut after = serde_json::to_value(&moved).unwrap(); - for name in ["parent", "placement"] { - before.as_object_mut().unwrap().remove(name); - after.as_object_mut().unwrap().remove(name); - } - assert_eq!(before, after); - assert_eq!(original.object(), moved.object()); - assert_eq!(original.text_object(), moved.text_object()); - let restored: Insertion = - serde_json::from_value(serde_json::to_value(&moved).unwrap()).unwrap(); - let prepared = PreparedEdit::insert(&source, sid, &restored).unwrap(); - let store = Store::parse(prepared.as_bytes()).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - index.validate_current().unwrap(); - let doc = Document::parse(&index).unwrap(); - let space = &doc.spaces[&sid]; - let view = &space.revisions[&space.contexts[&ExGuid::default()]]; - if original == &p { - assert_eq!(view.nodes[&outline].children, [paragraph, moved.object()]); - } else { - assert_eq!( - ( - view.nodes[&moved.object()].layout.x, - view.nodes[&moved.object()].layout.y - ), - (Some(288.0), Some(360.0)) - ); - } - } + assert!(ops::edited(&original, restored.clone()).is_err()); + assert!(ops::edited(&updated, restored).is_err()); } #[test] #[cfg(any(unix, windows))] -fn filesystem_insertion_uses_the_prepared_identity_and_rejects_stale_replay() { +fn filesystem_insertion_commits_once_and_rejects_stale_replay() { use std::{fs, io::Write}; let source = onestore::create_section("file.one", "Original", "Author").unwrap(); let (sid, _, outline, _, _) = targets(&source); - let intent = Insertion::paragraph(outline, None, "File insertion", "Author").unwrap(); - let path = std::env::temp_dir().join(format!("onestore-insertion-{}.one", intent.object())); + let (ops, object, _) = insertion(sid, Place::In(outline, None), "File insertion", &[]); + let path = std::env::temp_dir().join(format!("onestore-insertion-{object}.one")); let mut file = fs::OpenOptions::new() .write(true) .create_new(true) @@ -599,60 +512,71 @@ fn filesystem_insertion_uses_the_prepared_identity_and_rejects_stale_replay() { file.write_all(&source).unwrap(); file.sync_all().unwrap(); drop(file); - let edit = PreparedEdit::insert(&source, sid, &intent).unwrap(); - let result = edit.commit_file(&path); + let transaction = ops::transaction(&source, "Author", ops).unwrap().unwrap(); + let result = transaction.commit_file(&path); let written = onestore::read_file(&path).unwrap(); - let repeated = edit.commit_file(&path).unwrap_err(); + let repeated = transaction.commit_file(&path).unwrap_err(); fs::remove_file(path).unwrap(); result.unwrap(); - assert_eq!(written, edit.as_bytes()); + let mut expected = source.clone(); + transaction.apply(&mut expected).unwrap(); + assert_eq!(written, expected); assert_eq!(repeated.state, onestore::CommitState::NotCommitted); assert_eq!(repeated.error.kind(), std::io::ErrorKind::ResourceBusy); } #[test] -fn anchors_targets_serialized_identities_and_text_are_validated_before_publication() { +fn anchors_targets_identities_and_text_are_validated_before_publication() { let source = onestore::create_section("invalid.one", "Original", "Author").unwrap(); - let (sid, page, outline, paragraph, text) = targets(&source); - assert!(Insertion::outline(page, f32::NAN, 0.0, "Text", "Author").is_err()); - assert!(Insertion::outline(page, 0.0, f32::INFINITY, "Text", "Author").is_err()); + let (sid, page, outline, _, text) = targets(&source); + let refused = |source: &[u8], place, content: &str| { + ops::edited(source, insertion(sid, place, content, &[]).0).is_err() + }; + assert!(refused(&source, Place::Outline(f32::NAN, 0.0), "Text")); + assert!(refused(&source, Place::Outline(0.0, f32::INFINITY), "Text")); for content in ["a\0b", "a\nb", "a\u{fffc}b"] { - assert!(Insertion::paragraph(outline, None, content, "Author").is_err()); + assert!(refused(&source, Place::In(outline, None), content)); } - assert!(Insertion::paragraph(outline, None, "Text", "a\0b").is_err()); - for intent in [ - Insertion::paragraph(outline, Some(text), "Text", "Author").unwrap(), - Insertion::paragraph(text, None, "Text", "Author").unwrap(), - Insertion::paragraph(page, None, "Text", "Author").unwrap(), - Insertion::outline(paragraph, 0.0, 0.0, "Text", "Author").unwrap(), + assert!( + ops::transaction(&source, "a\0b", insertion(sid, Place::In(outline, None), "Text", &[]).0) + .is_err() + ); + for place in [ + Place::In(outline, Some(text)), + Place::In(text, None), + Place::In(page, None), ] { - assert!(PreparedEdit::insert(&source, sid, &intent).is_err()); + assert!(refused(&source, place, "Text")); } - let intent = Insertion::paragraph(outline, None, "Text", "Author").unwrap(); - let created = PreparedEdit::insert(&source, sid, &intent).unwrap(); - let before_missing = - Insertion::paragraph(outline, Some(intent.object()), "Anchored", "Author").unwrap(); - assert!(PreparedEdit::insert(&source, sid, &before_missing).is_err()); - assert!(PreparedEdit::insert(created.as_bytes(), sid, &before_missing).is_ok()); - let mut encoded = serde_json::to_value(&intent).unwrap(); - encoded["parent"] = serde_json::to_value(intent.object()).unwrap(); - let collision: Insertion = serde_json::from_value(encoded).unwrap(); - assert!(PreparedEdit::insert(created.as_bytes(), sid, &collision).is_err()); + let (ops, object, _) = insertion(sid, Place::In(outline, None), "Text", &[]); + let created = ops::edited(&source, ops.clone()).unwrap(); + let before_created = Place::In(outline, Some(object)); + assert!(refused(&source, before_created, "Anchored")); + assert!(!refused(&created, before_created, "Anchored")); + // A new object's identity may not already be on the page. + assert!(ops::edited(&created, ops).is_err()); } #[test] fn insertions_into_nested_paragraphs_and_native_table_cells_preserve_structure() { let source = onestore::create_section("nested.one", "Original", "Author").unwrap(); let (sid, _, outline, paragraph, _) = targets(&source); - let child = Insertion::paragraph(paragraph, None, "Nested", "Author").unwrap(); - let changed = PreparedEdit::insert(&source, sid, &child).unwrap(); - let store = Store::parse(changed.as_bytes()).unwrap(); + let mut nested = ops::paragraph("Nested"); + nested.level = 2; + let child = nested.id; + let insert = PageOp::Insert { + container: paragraph, + before: None, + paragraphs: vec![nested], + }; + let changed = ops::page_edited(&source, sid, vec![insert]).unwrap(); + let store = Store::parse(&changed).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); let space = &document.spaces[&sid]; let view = &space.revisions[&space.contexts[&ExGuid::default()]]; assert_eq!(view.nodes[&outline].children, [paragraph]); - assert_eq!(view.nodes[¶graph].children, [child.object()]); + assert_eq!(view.nodes[¶graph].children, [child]); let source = include_bytes!( "../../../corpus/native/20260905-05/snapshots/07-table/notebook/synthetic.one" ); @@ -669,34 +593,32 @@ fn insertions_into_nested_paragraphs_and_native_table_cells_preserve_structure() }) }) .unwrap(); - let insertion = Insertion::paragraph(cell, None, "Added to cell", "Author").unwrap(); - let changed = PreparedEdit::insert(source, sid, &insertion).unwrap(); - current::current(changed.as_bytes()); + let (ops, _, _) = insertion(sid, Place::In(cell, None), "Added to cell", &[]); + current::current(&ops::edited(source, ops).unwrap()); } #[test] fn insertion_publication_faults_expose_only_complete_graphs_and_title_caches() { let original = onestore::create_section("atomic.one", "Original", "Author").unwrap(); let (sid, _, _, _, text) = targets(&original); - let checkpoint = checkpoint::pending(&original, sid, text, 0x14001d7a); + let checkpoint = checkpoint::pending(&original, sid, text); for source in [ original.as_slice(), include_bytes!("../../../corpus/append/round-01/tx-255/notebook/synthetic.one"), &checkpoint, ] { - let (sid, page, outline, paragraph, _) = targets(source); - for intent in [ - Insertion::paragraph(outline, Some(paragraph), "First 🦀", "New author").unwrap(), - Insertion::outline(page, 0.0, 0.0, "First 🦀", "New author").unwrap(), - ] { - let intent = intent - .with_formatting(0..3, &[A::Bold(true), A::FontSize(18.0)]) - .unwrap() - .with_formatting(6..8, &[A::Italic(true), A::Highlight(Some([255, 255, 0]))]) - .unwrap(); - let edit = PreparedEdit::insert(source, sid, &intent).unwrap(); + let (sid, _, outline, paragraph, _) = targets(source); + for place in [Place::In(outline, Some(paragraph)), Place::Outline(0.0, 0.0)] { + let formats = [ + (0..3, vec![A::Bold(true), A::FontSize(18.0)]), + (6..8, vec![A::Italic(true), A::Highlight(Some([255, 255, 0]))]), + ]; + let (ops, _, _) = insertion(sid, place, "First 🦀", &formats); + let edit = ops::transaction(source, "New author", ops).unwrap().unwrap(); + let mut written = source.to_vec(); + edit.apply(&mut written).unwrap(); let before = current::current(source); - let after = current::current(edit.as_bytes()); + let after = current::current(&written); for write_limit in [17, 4096] { let disk = |fail_at| disk::Disk { visible: source.to_vec(), @@ -708,7 +630,7 @@ fn insertion_publication_faults_expose_only_complete_graphs_and_title_caches() { }; let mut successful = disk(None); edit.commit(&mut successful).unwrap(); - assert_eq!(successful.durable, edit.as_bytes()); + assert_eq!(successful.durable, written); for at in 1..=successful.operation { let mut interrupted = disk(Some(at)); let failure = edit.commit(&mut interrupted).unwrap_err(); diff --git a/crates/onestore/tests/outline.rs b/crates/onestore/tests/outline.rs index e5cea6fb9d28a956ebc49c029a42ef1e1beaeca4..ba143b2a9943619e5edc4f3858607d96726db6a5 100644 --- a/crates/onestore/tests/outline.rs +++ b/crates/onestore/tests/outline.rs @@ -1,6 +1,7 @@ use onestore::{ - ExGuid, OutlineEdit as Edit, PreparedEdit, RevisionIndex, Store, + ExGuid, OutlineEdit as Edit, RevisionIndex, Store, document::{Document, Kind}, + op::PageOp, }; use std::collections::BTreeSet; @@ -8,6 +9,8 @@ use std::collections::BTreeSet; mod current; #[path = "support/disk.rs"] mod disk; +#[path = "support/ops.rs"] +mod ops; const SOURCE: &[u8] = include_bytes!("../../../corpus/outline-edit/before/notebook/synthetic.one"); @@ -62,7 +65,7 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { for (name, sid, object, edit) in cases(&document) { let restored = serde_json::from_value(serde_json::to_value(edit).unwrap()).unwrap(); assert_eq!(edit, restored); - let prepared = PreparedEdit::outline(SOURCE, sid, object, restored).unwrap(); + let prepared = ops::page_op(SOURCE, sid, PageOp::Outline { object, edit: restored }).unwrap(); let after_store = Store::parse(prepared.as_bytes()).unwrap(); assert!(after_store.checksum_mismatches.is_empty()); let after_index = RevisionIndex::parse(&after_store).unwrap(); @@ -139,7 +142,7 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { assert_eq!(old_space.labels, after_index.spaces[space_id].labels); } } - let again = PreparedEdit::outline(prepared.as_bytes(), sid, object, edit).unwrap(); + let again = ops::page_op(prepared.as_bytes(), sid, PageOp::Outline { object, edit }).unwrap(); assert_eq!(again.as_bytes(), prepared.as_bytes()); } } @@ -159,20 +162,15 @@ fn invalid_geometry_and_non_outline_targets_are_rejected() { user_set: true, }, ] { - assert!(PreparedEdit::outline(SOURCE, sid, object, edit).is_err()); + assert!(ops::page_op(SOURCE, sid, PageOp::Outline { object, edit }).is_err()); } } for points in [-1.0, 0.0, 35.999] { assert!( - PreparedEdit::outline( - SOURCE, - sid, - object, - Edit::Width { + ops::page_op(SOURCE, sid, PageOp::Outline { object, edit: Edit::Width { points, user_set: false - } - ) + } }) .is_err() ); } @@ -181,7 +179,7 @@ fn invalid_geometry_and_non_outline_targets_are_rejected() { for (id, node) in &view.nodes { if !matches!(node.kind, Kind::Outline { .. }) { assert!( - PreparedEdit::outline(SOURCE, sid, *id, Edit::Position { x: 72.0, y: 72.0 }) + ops::page_op(SOURCE, sid, PageOp::Outline { object: *id, edit: Edit::Position { x: 72.0, y: 72.0 } }) .is_err() ); } @@ -197,20 +195,20 @@ fn invalid_geometry_and_non_outline_targets_are_rejected() { }, Edit::Collapsed(true), ] { - assert!(PreparedEdit::outline(SOURCE, sid, child, edit).is_err()); + assert!(ops::page_op(SOURCE, sid, PageOp::Outline { object: child, edit }).is_err()); } } } if !matches!(node.kind, Kind::Paragraph { .. }) { - assert!(PreparedEdit::outline(SOURCE, sid, *id, Edit::Collapsed(true)).is_err()); + assert!(ops::page_op(SOURCE, sid, PageOp::Outline { object: *id, edit: Edit::Collapsed(true) }).is_err()); } } assert!( - PreparedEdit::outline(SOURCE, ExGuid::default(), object, Edit::Collapsed(true)) + ops::page_op(SOURCE, ExGuid::default(), PageOp::Outline { object, edit: Edit::Collapsed(true) }) .is_err() ); assert!( - PreparedEdit::outline(SOURCE, sid, ExGuid::default(), Edit::Collapsed(true)).is_err() + ops::page_op(SOURCE, sid, PageOp::Outline { object: ExGuid::default(), edit: Edit::Collapsed(true) }).is_err() ); } } @@ -228,15 +226,10 @@ fn resizing_a_native_reserved_width_preserves_content() { let space = &document.spaces[&sid]; let before = &space.revisions[&space.contexts[&ExGuid::default()]]; assert!(before.nodes[&object].layout.reserved_width.is_some()); - let edit = PreparedEdit::outline( - source, - sid, - object, - Edit::Width { + let edit = ops::page_op(source, sid, PageOp::Outline { object, edit: Edit::Width { points: 144.0, user_set: true, - }, - ) + } }) .unwrap(); let after_store = Store::parse(edit.as_bytes()).unwrap(); let after_index = RevisionIndex::parse(&after_store).unwrap(); @@ -270,12 +263,8 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); let (sid, page) = document.pages().unwrap()[0]; - let insertion = - onestore::Insertion::outline(page, 144.0, 144.0, "Second 🦋 é", "Author").unwrap(); - let source = PreparedEdit::insert(&source, sid, &insertion) - .unwrap() - .as_bytes() - .to_vec(); + let (add, insertion, _) = ops::new_outline(144.0, 144.0, "Second 🦋 é"); + let source = ops::page_op(&source, sid, add).unwrap().image; let store = Store::parse(&source).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); @@ -288,7 +277,7 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content (144.0, 36.0, "First"), (0.0, 144.0, "First"), ] { - let edit = PreparedEdit::outline(&source, sid, insertion.object(), Edit::Position { x, y }) + let edit = ops::page_op(&source, sid, PageOp::Outline { object: insertion, edit: Edit::Position { x, y } }) .unwrap(); let saved = Store::parse(edit.as_bytes()).unwrap(); let saved_index = RevisionIndex::parse(&saved).unwrap(); @@ -308,7 +297,7 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content after.nodes.keys().collect::>() ); for (oid, node) in &before.nodes { - if ![page, metadata, insertion.object()].contains(oid) { + if ![page, metadata, insertion].contains(oid) { assert_eq!( serde_json::to_value(node).unwrap(), serde_json::to_value(&after.nodes[oid]).unwrap() @@ -320,12 +309,7 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content format!("{:?}", saved_index.resolve(sid, rid).unwrap()) ); assert_eq!( - PreparedEdit::outline( - edit.as_bytes(), - sid, - insertion.object(), - Edit::Position { x, y } - ) + ops::page_op(edit.as_bytes(), sid, PageOp::Outline { object: insertion, edit: Edit::Position { x, y } }) .unwrap() .as_bytes(), edit.as_bytes() @@ -366,7 +350,7 @@ fn native_automatic_and_explicit_titles_follow_outline_movement() { 72.0 }, }; - candidate = PreparedEdit::outline(&candidate, sid, outline, change) + candidate = ops::page_op(&candidate, sid, PageOp::Outline { object: outline, edit: change }) .unwrap() .as_bytes() .to_vec(); @@ -418,11 +402,8 @@ fn outline_publication_interruptions_reopen_as_complete_old_or_new_layout() { .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) .unwrap(); let paragraph = view.nodes[&outline].children[0]; - let second = onestore::Insertion::outline(page, 144.0, 36.0, "Second", "Author").unwrap(); - let source = PreparedEdit::insert(&source, sid, &second) - .unwrap() - .as_bytes() - .to_vec(); + let (add, _, _) = ops::new_outline(144.0, 36.0, "Second"); + let source = ops::page_op(&source, sid, add).unwrap().image; for (object, operation) in [ (outline, Edit::Position { x: 216.0, y: 72.0 }), ( @@ -434,7 +415,7 @@ fn outline_publication_interruptions_reopen_as_complete_old_or_new_layout() { ), (paragraph, Edit::Collapsed(true)), ] { - let edit = PreparedEdit::outline(&source, sid, object, operation).unwrap(); + let edit = ops::page_op(&source, sid, PageOp::Outline { object, edit: operation }).unwrap(); let before = current::current(&source); let after = current::current(edit.as_bytes()); for write_limit in [17, 4096] { @@ -481,11 +462,8 @@ fn repeated_geometry_changes_and_expansion_match_an_independent_model() { .unwrap(); let paragraph = view.nodes[&outline].children[0]; let text = view.nodes[¶graph].content[0]; - let second = onestore::Insertion::outline(page, 144.0, 36.0, "Second", "Author").unwrap(); - let original = PreparedEdit::insert(&original, sid, &second) - .unwrap() - .as_bytes() - .to_vec(); + let (add, _, _) = ops::new_outline(144.0, 36.0, "Second"); + let original = ops::page_op(&original, sid, add).unwrap().image; for seed in 1..=16_u64 { let mut rng = seed; let mut source = original.clone(); @@ -520,7 +498,7 @@ fn repeated_geometry_changes_and_expansion_match_an_independent_model() { (paragraph, Edit::Collapsed(rng & 1 != 0)) } }; - source = PreparedEdit::outline(&source, sid, object, edit) + source = ops::page_op(&source, sid, PageOp::Outline { object, edit }) .unwrap() .as_bytes() .to_vec(); @@ -574,7 +552,7 @@ fn export_native_outline_candidates() { let document = Document::parse(&index).unwrap(); let mut bytes = SOURCE.to_vec(); for (_, sid, object, edit) in cases(&document) { - bytes = PreparedEdit::outline(&bytes, sid, object, edit) + bytes = ops::page_op(&bytes, sid, PageOp::Outline { object, edit }) .unwrap() .as_bytes() .to_vec(); diff --git a/crates/onestore/tests/page.rs b/crates/onestore/tests/page.rs index c11eec4e82b8c8301a6b26b79e919f52548dfbef..53164ab0b1351c26968f26d46afbf59145d4a686 100644 --- a/crates/onestore/tests/page.rs +++ b/crates/onestore/tests/page.rs @@ -1,6 +1,7 @@ use onestore::{ - ExGuid, Insertion, PageCreation, PreparedEdit, RevisionIndex, Store, + ExGuid, PageCreation, RevisionIndex, Store, document::{Document, Kind}, + op::{PageOp, SectionOp}, page::{Page, PageObject}, }; @@ -8,6 +9,8 @@ use onestore::{ mod current; #[path = "support/disk.rs"] mod disk; +#[path = "support/ops.rs"] +mod ops; const SOURCE: &[u8] = include_bytes!("../../../corpus/page-lifecycle/03-renamed/notebook/Lifecycle.one"); @@ -28,7 +31,7 @@ fn native_section_accepts_empty_and_titled_pages_with_preserved_history() { let serialized = serde_json::to_vec(&intent).unwrap(); let retained = serde_json::from_slice(&serialized).unwrap(); assert_eq!(intent, retained); - let prepared = PreparedEdit::create_page(&source, &retained).unwrap(); + let prepared = ops::section_op(&source, SectionOp::Create(retained.clone())).unwrap(); let store = Store::parse(prepared.as_bytes()).unwrap(); assert_eq!( store.header.transaction_count, @@ -78,7 +81,7 @@ fn native_section_accepts_empty_and_titled_pages_with_preserved_history() { assert_eq!(space.labels, index.spaces[sid].labels); } } - assert!(PreparedEdit::create_page(prepared.as_bytes(), &intent).is_err()); + assert!(ops::section_op(prepared.as_bytes(), SectionOp::Create(intent.clone())).is_err()); source = prepared.as_bytes().to_vec(); } if let Some(output) = std::env::var_os("ONESTORE_PAGE_CREATION_OUTPUT") { @@ -92,13 +95,12 @@ fn created_pages_support_title_edits_and_body_insertion() { let mut source = onestore::create_section("pages.one", "Original", "Author").unwrap(); for title in [None, Some(""), Some("Explicit 🦋 é")] { let intent = PageCreation::new(None, title, "Author").unwrap(); - source = PreparedEdit::create_page(&source, &intent) + source = ops::section_op(&source, SectionOp::Create(intent.clone())) .unwrap() .as_bytes() .to_vec(); - let insertion = - Insertion::outline(intent.object(), 36.0, 36.0, "Body 🦀 é", "Author").unwrap(); - source = PreparedEdit::insert(&source, intent.space(), &insertion) + let (add, ..) = ops::new_outline(36.0, 36.0, "Body 🦀 é"); + source = ops::page_op(&source, intent.space(), add) .unwrap() .as_bytes() .to_vec(); @@ -115,13 +117,7 @@ fn created_pages_support_title_edits_and_body_insertion() { Some(title.filter(|s| !s.is_empty()).unwrap_or("Body 🦀 é")) ); if let Some(text) = intent.title_object() { - source = PreparedEdit::text( - &source, - intent.space(), - text, - 0..title.unwrap().encode_utf16().count() as u32, - "Renamed", - ) + source = ops::page_op(&source, intent.space(), PageOp::Text { text, range: 0..title.unwrap().encode_utf16().count() as u32, with: "Renamed".into() }) .unwrap() .as_bytes() .to_vec(); @@ -149,13 +145,13 @@ fn created_body_outlines_carry_the_indentation_table_onenote_writes() { let [first] = document.active(space).unwrap().nodes[&page].children[..] else { panic!() }; - let insertion = Insertion::outline(page, 72.0, 144.0, "Second", "Author").unwrap(); - let written = PreparedEdit::insert(&source, space, &insertion).unwrap(); + let (add, insertion, _) = ops::new_outline(72.0, 144.0, "Second"); + let written = ops::page_op(&source, space, add).unwrap(); let store = Store::parse(written.as_bytes()).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); let view = document.active(space).unwrap(); - for outline in [first, insertion.object()] { + for outline in [first, insertion] { let Kind::Outline { indents } = &view.nodes[&outline].kind else { panic!() }; @@ -167,7 +163,7 @@ fn created_body_outlines_carry_the_indentation_table_onenote_writes() { fn a_created_title_outline_is_stored_and_read_as_onenote_stores_it() { let source = onestore::create_section("pages.one", "Original", "Author").unwrap(); let intent = PageCreation::new(None, Some("Title"), "Author").unwrap(); - let prepared = PreparedEdit::create_page(&source, &intent).unwrap(); + let prepared = ops::section_op(&source, SectionOp::Create(intent.clone())).unwrap(); let store = Store::parse(prepared.as_bytes()).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); @@ -222,11 +218,11 @@ fn invalid_page_intents_and_nonleading_anchors_reject_before_publication() { let index = RevisionIndex::parse(&store).unwrap(); let pages = Document::parse(&index).unwrap().pages().unwrap(); let intent = PageCreation::new(Some(pages[4].0), Some("Child"), "Author").unwrap(); - assert!(PreparedEdit::create_page(source, &intent).is_err()); + assert!(ops::section_op(source, SectionOp::Create(intent.clone())).is_err()); let mut value = serde_json::to_value(PageCreation::new(None, None, "Author").unwrap()).unwrap(); value["guid"] = serde_json::to_value([0_u8; 16]).unwrap(); let invalid: PageCreation = serde_json::from_value(value.clone()).unwrap(); - assert!(PreparedEdit::create_page(source, &invalid).is_err()); + assert!(ops::section_op(source, SectionOp::Create(invalid.clone())).is_err()); value["unknown"] = true.into(); assert!(serde_json::from_value::(value).is_err()); } @@ -235,7 +231,7 @@ fn invalid_page_intents_and_nonleading_anchors_reject_before_publication() { fn new_space_and_section_entry_publish_as_one_complete_action() { let source = onestore::create_section("pages.one", "Original", "Author").unwrap(); let intent = PageCreation::new(None, Some("New 🦋 é"), "Author").unwrap(); - let prepared = PreparedEdit::create_page(&source, &intent).unwrap(); + let prepared = ops::section_op(&source, SectionOp::Create(intent.clone())).unwrap(); let old = current::current(&source); let new = current::current(prepared.as_bytes()); assert_eq!(new.len(), old.len() + 1); @@ -285,7 +281,7 @@ fn repeated_page_creation_crosses_root_fragments_counters_and_section_checkpoint "Stress author", ) .unwrap(); - let prepared = PreparedEdit::create_page(&source, &intent).unwrap(); + let prepared = ops::section_op(&source, SectionOp::Create(intent.clone())).unwrap(); let mut unpublished = prepared.as_bytes().to_vec(); unpublished[96..100].copy_from_slice(&source[96..100]); let old_store = Store::parse(&unpublished).unwrap(); @@ -363,7 +359,7 @@ fn native_changes_on_created_pages_accept_rust_followups() { let mut source = initial.to_vec(); for (sid, title, body) in targets { for (object, prefix) in [(body, "Rust + "), (title, "Reviewed ")] { - source = PreparedEdit::text(&source, sid, object, 0..0, prefix) + source = ops::page_op(&source, sid, PageOp::Text { text: object, range: 0..0, with: prefix.into() }) .unwrap() .as_bytes() .to_vec(); @@ -400,7 +396,7 @@ fn page_creation_preserves_native_features_and_file_data() { let old = RevisionIndex::parse(&before).unwrap(); let original = Document::parse(&old).unwrap(); let intent = PageCreation::new(None, Some("Preserved features"), "Author").unwrap(); - let prepared = PreparedEdit::create_page(source, &intent).unwrap(); + let prepared = ops::section_op(source, SectionOp::Create(intent.clone())).unwrap(); let after = Store::parse(prepared.as_bytes()).unwrap(); let new = RevisionIndex::parse(&after).unwrap(); let document = Document::parse(&new).unwrap(); @@ -427,3 +423,77 @@ fn page_creation_preserves_native_features_and_file_data() { } } } + +/// A page created into an empty section as OneNote 2010 creates one (its title's date and +/// time fields, then a colour) reads back with both; a page kept from elsewhere keeps its +/// identity and creation time. +#[test] +fn dated_pages_in_an_empty_section_read_back_with_their_date_and_colour() { + let source = onestore::create_empty_section("New Section 1.one", Some(0x00e4a88a)).unwrap(); + let store = Store::parse(&source).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + index.validate_current().unwrap(); + assert!(Document::parse(&index).unwrap().pages().unwrap().is_empty()); + let creation = PageCreation::new(None, Some(""), "Rust Author") + .unwrap() + .dated("Sunday, September 27, 2026", "2:23 AM") + .unwrap(); + let retained: PageCreation = + serde_json::from_slice(&serde_json::to_vec(&creation).unwrap()).unwrap(); + assert_eq!(retained, creation); + let space = creation.space(); + let identity = [7; 16]; + let kept = PageCreation::new(None, Some("Kept"), "Rust Author") + .unwrap() + .keeping(identity, 133_700_000_001_234_567) + .unwrap(); + let image = ops::edited( + &source, + vec![ + onestore::op::Op::Section(SectionOp::Create(creation.clone())), + onestore::op::Op::Page { + space, + op: PageOp::Color(Some(0x00f2f9d4)), + }, + onestore::op::Op::Section(SectionOp::Create(kept.clone())), + ], + ) + .unwrap(); + let store = Store::parse(&image).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + index.validate_current().unwrap(); + let document = Document::parse(&index).unwrap(); + let page = Page::from_space(&document, space).unwrap(); + assert_eq!(page.color, Some(0x00f2f9d4)); + assert_eq!(page.created, Some(creation.created())); + let [PageObject::Title(title)] = &page.objects[..] else { + panic!("{:?}", page.objects) + }; + let shown: Vec<_> = title.outlines[1] + .paragraphs + .iter() + .map(|paragraph| { + let text = paragraph.text().unwrap(); + (text.date_field, text.text.text().to_owned()) + }) + .collect(); + assert_eq!(title.date, Some(title.outlines[1].id)); + assert_eq!( + shown, + [ + // OneNote's own new pages read the same: the fields' order names their roles. + (None, "Sunday, September 27, 2026".to_owned()), + (None, "2:23 AM".to_owned()), + ] + ); + let kept_page = Page::from_space(&document, kept.space()).unwrap(); + assert_eq!(kept_page.identity, Some(identity)); + assert_eq!(kept_page.created, Some(133_700_000_001_234_567)); + assert_eq!(kept_page.color, None); + // Clearing the colour stores OneNote's "No color". + let cleared = ops::page_edited(&image, space, vec![PageOp::Color(None)]).unwrap(); + let store = Store::parse(&cleared).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + assert_eq!(Page::from_space(&document, space).unwrap().color, None); +} diff --git a/crates/onestore/tests/page_attachments.rs b/crates/onestore/tests/page_attachments.rs index 7304fb6d0355f13b8917f4df9f886235f2e48968..d37e475a5e83bf32ef0aebeb4ab2e9ec5f3c048d 100644 --- a/crates/onestore/tests/page_attachments.rs +++ b/crates/onestore/tests/page_attachments.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Format}, page::{ Attachment, Page, PageObject, PageParagraph, ParagraphContent, TextObject, text::new_id, @@ -9,7 +11,6 @@ use std::sync::Arc; const NATIVE: &[u8] = include_bytes!("../../../corpus/native/cold-05-06-attachment/notebook/synthetic.one"); -const AUTHOR: &str = "Attachment author"; const PAYLOAD: &[u8] = b"Rust wrote this attachment.\n"; fn first_page(bytes: &[u8]) -> (ExGuid, Page) { @@ -117,8 +118,8 @@ fn insert_attachment(section: &str, preview: Option>) -> Vec { }); body_paragraphs(&mut after).push(holder.clone()); body_paragraphs(&mut after).push(plain_paragraph(&template, "After the file")); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(&source, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); @@ -129,16 +130,16 @@ fn insert_attachment(section: &str, preview: Option>) -> Vec { assert_eq!(stored_attachment.bytes.as_deref(), Some(PAYLOAD)); assert_eq!(stored_attachment.preview, preview); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); let mut removed = stored.clone(); body_paragraphs(&mut removed).retain(|p| p.id != holder.id); - let again = PreparedEdit::page(written.as_bytes(), space, &removed, AUTHOR).unwrap(); - assert!(attachments(&page_in(again.as_bytes(), space)).is_empty()); - written.as_bytes().to_vec() + let again = ops::saved(written.as_slice(), space, &removed).unwrap(); + assert!(attachments(&page_in(again.as_slice(), space)).is_empty()); + written.as_slice().to_vec() } /// `ONESTORE_ATTACHMENT_EXPORT` names a directory receiving both candidates for a cold reopen: @@ -186,7 +187,7 @@ fn attachments_need_a_file_name_and_stored_ones_are_renamed_in_place() { recording: None, }); body_paragraphs(&mut after).push(holder); - assert!(PreparedEdit::page(&source, space, &after, AUTHOR).is_err()); + assert!(ops::saved(&source, space, &after).is_err()); let (space, native) = first_page(NATIVE); let mut renamed = native.clone(); let mut found = 0; @@ -202,8 +203,8 @@ fn attachments_need_a_file_name_and_stored_ones_are_renamed_in_place() { } } assert_eq!(found, 1); - let written = PreparedEdit::page(NATIVE, space, &renamed, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(NATIVE, space, &renamed).unwrap(); + let stored = page_in(written.as_slice(), space); let (before, after) = (attachments(&native)[0], attachments(&stored)[0]); assert_eq!(after.filename, "renamed \u{1f980}.txt"); assert_eq!( @@ -216,8 +217,8 @@ fn attachments_need_a_file_name_and_stored_ones_are_renamed_in_place() { if let Some(directory) = std::env::var_os("ONESTORE_ATTACHMENT_RENAME_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir_all(&directory).unwrap(); - std::fs::write(directory.join("synthetic.one"), written.as_bytes()).unwrap(); - let file_id = Store::parse(written.as_bytes()).unwrap().header.file_id; + std::fs::write(directory.join("synthetic.one"), written.as_slice()).unwrap(); + let file_id = Store::parse(written.as_slice()).unwrap().header.file_id; std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( diff --git a/crates/onestore/tests/page_deletion.rs b/crates/onestore/tests/page_deletion.rs index c3ceaa3706ce9617c7fcd4a6edfee76340991093..109c789c7d860fb3c0c35f6939aa710f858089a2 100644 --- a/crates/onestore/tests/page_deletion.rs +++ b/crates/onestore/tests/page_deletion.rs @@ -1,6 +1,7 @@ use onestore::{ - ExGuid, FileDataReference, ObjectData, PreparedEdit, PropertySets, RevisionIndex, Store, + ExGuid, FileDataReference, ObjectData, PropertySets, RevisionIndex, Store, document::{Document, Kind}, + op::{PageOp, SectionOp}, }; use std::collections::BTreeSet; @@ -130,7 +131,7 @@ fn removal_matches_all_native_cases_and_retains_every_old_revision() { .into_iter() .map(|ordinal| pages[ordinal].0) .collect(); - let prepared = PreparedEdit::delete_pages_permanently(&original, &removed).unwrap(); + let prepared = ops::section_op(&original, SectionOp::Delete(removed.to_vec())).unwrap(); if let Some(output) = std::env::var_os("ONESTORE_PAGE_REMOVAL_OUTPUT") { let output = std::path::Path::new(&output); assert!(output.is_absolute()); @@ -140,7 +141,7 @@ fn removal_matches_all_native_cases_and_retains_every_old_revision() { std::fs::write(case.join("Lifecycle.one"), prepared.as_bytes()).unwrap(); } verify(&original, prepared.as_bytes(), &native, &removed); - assert!(PreparedEdit::delete_pages_permanently(prepared.as_bytes(), &removed).is_err()); + assert!(ops::section_op(prepared.as_bytes(), SectionOp::Delete(removed.to_vec())).is_err()); } } @@ -160,22 +161,22 @@ fn removal_rejects_duplicate_missing_and_non_page_spaces_without_publication() { }], vec![pages[0].0, index.root], ] { - assert!(PreparedEdit::delete_pages_permanently(SOURCE, &selected).is_err()); + assert!(ops::section_op(SOURCE, SectionOp::Delete(selected.to_vec())).is_err()); } assert_eq!( - PreparedEdit::delete_pages_permanently(SOURCE, &[]) + ops::section_op(SOURCE, SectionOp::Delete([].to_vec())) .unwrap() .as_bytes(), SOURCE ); - let written = PreparedEdit::delete_pages_permanently(SOURCE, &[pages[0].0]).unwrap(); + let written = ops::section_op(SOURCE, SectionOp::Delete([pages[0].0].to_vec())).unwrap(); assert!( - PreparedEdit::delete_pages_permanently(written.as_bytes(), &[pages[1].0, pages[0].0]) + ops::section_op(written.as_bytes(), SectionOp::Delete([pages[1].0, pages[0].0].to_vec())) .is_err() ); } -use page_schedule::{current, disk}; +use page_schedule::{current, disk, ops}; /// OneNote created a page in each section after Rust permanently removed pages from it; /// the surviving pages keep their revisions through the native save and a cold reopen. @@ -267,7 +268,7 @@ fn tombstones_and_first_page_promotion_survive_each_storage_interruption() { .take(if all { pages.len() } else { 1 }) .map(|page| page.0) .collect(); - let prepared = PreparedEdit::delete_pages_permanently(source, &removed).unwrap(); + let prepared = ops::section_op(source, SectionOp::Delete(removed.to_vec())).unwrap(); let old = current::current(source); let new = current::current(prepared.as_bytes()); assert_ne!(old, new); @@ -331,8 +332,17 @@ fn stale_removal_cannot_delete_a_newer_page_edit() { _ => None, }) .unwrap(); - let removal = PreparedEdit::delete_pages_permanently(SOURCE, &[sid]).unwrap(); - let remote = onestore::replace_text(SOURCE, sid, text, 0..0, "New remote content ").unwrap(); + let removal = ops::section_op(SOURCE, SectionOp::Delete([sid].to_vec())).unwrap(); + let remote = ops::page_edited( + SOURCE, + sid, + vec![PageOp::Text { + text, + range: 0..0, + with: "New remote content ".into(), + }], + ) + .unwrap(); let mut disk = disk::Disk { visible: remote.clone(), durable: remote.clone(), @@ -380,7 +390,7 @@ fn external_payloads_and_their_historical_references_survive_file_removal() { let pages = Document::parse(&index).unwrap().pages().unwrap(); assert_eq!(pages.len(), 3); let selected: Vec<_> = pages.iter().map(|page| page.0).collect(); - let prepared = PreparedEdit::delete_pages_permanently(&original, &selected).unwrap(); + let prepared = ops::section_op(&original, SectionOp::Delete(selected.to_vec())).unwrap(); let output = std::env::var_os("ONESTORE_PAGE_REMOVAL_EXTERNAL_OUTPUT"); let root = output .as_ref() @@ -409,7 +419,7 @@ fn external_payloads_and_their_historical_references_survive_file_removal() { assert_eq!(payloads.len(), 3); let path = root.join("synthetic.one"); std::fs::write(&path, &original).unwrap(); - prepared.commit_file(&path).unwrap(); + prepared.transaction.as_ref().unwrap().commit_file(&path).unwrap(); let written = onestore::read_file(&path).unwrap(); assert_eq!(written, prepared.as_bytes()); let current_store = Store::parse(&written).unwrap(); diff --git a/crates/onestore/tests/page_images.rs b/crates/onestore/tests/page_images.rs index ad0f14d9bd2b0bff181adbfd3f888f28cc7a8182..3ed266fe54c2a4d322a4f65c3494813bc6c770a3 100644 --- a/crates/onestore/tests/page_images.rs +++ b/crates/onestore/tests/page_images.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Format, Layout}, page::{Image, Page, PageObject, PageParagraph, ParagraphContent, TextObject, text::new_id}, }; @@ -11,7 +13,6 @@ const NATIVE_RESIZE: &[u8] = include_bytes!("../../../corpus/picture-edit/native-resize/notebook/pictures.one"); const NATIVE_PAGE_LEVEL: &[u8] = include_bytes!("../../../corpus/picture-edit/native-page-level/notebook/pictures.one"); -const AUTHOR: &str = "Picture author"; /// A one-pixel PNG. const PNG: &[u8] = &[ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, @@ -120,8 +121,8 @@ fn a_picture_inserted_on_a_fresh_page_reads_back_and_can_be_removed() { holder.content = ParagraphContent::Image(image.clone()); body_paragraphs(&mut after).push(holder.clone()); body_paragraphs(&mut after).push(plain_paragraph(&template, "After the picture")); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(&source, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); @@ -131,16 +132,16 @@ fn a_picture_inserted_on_a_fresh_page_reads_back_and_can_be_removed() { }; assert_eq!(picture.bytes.as_deref(), Some(PNG)); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); if let Some(directory) = std::env::var_os("ONESTORE_IMAGE_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("pictures.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("pictures.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -153,8 +154,8 @@ fn a_picture_inserted_on_a_fresh_page_reads_back_and_can_be_removed() { } let mut removed = stored.clone(); body_paragraphs(&mut removed).retain(|p| p.id != holder.id); - let again = PreparedEdit::page(written.as_bytes(), space, &removed, AUTHOR).unwrap(); - let stored = page_in(again.as_bytes(), space); + let again = ops::saved(written.as_slice(), space, &removed).unwrap(); + let stored = page_in(again.as_slice(), space); assert!(pictures(&stored).is_empty()); assert_eq!(body_paragraphs(&mut stored.clone()).len(), 2); } @@ -187,20 +188,20 @@ fn a_native_picture_is_resized_and_described_then_reset() { ..Default::default() }; resize(&mut resized, layout, Some("Resized in Rust")); - let written = PreparedEdit::page(NATIVE, space, &resized, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(NATIVE, space, &resized).unwrap(); + let stored = page_in(written.as_slice(), space); assert_eq!(stored, resized); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); if let Some(directory) = std::env::var_os("ONESTORE_IMAGE_RESIZE_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("synthetic.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("synthetic.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -213,8 +214,8 @@ fn a_native_picture_is_resized_and_described_then_reset() { } let mut reset = stored.clone(); resize(&mut reset, Layout::default(), None); - let again = PreparedEdit::page(written.as_bytes(), space, &reset, AUTHOR).unwrap(); - assert_eq!(page_in(again.as_bytes(), space), native); + let again = ops::saved(written.as_slice(), space, &reset).unwrap(); + assert_eq!(page_in(again.as_slice(), space), native); } #[test] @@ -234,8 +235,8 @@ fn a_rust_picture_resized_natively_reads_back_with_its_size_and_description() { let mut layout = picture.layout.clone(); layout.max_width = Some(200.0); resize(&mut wider, layout, Some("Wider in Rust")); - let written = PreparedEdit::page(NATIVE_RESIZE, space, &wider, AUTHOR).unwrap(); - assert_eq!(page_in(written.as_bytes(), space), wider); + let written = ops::saved(NATIVE_RESIZE, space, &wider).unwrap(); + assert_eq!(page_in(written.as_slice(), space), wider); } fn page_pictures(page: &Page) -> Vec<&Image> { @@ -270,8 +271,8 @@ fn a_page_level_picture_placed_natively_reads_as_a_page_object() { image.layout.y = Some(400.0); } } - let written = PreparedEdit::page(NATIVE_PAGE_LEVEL, space, &moved, AUTHOR).unwrap(); - assert_eq!(page_in(written.as_bytes(), space), moved); + let written = ops::saved(NATIVE_PAGE_LEVEL, space, &moved).unwrap(); + assert_eq!(page_in(written.as_slice(), space), moved); } /// `ONESTORE_PAGE_IMAGE_EXPORT` names a new directory receiving the candidate for a cold reopen. @@ -296,23 +297,23 @@ fn a_page_level_picture_is_inserted_moved_and_removed() { background: false, }; after.objects.push(PageObject::Image(image.clone())); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(&source, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); assert_eq!(page_pictures(&stored)[0].bytes.as_deref(), Some(PNG)); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); if let Some(directory) = std::env::var_os("ONESTORE_PAGE_IMAGE_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("pictures.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("pictures.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -330,19 +331,19 @@ fn a_page_level_picture_is_inserted_moved_and_removed() { image.alt = None; } } - let again = PreparedEdit::page(written.as_bytes(), space, &moved, AUTHOR).unwrap(); - assert_eq!(page_in(again.as_bytes(), space), moved); + let again = ops::saved(written.as_slice(), space, &moved).unwrap(); + assert_eq!(page_in(again.as_slice(), space), moved); let mut removed = moved.clone(); removed.objects.retain(|object| object.id() != image.id); - let last = PreparedEdit::page(again.as_bytes(), space, &removed, AUTHOR).unwrap(); - let stored = page_in(last.as_bytes(), space); + let last = ops::saved(again.as_slice(), space, &removed).unwrap(); + let stored = page_in(last.as_slice(), space); assert!(page_pictures(&stored).is_empty()); assert_eq!(stored, removed); let mut unplaced = stored.clone(); let mut nowhere = image.clone(); nowhere.layout = Layout::default(); unplaced.objects.push(PageObject::Image(nowhere)); - assert!(PreparedEdit::page(last.as_bytes(), space, &unplaced, AUTHOR).is_err()); + assert!(ops::saved(last.as_slice(), space, &unplaced).is_err()); } #[test] @@ -361,7 +362,7 @@ fn pictures_need_a_recognised_payload_and_stored_ones_stay_fixed() { background: false, }); body_paragraphs(&mut after).push(holder); - assert!(PreparedEdit::page(&source, space, &after, AUTHOR).is_err()); + assert!(ops::saved(&source, space, &after).is_err()); let (space, native) = first_page(NATIVE); let mut resized = native.clone(); let mut found = 0; @@ -376,5 +377,5 @@ fn pictures_need_a_recognised_payload_and_stored_ones_stay_fixed() { } } assert_eq!(found, 1); - assert!(PreparedEdit::page(NATIVE, space, &resized, AUTHOR).is_err()); + assert!(ops::saved(NATIVE, space, &resized).is_err()); } diff --git a/crates/onestore/tests/page_ink.rs b/crates/onestore/tests/page_ink.rs index a27a31732ea125864e778834dab907ad05fbee22..c986b4d402e308f051bd5f42fffb3c693ca55e42 100644 --- a/crates/onestore/tests/page_ink.rs +++ b/crates/onestore/tests/page_ink.rs @@ -1,10 +1,11 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::Document, page::{Ink, InkStroke, Page, PageObject, text::new_id}, }; -const AUTHOR: &str = "Ink author"; /// Two drawings made with the mouse in OneNote 2010 (`tools/native/ink.ahk`): a diamond and a /// diagonal line, whose native read reports their positions and sizes. @@ -142,8 +143,8 @@ fn an_ink_drawing_is_written_erased_stroke_by_stroke_and_removed() { let mut after = before.clone(); let ink = drawing(); after.objects.push(PageObject::Ink(ink.clone())); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(&source, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); @@ -157,12 +158,12 @@ fn an_ink_drawing_is_written_erased_stroke_by_stroke_and_removed() { ] )); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); - export("ONESTORE_INK_EXPORT", "ink.one", written.as_bytes()); + export("ONESTORE_INK_EXPORT", "ink.one", written.as_slice()); let mut erased = stored.clone(); for object in &mut erased.objects { if let PageObject::Ink(ink) = object { @@ -171,13 +172,13 @@ fn an_ink_drawing_is_written_erased_stroke_by_stroke_and_removed() { .push(stroke(&[[330.0, 100.0], [330.0, 200.0]], Some(0xff0000))); } } - let again = PreparedEdit::page(written.as_bytes(), space, &erased, AUTHOR).unwrap(); - let stored = page_in(again.as_bytes(), space); + let again = ops::saved(written.as_slice(), space, &erased).unwrap(); + let stored = page_in(again.as_slice(), space); assert_eq!(stored, erased); let mut removed = stored.clone(); removed.objects.retain(|object| object.id() != ink.id); - let last = PreparedEdit::page(again.as_bytes(), space, &removed, AUTHOR).unwrap(); - let stored = page_in(last.as_bytes(), space); + let last = ops::saved(again.as_slice(), space, &removed).unwrap(); + let stored = page_in(last.as_slice(), space); assert!(drawings(&stored).is_empty()); assert_eq!(stored, removed); } @@ -209,15 +210,15 @@ fn handwriting_is_written_as_paragraph_content() { paragraph.format = Default::default(); paragraph.content = ParagraphContent::Ink(drawing()); outline.paragraphs.push(paragraph); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(&source, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); export( "ONESTORE_INK_PARAGRAPH_EXPORT", "ink.one", - written.as_bytes(), + written.as_slice(), ); } @@ -230,5 +231,5 @@ fn stored_strokes_keep_their_paths() { ink.strokes[0].points[0][0] += 1.0; } } - assert!(PreparedEdit::page(NATIVE, space, &moved, AUTHOR).is_err()); + assert!(ops::saved(NATIVE, space, &moved).is_err()); } diff --git a/crates/onestore/tests/page_links.rs b/crates/onestore/tests/page_links.rs index e315f23c148917354be9a7414cda46504b95938b..f7472b47220a8ca5fbef6c482cc337100aec9fac 100644 --- a/crates/onestore/tests/page_links.rs +++ b/crates/onestore/tests/page_links.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::Document, page::{ Page, PageObject, PageParagraph, Paragraph, @@ -13,7 +15,6 @@ const OUTLINES: &[u8] = const NATIVE_LINKS: &[u8] = include_bytes!("../../../corpus/link-edit/native-links/notebook/links.one"); const NATIVE_BASE_PATH: &str = r"C:\one-tests\runs\capture\notebook\links.one"; -const AUTHOR: &str = "Link author"; const CODE: &str = "\u{fddf}HYPERLINK \"https://example.invalid/rust\""; fn page_by_title(bytes: &[u8], title: &str) -> (ExGuid, Page) { @@ -133,8 +134,8 @@ fn a_native_link_is_removed_leaving_plain_label_text() { replacement: Paragraph::new(content[end..].to_owned(), plain), }) .unwrap(); - let written = PreparedEdit::page(OUTLINES, space, &after, AUTHOR).unwrap(); - let stored = assert_same(written.as_bytes(), space, &after); + let written = ops::saved(OUTLINES, space, &after).unwrap(); + let stored = assert_same(written.as_slice(), space, &after); let mut stored = stored; let text = &body_paragraphs(&mut stored)[at].text().unwrap().text; assert!(text.text().ends_with(" Link label")); @@ -159,8 +160,8 @@ fn a_link_is_added_to_a_fresh_page_and_reads_back() { &mut body_paragraphs(&mut after)[0].text_mut().unwrap().text, "the Rust site", ); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - let stored = assert_same(written.as_bytes(), space, &after); + let written = ops::saved(&source, space, &after).unwrap(); + let stored = assert_same(written.as_slice(), space, &after); let mut stored = stored; let text = &body_paragraphs(&mut stored)[0].text().unwrap().text; let flags: Vec<(Option, Option, Option)> = text @@ -185,8 +186,8 @@ fn a_link_is_added_to_a_fresh_page_and_reads_back() { if let Some(directory) = std::env::var_os("ONESTORE_LINK_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("links.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("links.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -292,10 +293,11 @@ fn internal_links_match_what_onenote_stores() { fn a_page_links_to_another_page_and_its_paragraph() { let source = onestore::create_section("links.one", "Linking page", "Author").unwrap(); let creation = onestore::PageCreation::new(None, Some("Link target"), "Author").unwrap(); - let with_target = onestore::PreparedEdit::create_page(&source, &creation) - .unwrap() - .as_bytes() - .to_vec(); + let with_target = ops::edited( + &source, + vec![onestore::op::Op::Section(onestore::op::SectionOp::Create(creation))], + ) + .unwrap(); let section = Store::parse(&with_target).unwrap().header.file_id; let (_, target) = page_by_title(&with_target, "Link target"); let (space, before) = page_by_title(&with_target, "Linking page"); @@ -351,12 +353,12 @@ fn a_page_links_to_another_page_and_its_paragraph() { replacement: Paragraph::from_runs(runs), }) .unwrap(); - let written = PreparedEdit::page(&with_target, space, &after, AUTHOR).unwrap(); - assert_same(written.as_bytes(), space, &after); + let written = ops::saved(&with_target, space, &after).unwrap(); + assert_same(written.as_slice(), space, &after); if let Some(directory) = std::env::var_os("ONESTORE_INTERNAL_LINK_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("links.one"), written.as_bytes()).unwrap(); + std::fs::write(directory.join("links.one"), written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents("Open Notebook.onetoc2", &[("links.one", section)]) diff --git a/crates/onestore/tests/page_lists.rs b/crates/onestore/tests/page_lists.rs index af757f3d277fe932987552b273abdd2c83d08bcb..70236cc007b6ea3046909d8176eff9bef1c9ce4c 100644 --- a/crates/onestore/tests/page_lists.rs +++ b/crates/onestore/tests/page_lists.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Format, Kind}, page::{ Definition, Page, PageObject, PageParagraph, ParagraphContent, TextObject, text::new_id, @@ -9,7 +11,6 @@ use std::collections::BTreeMap; const TREES: &[u8] = include_bytes!("../../../corpus/outline-edit/tree/before/notebook/synthetic.one"); -const AUTHOR: &str = "List author"; fn page_by_title(bytes: &[u8], title: &str) -> (ExGuid, Page) { let store = Store::parse(bytes).unwrap(); @@ -114,8 +115,8 @@ fn a_numbered_paragraph_becomes_plain_while_its_neighbours_keep_their_nodes() { assert!(numbered.len() >= 2, "{lists:?}"); let mut after = before.clone(); body_paragraphs(&mut after)[numbered[0]].lists.clear(); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(TREES, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); expected.definitions.remove(&lists[numbered[0]][0]); @@ -123,16 +124,16 @@ fn a_numbered_paragraph_becomes_plain_while_its_neighbours_keep_their_nodes() { for other in &numbered[1..] { for node in &lists[*other] { assert_eq!( - raw_object(written.as_bytes(), space, *node), + raw_object(written.as_slice(), space, *node), raw_object(TREES, space, *node) ); } } assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); } @@ -157,16 +158,16 @@ fn bullets_and_numbering_write_from_definitions_and_read_back() { let mut fresh = plain_paragraph(&template, "Numbered from three"); fresh.lists = vec![number]; body_paragraphs(&mut after).push(fresh); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(TREES, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); } @@ -183,8 +184,8 @@ fn a_list_definition_changes_in_place() { }; *restart = Some(7); definition.format.bold = Some(true); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(TREES, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); @@ -199,8 +200,8 @@ fn a_definition_shared_between_paragraphs_becomes_a_node_per_paragraph() { let plain = (0..lists.len()).find(|i| lists[*i].is_empty()).unwrap(); let mut shared = before.clone(); body_paragraphs(&mut shared)[plain].lists = lists[numbered].clone(); - let written = PreparedEdit::page(TREES, space, &shared, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(TREES, space, &shared).unwrap(); + let stored = page_in(written.as_slice(), space); let after = list_ids(&stored); assert_eq!(after[numbered], lists[numbered]); assert_eq!(after[plain].len(), 1); @@ -218,12 +219,12 @@ fn missing_and_foreign_list_definitions_are_refused() { let plain = (0..lists.len()).find(|i| lists[*i].is_empty()).unwrap(); let mut missing = before.clone(); body_paragraphs(&mut missing)[plain].lists = vec![new_id().unwrap()]; - assert!(PreparedEdit::page(TREES, space, &missing, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &missing).is_err()); let mut foreign = before.clone(); let paragraph = body_paragraphs(&mut foreign)[plain].id; foreign.definitions.insert(paragraph, bullet_definition()); body_paragraphs(&mut foreign)[plain].lists = vec![paragraph]; - assert!(PreparedEdit::page(TREES, space, &foreign, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &foreign).is_err()); let mut style: BTreeMap = before.definitions.clone(); if let Some((id, definition)) = style .iter_mut() @@ -232,7 +233,7 @@ fn missing_and_foreign_list_definitions_are_refused() { definition.format.bold = Some(true); let mut edited = before.clone(); edited.definitions.insert(*id, definition.clone()); - assert!(PreparedEdit::page(TREES, space, &edited, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &edited).is_err()); } } @@ -299,16 +300,16 @@ fn bullets_numbering_nesting_and_restarts_publish_on_a_fresh_page() { body_paragraphs(&mut after).push(paragraph); } body_paragraphs(&mut after).push(plain_paragraph(&template, "Plain again")); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(&source, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let mut expected = after.clone(); expected.title = stored.title.clone(); assert_eq!(stored, expected); if let Some(directory) = std::env::var_os("ONESTORE_LIST_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("lists.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("lists.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( diff --git a/crates/onestore/tests/page_math.rs b/crates/onestore/tests/page_math.rs index e0787b8af3027dfe0f14351118f304ddade40ba3..b7f707d5a90475972f35e4597a32fa6abb023daf 100644 --- a/crates/onestore/tests/page_math.rs +++ b/crates/onestore/tests/page_math.rs @@ -1,3 +1,5 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ RevisionIndex, Store, document::Document, @@ -272,8 +274,8 @@ fn equations_are_written_and_read_back() { } outline.paragraphs.push(paragraph); } - let written = onestore::PreparedEdit::page(&source, space, &after, "Math author").unwrap(); - let store = Store::parse(written.as_bytes()).unwrap(); + let written = ops::saved(&source, space, &after).unwrap(); + let store = Store::parse(written.as_slice()).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); let stored = Page::from_space(&document, space).unwrap(); @@ -305,13 +307,13 @@ fn equations_are_written_and_read_back() { } assert_eq!(stored, expected); assert_eq!( - rendered_equations(written.as_bytes(), &stored.title), + rendered_equations(written.as_slice(), &stored.title), expected_mathml ); if let Some(directory) = std::env::var_os("ONESTORE_MATH_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("math.one"), written.as_bytes()).unwrap(); + std::fs::write(directory.join("math.one"), written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -361,8 +363,8 @@ fn a_paragraph_emptied_of_its_inline_math_saves() { let plain = text.text.spans()[0].format.clone(); assert_ne!(plain.math, Some(true)); text.text = onestore::page::Paragraph::new(String::new(), plain); - let written = onestore::PreparedEdit::page(NATIVE, space, &after, "Author").unwrap(); - let store = Store::parse(written.as_bytes()).unwrap(); + let written = ops::saved(NATIVE, space, &after).unwrap(); + let store = Store::parse(written.as_slice()).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); assert_eq!(Page::from_space(&document, space).unwrap(), after); diff --git a/crates/onestore/tests/page_media.rs b/crates/onestore/tests/page_media.rs index 9f24c297c880a8041b58fd91eb6d2b9f604f13d9..c18be4ca6372b03407a962d7dce9f053837ab5dc 100644 --- a/crates/onestore/tests/page_media.rs +++ b/crates/onestore/tests/page_media.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::Document, page::{MediaIndex, Page, PageObject, ParagraphContent, Recording}, }; @@ -96,8 +98,8 @@ fn editing_a_recorded_page_keeps_the_recording_and_its_annotation() { tags: Vec::new(), }); outline.paragraphs.push(added.clone()); - let written = PreparedEdit::page(NATIVE, space, &after, "Media author").unwrap(); - let (_, stored) = page(written.as_bytes()); + let written = ops::saved(NATIVE, space, &after).unwrap(); + let (_, stored) = page(written.as_slice()); assert_eq!(recording(&stored), recording(&before)); assert_eq!(stored.objects.len(), before.objects.len()); @@ -112,7 +114,7 @@ fn editing_a_recorded_page_keeps_the_recording_and_its_annotation() { .unwrap(); let last = outline.paragraphs.last_mut().unwrap(); last.media = recording(&before).1; - assert!(PreparedEdit::page(written.as_bytes(), space, &annotated, "x").is_err()); + assert!(ops::saved(written.as_slice(), space, &annotated).is_err()); let mut recaptured = after.clone(); for object in &mut recaptured.objects { if let PageObject::Outline(outline) = object { @@ -123,13 +125,13 @@ fn editing_a_recorded_page_keeps_the_recording_and_its_annotation() { } } } - assert!(PreparedEdit::page(written.as_bytes(), space, &recaptured, "x").is_err()); + assert!(ops::saved(written.as_slice(), space, &recaptured).is_err()); if let Some(directory) = std::env::var_os("ONESTORE_MEDIA_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir_all(&directory).unwrap(); - std::fs::write(directory.join("Features.one"), written.as_bytes()).unwrap(); - let file_id = Store::parse(written.as_bytes()).unwrap().header.file_id; + std::fs::write(directory.join("Features.one"), written.as_slice()).unwrap(); + let file_id = Store::parse(written.as_slice()).unwrap().header.file_id; std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -139,7 +141,7 @@ fn editing_a_recorded_page_keeps_the_recording_and_its_annotation() { .unwrap(), ) .unwrap(); - let store = Store::parse(written.as_bytes()).unwrap(); + let store = Store::parse(written.as_slice()).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let count = Document::parse(&index).unwrap().pages().unwrap().len(); std::fs::write(directory.join("expected-count.txt"), count.to_string()).unwrap(); diff --git a/crates/onestore/tests/page_movement.rs b/crates/onestore/tests/page_movement.rs index d726678e26b2b9275ddad8283af0bfb9d8927305..fed316aac3be21bc7a789ace471b01cc87da864d 100644 --- a/crates/onestore/tests/page_movement.rs +++ b/crates/onestore/tests/page_movement.rs @@ -1,6 +1,10 @@ +#[path = "support/ops.rs"] +mod ops; + use onestore::{ - ExGuid, PageEdit, PreparedEdit, RevisionIndex, Store, + ExGuid, PageEdit, RevisionIndex, Store, document::{Document, Kind}, + op::{PageOp, SectionOp}, }; #[path = "support/current.rs"] @@ -175,10 +179,10 @@ fn explicit_page_edits_match_native_selection_and_indentation() { let serialized = serde_json::to_vec(&edits).unwrap(); let retained: Vec = serde_json::from_slice(&serialized).unwrap(); assert_eq!(edits, retained); - let prepared = PreparedEdit::pages(&source, &retained).unwrap(); + let prepared = ops::section_op(&source, SectionOp::Pages(retained.to_vec())).unwrap(); verify(&source, prepared.as_bytes(), native); assert_eq!( - PreparedEdit::pages(prepared.as_bytes(), &retained) + ops::section_op(prepared.as_bytes(), SectionOp::Pages(retained.to_vec())) .unwrap() .as_bytes(), prepared.as_bytes() @@ -187,7 +191,7 @@ fn explicit_page_edits_match_native_selection_and_indentation() { source = prepared.as_bytes().to_vec(); } let edit = PageEdit::move_to(ids[3], None, 1).unwrap(); - let single = PreparedEdit::pages(SOURCE, &[edit]).unwrap(); + let single = ops::section_op(SOURCE, SectionOp::Pages([edit].to_vec())).unwrap(); verify( SOURCE, single.as_bytes(), @@ -206,32 +210,29 @@ fn invalid_and_empty_page_edits_do_not_publish() { } assert!(PageEdit::move_to(ids[0], Some(ids[0]), 1).is_err()); assert!(PageEdit::set_level(ExGuid::default(), 1).is_err()); - assert_eq!(PreparedEdit::pages(SOURCE, &[]).unwrap().as_bytes(), SOURCE); + assert_eq!(ops::section_op(SOURCE, SectionOp::Pages([].to_vec())).unwrap().as_bytes(), SOURCE); assert_eq!( - PreparedEdit::pages(SOURCE, &[PageEdit::set_level(ids[0], 1).unwrap()]) + ops::section_op(SOURCE, SectionOp::Pages([PageEdit::set_level(ids[0], 1).unwrap()].to_vec())) .unwrap() .as_bytes(), SOURCE ); - assert!(PreparedEdit::pages(SOURCE, &[PageEdit::set_level(ids[0], 2).unwrap()]).is_err()); + assert!(ops::section_op(SOURCE, SectionOp::Pages([PageEdit::set_level(ids[0], 2).unwrap()].to_vec())).is_err()); let duplicate = PageEdit::set_level(ids[3], 2).unwrap(); - assert!(PreparedEdit::pages(SOURCE, &[duplicate.clone(), duplicate]).is_err()); + assert!(ops::section_op(SOURCE, SectionOp::Pages([duplicate.clone(), duplicate].to_vec())).is_err()); let missing = ExGuid { guid: [0x77; 16], n: 1, }; - assert!(PreparedEdit::pages(SOURCE, &[PageEdit::set_level(missing, 1).unwrap()]).is_err()); + assert!(ops::section_op(SOURCE, SectionOp::Pages([PageEdit::set_level(missing, 1).unwrap()].to_vec())).is_err()); assert!( - PreparedEdit::pages( - SOURCE, - &[PageEdit::move_to(ids[3], Some(missing), 1).unwrap()] - ) + ops::section_op(SOURCE, SectionOp::Pages([PageEdit::move_to(ids[3], Some(missing), 1).unwrap()].to_vec())) .is_err() ); let mut value = serde_json::to_value(PageEdit::set_level(ids[3], 2).unwrap()).unwrap(); value["level"] = 4.into(); let invalid: PageEdit = serde_json::from_value(value.clone()).unwrap(); - assert!(PreparedEdit::pages(SOURCE, &[invalid]).is_err()); + assert!(ops::section_op(SOURCE, SectionOp::Pages([invalid].to_vec())).is_err()); value["unknown"] = true.into(); assert!(serde_json::from_value::(value).is_err()); } @@ -246,12 +247,12 @@ fn moving_pages_preserves_ink_file_data_and_native_feature_objects() { let first = pages(source)[0]; let created = onestore::PageCreation::new(Some(first), Some("Movement anchor"), "Author").unwrap(); - let source = PreparedEdit::create_page(source, &created) + let source = ops::section_op(source, SectionOp::Create(created.clone())) .unwrap() .as_bytes() .to_vec(); let moved = - PreparedEdit::pages(&source, &[PageEdit::move_to(first, None, 1).unwrap()]).unwrap(); + ops::section_op(&source, SectionOp::Pages([PageEdit::move_to(first, None, 1).unwrap()].to_vec())).unwrap(); assert_eq!(pages(moved.as_bytes()).last(), Some(&first)); let stores = [&source, moved.as_bytes()].map(|bytes| Store::parse(bytes).unwrap()); let indexes = stores @@ -295,7 +296,7 @@ fn section_reordering_refreshes_stale_native_metadata_levels() { .enumerate() .map(|(i, sid)| PageEdit::move_to(*sid, None, u32::try_from(i + 1).unwrap()).unwrap()) .collect(); - let prepared = PreparedEdit::pages(source, &edits).unwrap(); + let prepared = ops::section_op(source, SectionOp::Pages(edits.to_vec())).unwrap(); let stores = [source.as_slice(), prepared.as_bytes()].map(|b| Store::parse(b).unwrap()); let indexes = stores.each_ref().map(|s| RevisionIndex::parse(s).unwrap()); let documents = indexes.each_ref().map(|i| Document::parse(i).unwrap()); @@ -335,7 +336,7 @@ fn metadata_copy_order_does_not_change_page_identity() { "../../../corpus/page-lifecycle/page-edits/optional-cache/source-cold/notebook/Lifecycle.one" ); let sid = pages(source)[3]; - let edited = PreparedEdit::pages(source, &[PageEdit::set_level(sid, 2).unwrap()]).unwrap(); + let edited = ops::section_op(source, SectionOp::Pages([PageEdit::set_level(sid, 2).unwrap()].to_vec())).unwrap(); let expected = include_bytes!( "../../../corpus/page-lifecycle/page-edits/optional-cache/candidate/Lifecycle.one" ); @@ -371,8 +372,8 @@ fn native_edits_on_moved_pages_support_more_rust_changes() { let bodies: Vec<_> = view.nodes.iter().filter_map(|(id, node)| matches!(&node.kind, Kind::RichText { text, .. } if text == "Native body after a Rust page move.").then_some(*id)).collect(); let [body] = bodies.as_slice() else { panic!() }; - let moved = PreparedEdit::pages(source, &[PageEdit::set_level(sid, 2).unwrap()]).unwrap(); - let edited = PreparedEdit::text(moved.as_bytes(), sid, *body, 0..0, "Rust + ").unwrap(); + let moved = ops::section_op(source, SectionOp::Pages([PageEdit::set_level(sid, 2).unwrap()].to_vec())).unwrap(); + let edited = ops::page_op(moved.as_bytes(), sid, PageOp::Text { text: *body, range: 0..0, with: "Rust + ".into() }).unwrap(); let after_store = Store::parse(edited.as_bytes()).unwrap(); let after_index = RevisionIndex::parse(&after_store).unwrap(); let after = Document::parse(&after_index).unwrap(); @@ -401,7 +402,7 @@ fn grouped_page_levels_and_membership_survive_each_storage_interruption() { let mut source = onestore::create_section("movement.one", "Original", "Author").unwrap(); for _ in 0..2 { let page = onestore::PageCreation::new(None, Some("Same title"), "Author").unwrap(); - source = PreparedEdit::create_page(&source, &page) + source = ops::section_op(&source, SectionOp::Create(page.clone())) .unwrap() .as_bytes() .to_vec(); @@ -421,7 +422,7 @@ fn grouped_page_levels_and_membership_survive_each_storage_interruption() { PageEdit::set_level(ids[first], 2).unwrap(), PageEdit::set_level(ids[first + 1], 3).unwrap(), ]; - let prepared = PreparedEdit::pages(source, &edits).unwrap(); + let prepared = ops::section_op(source, SectionOp::Pages(edits.to_vec())).unwrap(); let old = current::current(source); let new = current::current(prepared.as_bytes()); assert_eq!(new.len(), old.len()); @@ -460,7 +461,7 @@ fn grouped_page_levels_and_membership_survive_each_storage_interruption() { fn repeated_nesting_retains_series_history_across_revision_checkpoints() { let mut source = onestore::create_section("movement.one", "Original", "Author").unwrap(); let page = onestore::PageCreation::new(None, Some("Child"), "Author").unwrap(); - source = PreparedEdit::create_page(&source, &page) + source = ops::section_op(&source, SectionOp::Create(page.clone())) .unwrap() .as_bytes() .to_vec(); @@ -468,7 +469,7 @@ fn repeated_nesting_retains_series_history_across_revision_checkpoints() { for step in 0..520 { let level = if step % 2 == 0 { 2 } else { 1 }; let edit = PageEdit::set_level(page.space(), level).unwrap(); - let written = PreparedEdit::pages(&source, &[edit]) + let written = ops::section_op(&source, SectionOp::Pages([edit].to_vec())) .unwrap() .as_bytes() .to_vec(); diff --git a/crates/onestore/tests/page_removal.rs b/crates/onestore/tests/page_removal.rs index 08a1e7ad2894c15dae67183c9a9009bf37b0ec69..19b9d2101a0f3e3b1c70e4333c056637d711ada4 100644 --- a/crates/onestore/tests/page_removal.rs +++ b/crates/onestore/tests/page_removal.rs @@ -1,3 +1,5 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ ExGuid, FileDataReference, IdStream, ObjectData, PropertySets, RevisionIndex, Store, Value, document::Document, @@ -525,7 +527,16 @@ fn copy_comparison_rejects_text_and_payload_changes() { .then_some(*oid) }) .unwrap(); - let changed = onestore::replace_text(copied, copy_sid, text, 0..0, "Changed ").unwrap(); + let changed = ops::page_edited( + copied, + copy_sid, + vec![onestore::op::PageOp::Text { + text, + range: 0..0, + with: "Changed ".into(), + }], + ) + .unwrap(); let changed_store = Store::parse(&changed).unwrap(); RevisionIndex::parse(&changed_store) .unwrap() diff --git a/crates/onestore/tests/page_tables.rs b/crates/onestore/tests/page_tables.rs index 85a87143eaabcd75db21683f49bd1c500934529b..8beeeba1c8d9d9d0ba61cf393716559f5ae74c75 100644 --- a/crates/onestore/tests/page_tables.rs +++ b/crates/onestore/tests/page_tables.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Format}, page::{ Page, PageObject, PageParagraph, ParagraphContent, Table, TableCell, TableColumn, TableRow, @@ -9,7 +11,6 @@ use onestore::{ const TREES: &[u8] = include_bytes!("../../../corpus/outline-edit/tree/before/notebook/synthetic.one"); -const AUTHOR: &str = "Table author"; fn page_by_title(bytes: &[u8], title: &str) -> (ExGuid, Page) { let store = Store::parse(bytes).unwrap(); @@ -174,19 +175,19 @@ fn a_row_and_a_column_are_added_to_a_native_table_and_removed_again() { new_cell(&template, "Second row, third column"), ], }); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - let stored = assert_same(written.as_bytes(), space, &after); + let written = ops::saved(TREES, space, &after).unwrap(); + let stored = assert_same(written.as_slice(), space, &after); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); if let Some(directory) = std::env::var_os("ONESTORE_TABLE_EDIT_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("synthetic.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("synthetic.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -204,8 +205,8 @@ fn a_row_and_a_column_are_added_to_a_native_table_and_removed_again() { row.cells.remove(1); } table.rows.remove(0); - let again = PreparedEdit::page(written.as_bytes(), space, &smaller, AUTHOR).unwrap(); - let mut stored = assert_same(again.as_bytes(), space, &smaller); + let again = ops::saved(written.as_slice(), space, &smaller).unwrap(); + let mut stored = assert_same(again.as_slice(), space, &smaller); let table = table_mut(&mut stored); assert_eq!((table.rows.len(), table.columns.len()), (1, 2)); } @@ -218,8 +219,8 @@ fn column_widths_locks_and_borders_change_in_place() { table.columns[0].width = 300.0; table.columns[1].locked = false; table.borders = Some(false); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - assert_same(written.as_bytes(), space, &after); + let written = ops::saved(TREES, space, &after).unwrap(); + assert_same(written.as_slice(), space, &after); } #[test] @@ -232,17 +233,17 @@ fn cell_shading_and_indents_change_in_place() { cell.shading = Some(0x0000ffff); let original_indents = cell.indents.clone(); cell.indents.clear(); - assert!(PreparedEdit::page(TREES, space, &after, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &after).is_err()); let cell = &mut table_mut(&mut after).rows[0].cells[0]; cell.indents = vec![18.0, 0.0, 27.0, 27.0]; - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - let stored = assert_same(written.as_bytes(), space, &after); + let written = ops::saved(TREES, space, &after).unwrap(); + let stored = assert_same(written.as_slice(), space, &after); let mut reset = stored.clone(); let cell = &mut table_mut(&mut reset).rows[0].cells[0]; cell.shading = None; cell.indents = original_indents; - let again = PreparedEdit::page(written.as_bytes(), space, &reset, AUTHOR).unwrap(); - assert_eq!(assert_same(again.as_bytes(), space, &reset), before); + let again = ops::saved(written.as_slice(), space, &reset).unwrap(); + assert_eq!(assert_same(again.as_slice(), space, &reset), before); } /// `ONESTORE_NESTED_TABLE_EXPORT` names a new directory receiving the candidate for a cold reopen. @@ -286,7 +287,7 @@ fn a_table_nests_inside_a_cell_and_is_removed_again() { table_mut(&mut after).rows[0].cells[1] .paragraphs .push(holder); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); + let written = ops::saved(TREES, space, &after).unwrap(); let ParagraphContent::Table(nested) = &mut table_mut(&mut after).rows[0].cells[1] .paragraphs .last_mut() @@ -296,12 +297,12 @@ fn a_table_nests_inside_a_cell_and_is_removed_again() { panic!() }; with_native_indents(nested); - let stored = assert_same(written.as_bytes(), space, &after); + let stored = assert_same(written.as_slice(), space, &after); if let Some(directory) = std::env::var_os("ONESTORE_NESTED_TABLE_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("synthetic.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("synthetic.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -316,8 +317,8 @@ fn a_table_nests_inside_a_cell_and_is_removed_again() { table_mut(&mut removed).rows[0].cells[1] .paragraphs .retain(|paragraph| paragraph.id != holder_id); - let again = PreparedEdit::page(written.as_bytes(), space, &removed, AUTHOR).unwrap(); - assert_eq!(assert_same(again.as_bytes(), space, &removed), before); + let again = ops::saved(written.as_slice(), space, &removed).unwrap(); + assert_eq!(assert_same(again.as_slice(), space, &removed), before); } #[test] @@ -328,20 +329,17 @@ fn inconsistent_tables_are_refused() { table_mut(&mut ragged).rows[0] .cells .push(new_cell(&template, "Extra")); - assert!(PreparedEdit::page(TREES, space, &ragged, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &ragged).is_err()); + // A cell keeps a paragraph. let mut empty = before.clone(); table_mut(&mut empty).rows[0].cells[0].paragraphs.clear(); - let written = PreparedEdit::page(TREES, space, &empty, AUTHOR).unwrap(); - let mut stored = page_in(written.as_bytes(), space); - let cell = &table_mut(&mut stored).rows[0].cells[0]; - assert_eq!(cell.paragraphs.len(), 1); - assert_eq!(cell.paragraphs[0].text().unwrap().text.text(), ""); + assert!(ops::saved(TREES, space, &empty).is_err()); let mut narrow = before.clone(); table_mut(&mut narrow).columns[0].width = 10.0; - assert!(PreparedEdit::page(TREES, space, &narrow, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &narrow).is_err()); let mut rowless = before.clone(); table_mut(&mut rowless).rows.clear(); - assert!(PreparedEdit::page(TREES, space, &rowless, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &rowless).is_err()); } /// `ONESTORE_TABLE_EXPORT` names a new directory receiving the candidate for a cold reopen. @@ -394,14 +392,14 @@ fn a_new_table_is_created_on_a_fresh_page() { holder.format = Format::default(); body_paragraphs(&mut after).push(holder); body_paragraphs(&mut after).push(cell_paragraph(&template, "After the table")); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); + let written = ops::saved(&source, space, &after).unwrap(); with_native_indents(table_mut(&mut after)); - assert_same(written.as_bytes(), space, &after); + assert_same(written.as_slice(), space, &after); if let Some(directory) = std::env::var_os("ONESTORE_TABLE_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("tables.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("tables.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( diff --git a/crates/onestore/tests/page_tags.rs b/crates/onestore/tests/page_tags.rs index 16bd65cbd386c77452913718e578ed4a179a37d4..eec322c51ca55e55bd0f5e1c8199fb83bda499e6 100644 --- a/crates/onestore/tests/page_tags.rs +++ b/crates/onestore/tests/page_tags.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Format, Kind, Tag}, page::{ Definition, Page, PageObject, PageParagraph, ParagraphContent, TextObject, text::new_id, @@ -8,7 +10,6 @@ use onestore::{ const TREES: &[u8] = include_bytes!("../../../corpus/outline-edit/tree/before/notebook/synthetic.one"); -const AUTHOR: &str = "Tag author"; fn page_by_title(bytes: &[u8], title: &str) -> (ExGuid, Page) { let store = Store::parse(bytes).unwrap(); @@ -161,13 +162,13 @@ fn removing_a_tag_leaves_its_definition_and_the_other_paragraphs_untouched() { .unwrap() .tags .clear(); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); + let written = ops::saved(TREES, space, &after).unwrap(); let mut expected = after.clone(); expected.definitions.remove(&definition); - let stored = assert_same(written.as_bytes(), space, &expected); + let stored = assert_same(written.as_slice(), space, &expected); assert!(!stored.definitions.contains_key(&definition)); assert_eq!( - raw_object(written.as_bytes(), space, definition), + raw_object(written.as_slice(), space, definition), raw_object(TREES, space, definition) ); let count = body_paragraphs(&mut after).len(); @@ -176,7 +177,7 @@ fn removing_a_tag_leaves_its_definition_and_the_other_paragraphs_untouched() { .unwrap() .id; assert_eq!( - raw_object(written.as_bytes(), space, other), + raw_object(written.as_slice(), space, other), raw_object(TREES, space, other) ); } @@ -195,24 +196,17 @@ fn an_existing_definition_tags_another_paragraph_and_a_task_completes_in_place() .unwrap() .tags .push(tag(definition, false)); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - let stored = assert_same(written.as_bytes(), space, &after); + let written = ops::saved(TREES, space, &after).unwrap(); + let stored = assert_same(written.as_slice(), space, &after); let mut completed = stored.clone(); let text = body_paragraphs(&mut completed)[other].text_mut().unwrap(); text.tags[0].status = 1; text.tags[0].completed = Some(1_262_405_106); - let again = PreparedEdit::page(written.as_bytes(), space, &completed, AUTHOR).unwrap(); - assert_same(again.as_bytes(), space, &completed); + let again = ops::saved(written.as_slice(), space, &completed).unwrap(); + assert_same(again.as_slice(), space, &completed); assert_eq!( - PreparedEdit::page( - again.as_bytes(), - space, - &page_in(again.as_bytes(), space), - AUTHOR - ) - .unwrap() - .as_bytes(), - again.as_bytes() + ops::saved(&again, space, &page_in(&again, space)).unwrap(), + again ); } @@ -225,7 +219,7 @@ fn tags_without_a_known_definition_are_refused() { .unwrap() .tags .push(tag(new_id().unwrap(), false)); - assert!(PreparedEdit::page(TREES, space, &missing, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &missing).is_err()); let mut repeated = before.clone(); let at = tagged(&repeated); let existing = body_paragraphs(&mut repeated)[at].text().unwrap().tags[0].clone(); @@ -234,7 +228,7 @@ fn tags_without_a_known_definition_are_refused() { .unwrap() .tags .push(tag(existing.definition.unwrap(), false)); - assert!(PreparedEdit::page(TREES, space, &repeated, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &repeated).is_err()); let mut wrong = before.clone(); let list = wrong .definitions @@ -247,7 +241,7 @@ fn tags_without_a_known_definition_are_refused() { .unwrap() .tags .push(tag(list, false)); - assert!(PreparedEdit::page(TREES, space, &wrong, AUTHOR).is_err()); + assert!(ops::saved(TREES, space, &wrong).is_err()); } } @@ -293,13 +287,13 @@ fn new_definitions_and_tags_publish_on_a_fresh_page() { both.text_mut().unwrap().tags.push(tag(task, false)); body_paragraphs(&mut after).push(both); body_paragraphs(&mut after).push(plain_paragraph(&template, "Untagged")); - let written = PreparedEdit::page(&source, space, &after, AUTHOR).unwrap(); - assert_same(written.as_bytes(), space, &after); + let written = ops::saved(&source, space, &after).unwrap(); + assert_same(written.as_slice(), space, &after); if let Some(directory) = std::env::var_os("ONESTORE_TAG_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("tags.one"), written.as_bytes()).unwrap(); - let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write(directory.join("tags.one"), written.as_slice()).unwrap(); + let written_store = Store::parse(written.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( diff --git a/crates/onestore/tests/page_writer.rs b/crates/onestore/tests/page_writer.rs index 01a19853c98863b7b2887875d3e09da08c2a3b83..db9f45ce7ad0db0e4b478d8c12c71a314a9ce3da 100644 --- a/crates/onestore/tests/page_writer.rs +++ b/crates/onestore/tests/page_writer.rs @@ -1,15 +1,34 @@ -//! The page writer must publish the same graph the typed writers publish for the same edit, -//! in one transaction, and must round-trip the model it was given. +//! A page edit lowered whole (`op::lower_page`) must publish the same graph its ops applied +//! one by one publish, in one transaction, and must round-trip the model it was given. + +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, Insertion, OutlineEdit, ParagraphJoin, ParagraphSplit, PreparedEdit, RevisionIndex, - Store, TextAttribute, TreeEdit, + ExGuid, OutlineEdit, RevisionIndex, Store, TextAttribute, document::{Document, Element, Kind, Revision}, + op::PageOp, page::{Page, PageObject, ParagraphContent, text::new_id}, }; use std::collections::BTreeSet; -const AUTHOR: &str = "Page author"; +/// `source` after `ops` on the page in `space`. +fn edited(source: &[u8], space: ExGuid, ops: Vec) -> Vec { + ops::page_edited(source, space, ops).unwrap() +} + +/// A plain paragraph of `text` inserted in `container` before `before`, at `level`. +fn insert(container: ExGuid, before: Option, text: &str, level: u32) -> PageOp { + let mut paragraph = ops::paragraph(text); + paragraph.level = level; + PageOp::Insert { + container, + before, + paragraphs: vec![paragraph], + } +} + +const AUTHOR: &str = "Author"; const PARAGRAPHS: &[u8] = include_bytes!("../../../corpus/paragraph-edit/before/notebook/synthetic.one"); const OUTLINES: &[u8] = @@ -144,7 +163,7 @@ fn same_element(revision_a: &Revision<'_>, revision_b: &Revision<'_>, id: ExGuid } } -/// Every active revision the typed writers produced is reproduced by the page writer, +/// Every active revision the ops produced one by one is reproduced by the page lowered whole, /// ignoring writer-allocated immutable styles and author records. Modification times are /// not compared: a reorder can be attributed to either sibling, and the model cannot say which. fn equivalent(reference: &[u8], written: &[u8]) { @@ -182,11 +201,11 @@ fn equivalent(reference: &[u8], written: &[u8]) { } } -/// Runs the page writer on the model the typed writers produced and checks both contracts. +/// Lowers the page the ops produced whole and checks both contracts. fn oracle(source: &[u8], space: ExGuid, reference: &[u8]) -> Vec { let after = page_in(reference, space); - let prepared = PreparedEdit::page(source, space, &after, AUTHOR).unwrap(); - let written = prepared.as_bytes().to_vec(); + let prepared = ops::saved(source, space, &after).unwrap(); + let written = prepared.as_slice().to_vec(); assert_eq!(page_in(&written, space), after, "model round trip"); equivalent(reference, &written); let transactions = |bytes: &[u8]| Store::parse(bytes).unwrap().header.transaction_count; @@ -209,9 +228,12 @@ fn text_replacement_matches_replace_text() { (end..end, " end"), (0..end, ""), ] { - let reference = - onestore::replace_text(PARAGRAPHS, space, text, range, replacement).unwrap(); - oracle(PARAGRAPHS, space, &reference); + let op = PageOp::Text { + text, + range, + with: replacement.into(), + }; + oracle(PARAGRAPHS, space, &edited(PARAGRAPHS, space, vec![op])); } } @@ -220,28 +242,16 @@ fn formatting_matches_format_text() { let (space, page) = page_by_title(PARAGRAPHS, "Split style boundary"); let (_, text, content) = text_ids(body(&page)[0])[0].clone(); let end = u32::try_from(content.encode_utf16().count()).unwrap(); - let reference = PreparedEdit::format( - PARAGRAPHS, - space, - text, - 1..end.min(4), - &[ + let reference = ops::page_op(PARAGRAPHS, space, PageOp::Format { text, range: 1..end.min(4), set: vec![ TextAttribute::Bold(true), TextAttribute::Color(Some([255, 0, 0])), TextAttribute::FontSize(14.0), - ], - ) + ], clear: Vec::new() }) .unwrap(); - let reference = PreparedEdit::format( - reference.as_bytes(), - space, - text, - 0..1, - &[ + let reference = ops::page_op(reference.as_bytes(), space, PageOp::Format { text, range: 0..1, set: vec![ TextAttribute::Italic(true), TextAttribute::Font("Consolas".into()), - ], - ) + ], clear: Vec::new() }) .unwrap(); oracle(PARAGRAPHS, space, reference.as_bytes()); } @@ -257,14 +267,13 @@ fn paragraph_insertions_match_insertion() { .find(|p| p.parent.is_none()) .unwrap() .id; - for intent in [ - Insertion::paragraph(outline.id, None, "Appended 🦋 é", AUTHOR).unwrap(), - Insertion::paragraph(outline.id, Some(paragraphs[0].0), "First", AUTHOR).unwrap(), - Insertion::paragraph(parent, None, "Nested child", AUTHOR).unwrap(), - Insertion::paragraph(outline.id, None, "", AUTHOR).unwrap(), + for op in [ + insert(outline.id, None, "Appended 🦋 é", 1), + insert(outline.id, Some(paragraphs[0].0), "First", 1), + insert(parent, None, "Nested child", 2), + insert(outline.id, None, "", 1), ] { - let reference = PreparedEdit::insert(OUTLINES, space, &intent).unwrap(); - oracle(OUTLINES, space, reference.as_bytes()); + oracle(OUTLINES, space, &edited(OUTLINES, space, vec![op])); } } @@ -281,27 +290,23 @@ fn cell_insertion_and_deletion_match() { }) .unwrap(); let cell = &table.rows[0].cells[0]; - let intent = Insertion::paragraph(cell.id, None, "Cell text", AUTHOR).unwrap(); - let reference = PreparedEdit::insert(TREES, space, &intent).unwrap(); - oracle(TREES, space, reference.as_bytes()); + let reference = edited(TREES, space, vec![insert(cell.id, None, "Cell text", 1)]); + oracle(TREES, space, &reference); let victim = cell .paragraphs .iter() .find(|p| p.parent.is_none()) .unwrap() .id; - let reference = - PreparedEdit::tree(TREES, space, &TreeEdit::delete(victim, AUTHOR).unwrap()).unwrap(); - oracle(TREES, space, reference.as_bytes()); + let reference = edited(&reference, space, vec![PageOp::Delete { object: victim }]); + oracle(TREES, space, &reference); } #[test] fn outline_insertion_matches_insertion() { let (space, _) = page_by_title(OUTLINES, "Move leaf down"); - let page = page_id(OUTLINES, space); - let intent = Insertion::outline(page, 144.0, 200.0, "New outline 東京", AUTHOR).unwrap(); - let reference = PreparedEdit::insert(OUTLINES, space, &intent).unwrap(); - oracle(OUTLINES, space, reference.as_bytes()); + let (add, ..) = ops::new_outline(144.0, 200.0, "New outline 東京"); + oracle(OUTLINES, space, &edited(OUTLINES, space, vec![add])); } #[test] @@ -325,16 +330,24 @@ fn splits_and_joins_match_their_writers() { .is_none_or(|u| !(0xd800..=0xdbff).contains(&u)) }) .unwrap(); - let split = ParagraphSplit::new(text, offset, AUTHOR).unwrap(); - let reference = PreparedEdit::split(PARAGRAPHS, space, &split).unwrap(); - oracle(PARAGRAPHS, space, reference.as_bytes()); + let lists = body(&page)[0].paragraphs[0].lists.len(); + let split = PageOp::Split { + text, + at: offset, + paragraph: new_id().unwrap(), + right: new_id().unwrap(), + lists: (0..lists).map(|_| new_id().unwrap()).collect(), + }; + oracle(PARAGRAPHS, space, &edited(PARAGRAPHS, space, vec![split])); } for title in ["Split middle", "Split empty"] { let (space, page) = page_by_title(PARAGRAPHS, title); let paragraphs = text_ids(body(&page)[0]); - let join = ParagraphJoin::new(paragraphs[0].1, paragraphs[1].1, AUTHOR).unwrap(); - let reference = PreparedEdit::join(PARAGRAPHS, space, &join).unwrap(); - oracle(PARAGRAPHS, space, reference.as_bytes()); + let join = PageOp::Join { + left: paragraphs[0].1, + right: paragraphs[1].1, + }; + oracle(PARAGRAPHS, space, &edited(PARAGRAPHS, space, vec![join])); } } @@ -357,18 +370,28 @@ fn subtree_moves_and_deletions_match_tree_edits() { .filter(|p| p.parent.is_none()) .map(|p| p.id) .collect(); - let edit = match build { - 0 | 1 => TreeEdit::move_to(top[0], outline.id, top.get(2).copied(), AUTHOR).unwrap(), - 2 | 3 => TreeEdit::delete(top[0], AUTHOR).unwrap(), - 4 => TreeEdit::move_to(outline.id, page_id(OUTLINES, space), None, AUTHOR).unwrap(), - _ => TreeEdit::delete(outline.id, AUTHOR).unwrap(), + let op = match build { + 0 | 1 => PageOp::Move { + object: top[0], + parent: Some(outline.id), + before: top.get(2).copied(), + }, + 2 | 3 => PageOp::Delete { object: top[0] }, + 4 => PageOp::Move { + object: outline.id, + parent: None, + before: None, + }, + _ => PageOp::Delete { object: outline.id }, }; - let reference = PreparedEdit::tree(OUTLINES, space, &edit).unwrap(); - oracle(OUTLINES, space, reference.as_bytes()); + oracle(OUTLINES, space, &edited(OUTLINES, space, vec![op])); if outlines.len() > 1 && build == 0 { - let across = TreeEdit::move_to(top[1], outlines[1].id, None, AUTHOR).unwrap(); - let reference = PreparedEdit::tree(OUTLINES, space, &across).unwrap(); - oracle(OUTLINES, space, reference.as_bytes()); + let across = PageOp::Move { + object: top[1], + parent: Some(outlines[1].id), + before: None, + }; + oracle(OUTLINES, space, &edited(OUTLINES, space, vec![across])); } } } @@ -401,7 +424,7 @@ fn outline_layout_and_collapse_match_outline_edits() { ), (parent, OutlineEdit::Collapsed(true)), ] { - let reference = PreparedEdit::outline(OUTLINES, space, object, edit).unwrap(); + let reference = ops::page_op(OUTLINES, space, PageOp::Outline { object, edit }).unwrap(); oracle(OUTLINES, space, reference.as_bytes()); } } @@ -411,38 +434,41 @@ fn composed_edits_publish_one_transaction() { let (space, page) = page_by_title(OUTLINES, "Move subtree up"); let outline = body(&page)[0]; let paragraphs = text_ids(outline); - let mut reference = - onestore::replace_text(OUTLINES, space, paragraphs[0].1, 0..0, "Lead ").unwrap(); - let insertion = - Insertion::paragraph(outline.id, Some(paragraphs[1].0), "Inserted", AUTHOR).unwrap(); - reference = PreparedEdit::insert(&reference, space, &insertion) - .unwrap() - .as_bytes() - .to_vec(); - reference = PreparedEdit::format( + let mut reference = edited( + OUTLINES, + space, + vec![PageOp::Text { + text: paragraphs[0].1, + range: 0..0, + with: "Lead ".into(), + }], + ); + let insertion = insert(outline.id, Some(paragraphs[1].0), "Inserted", 1); + let PageOp::Insert { paragraphs: inserted, .. } = &insertion else { + unreachable!() + }; + let inserted = inserted[0].text().unwrap().id; + reference = edited(&reference, space, vec![insertion]); + reference = edited( &reference, space, - insertion.text_object(), - 0..3, - &[TextAttribute::Bold(true)], - ) - .unwrap() - .as_bytes() - .to_vec(); + vec![PageOp::Format { + text: inserted, + range: 0..3, + set: vec![TextAttribute::Bold(true)], + clear: Vec::new(), + }], + ); let last = paragraphs.last().unwrap().0; - reference = PreparedEdit::tree(&reference, space, &TreeEdit::delete(last, AUTHOR).unwrap()) - .unwrap() - .as_bytes() - .to_vec(); - reference = PreparedEdit::outline( + reference = edited(&reference, space, vec![PageOp::Delete { object: last }]); + reference = edited( &reference, space, - outline.id, - OutlineEdit::Position { x: 60.0, y: 60.0 }, - ) - .unwrap() - .as_bytes() - .to_vec(); + vec![PageOp::Outline { + object: outline.id, + edit: OutlineEdit::Position { x: 60.0, y: 60.0 }, + }], + ); let written = oracle(OUTLINES, space, &reference); assert_eq!( Store::parse(&reference).unwrap().header.transaction_count, @@ -535,8 +561,8 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { ); outline.paragraphs.push(paragraph); after.objects.push(PageObject::Outline(outline)); - let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(TREES, space, &after).unwrap(); + let stored = page_in(written.as_slice(), space); let normalize = |page: &Page| -> Vec<(ExGuid, Vec<(ExGuid, String)>)> { body(page) .iter() @@ -565,7 +591,7 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { let store = Store::parse(TREES).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let raw_before = index.resolve_active(space).unwrap(); - let store = Store::parse(written.as_bytes()).unwrap(); + let store = Store::parse(written.as_slice()).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let raw_after = index.resolve_active(space).unwrap(); let touched: BTreeSet = body(&after) @@ -604,7 +630,7 @@ fn unsupported_model_edits_are_rejected_before_writing() { } } } - assert!(PreparedEdit::page(TREES, space, &widened, AUTHOR).is_ok()); + assert!(ops::saved(TREES, space, &widened).is_ok()); let mut relisted = page_in(TREES, space); let outline = body(&page)[0]; for object in &mut relisted.objects { @@ -614,11 +640,33 @@ fn unsupported_model_edits_are_rejected_before_writing() { o.paragraphs[0].lists.push(new_id().unwrap()); } } - assert!(PreparedEdit::page(TREES, space, &relisted, AUTHOR).is_err()); - let mut renamed = page_in(TREES, space); - renamed.created = Some(1); - assert!(PreparedEdit::page(TREES, space, &renamed, AUTHOR).is_err()); - assert!(PreparedEdit::page(TREES, space, &page, "a\0b").is_err()); + assert!(ops::saved(TREES, space, &relisted).is_err()); + // A page's date is an op of its own now. + let mut redated = page_in(TREES, space); + redated.created = Some(1); + let written = ops::saved(TREES, space, &redated).unwrap(); + assert_eq!(page_in(&written, space).created, Some(1)); + let text = page + .objects + .iter() + .find_map(|object| match object { + PageObject::Title(title) => title + .outlines + .iter() + .flat_map(|outline| &outline.paragraphs) + .find_map(|paragraph| paragraph.text().map(|text| text.id)), + _ => None, + }) + .unwrap(); + let op = onestore::op::Op::Page { + space, + op: PageOp::Text { + text, + range: 0..0, + with: "x".into(), + }, + }; + assert!(ops::transaction(TREES, "a\0b", vec![op]).is_err()); } /// Unset run values stay unset; only the language, which MS-ONE requires on every run, comes @@ -655,8 +703,8 @@ fn a_new_paragraph_with_unspecified_formatting_rereads_with_only_the_required_la unsupported: Vec::new(), }; page.objects.insert(0, PageObject::Outline(outline)); - let written = PreparedEdit::page(OUTLINES, space, &page, AUTHOR).unwrap(); - let stored = page_in(written.as_bytes(), space); + let written = ops::saved(OUTLINES, space, &page).unwrap(); + let stored = page_in(written.as_slice(), space); let read_back = body(&stored) .iter() .flat_map(|outline| outline.paragraphs.iter()) @@ -673,18 +721,18 @@ fn a_new_paragraph_with_unspecified_formatting_rereads_with_only_the_required_la } ); assert_eq!( - PreparedEdit::page(written.as_bytes(), space, &stored, AUTHOR) + ops::saved(written.as_slice(), space, &stored) .unwrap() - .as_bytes(), - written.as_bytes() + .as_slice(), + written.as_slice() ); } #[test] fn an_unchanged_model_publishes_nothing() { let (space, page) = page_by_title(OUTLINES, "Resize outline"); - let prepared = PreparedEdit::page(OUTLINES, space, &page, AUTHOR).unwrap(); - assert_eq!(prepared.as_bytes(), OUTLINES); + let prepared = ops::saved(OUTLINES, space, &page).unwrap(); + assert_eq!(prepared.as_slice(), OUTLINES); } const JOIN_PAGE: &str = "Bold 🦀 italic e\u{301} color 東京"; @@ -1001,8 +1049,8 @@ fn export_native_page_model_candidates() { }); } } - let edit = PreparedEdit::page(&bytes, space, &after, "Rust page writer").unwrap(); - bytes = edit.as_bytes().to_vec(); + let edit = ops::saved(&bytes, space, &after).unwrap(); + bytes = edit.as_slice().to_vec(); let stored = page_in(&bytes, space); let index = { let store = Store::parse(&bytes).unwrap(); diff --git a/crates/onestore/tests/paragraph.rs b/crates/onestore/tests/paragraph.rs index 03d0651e6459e3d38e82104853235443a4aee73b..4904916f1c240763bbe1e49f350406e8c768d53d 100644 --- a/crates/onestore/tests/paragraph.rs +++ b/crates/onestore/tests/paragraph.rs @@ -1,6 +1,8 @@ use onestore::{ - ExGuid, ParagraphJoin, ParagraphSplit, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Kind, Revision}, + op::{Op, OpError, PageOp}, + page::text::new_id, }; use serde_json::Value; @@ -10,6 +12,40 @@ mod checkpoint; mod current; #[path = "support/disk.rs"] mod disk; +#[path = "support/ops.rs"] +mod ops; + +/// Enter at `at` in `text`, with fresh identities for the new paragraph, its text and a +/// copy of each list node of the paragraph: the op, the new paragraph and its text. +fn split(source: &[u8], space: ExGuid, text: ExGuid, at: u32) -> (Op, ExGuid, ExGuid) { + let store = Store::parse(source).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let lists = document.spaces[&space] + .active() + .unwrap() + .nodes + .values() + .find_map(|node| match &node.kind { + Kind::Paragraph { lists, .. } if node.content == [text] => Some(lists.len()), + _ => None, + }) + .unwrap_or(0); + let (paragraph, right) = (new_id().unwrap(), new_id().unwrap()); + let op = PageOp::Split { + text, + at, + paragraph, + right, + lists: (0..lists).map(|_| new_id().unwrap()).collect(), + }; + (Op::Page { space, op }, paragraph, right) +} + +/// `source` after joining the paragraph of text `right` to the one of text `left`. +fn join(source: &[u8], space: ExGuid, left: ExGuid, right: ExGuid) -> Result, OpError> { + ops::edited(source, vec![Op::Page { space, op: PageOp::Join { left, right } }]) +} const SOURCE: &[u8] = include_bytes!("../../../corpus/paragraph-edit/before/notebook/synthetic.one"); @@ -114,12 +150,9 @@ fn joins_match_native_graphs_tags_and_inherited_character_styles() { .contains_key(&left) }) .unwrap(); - let intent = ParagraphJoin::new(left, right, "Join author").unwrap(); - let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap(); - assert_eq!(intent, restored); - let edited = PreparedEdit::join(source, *sid, &restored) - .unwrap_or_else(|error| panic!("{name}: {error}")); - let current_store = Store::parse(edited.as_bytes()).unwrap(); + let edited = + join(source, *sid, left, right).unwrap_or_else(|error| panic!("{name}: {error}")); + let current_store = Store::parse(&edited).unwrap(); assert_eq!( current_store.header.transaction_count, store.header.transaction_count + 1 @@ -276,7 +309,7 @@ fn joins_match_native_graphs_tags_and_inherited_character_styles() { } } } - assert!(PreparedEdit::join(edited.as_bytes(), *sid, &intent).is_err()); + assert!(join(&edited, *sid, left, right).is_err()); assert_eq!( space.contexts.len(), current_document.spaces[sid].contexts.len() @@ -321,10 +354,8 @@ fn splits_partition_native_paragraphs_at_every_scalar_boundary() { })) .collect(); for (position, offset) in offsets.iter().enumerate() { - let intent = ParagraphSplit::new(text, *offset, "Split author").unwrap(); - let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap(); - assert_eq!(intent, restored); - let edited = PreparedEdit::split(SOURCE, *sid, &restored); + let (op, new_paragraph, new_text) = split(SOURCE, *sid, text, *offset); + let edited = ops::edited(SOURCE, vec![op.clone()]); // OneNote 2010 splits before or after a hyperlink and ignores Enter inside one. let link = |at: usize| { expected @@ -341,7 +372,7 @@ fn splits_partition_native_paragraphs_at_every_scalar_boundary() { } let edited = edited.unwrap_or_else(|error| panic!("{} at {offset}: {error}", case["case"])); - let current_store = Store::parse(edited.as_bytes()).unwrap(); + let current_store = Store::parse(&edited).unwrap(); assert_eq!( current_store.header.transaction_count, store.header.transaction_count + 1 @@ -384,7 +415,7 @@ fn splits_partition_native_paragraphs_at_every_scalar_boundary() { case["case"] ); assert_eq!( - characters(after, intent.text_object()), + characters(after, new_text), expected[position..], "{} suffix at {offset}", case["case"] @@ -392,7 +423,7 @@ fn splits_partition_native_paragraphs_at_every_scalar_boundary() { if case["offset_utf16"] == *offset { let native = &native_document.spaces[sid]; let native = &native.revisions[&native.contexts[&ExGuid::default()]]; - let new_text = serde_json::from_value(case["new_text"].clone()).unwrap(); + let native_text = serde_json::from_value(case["new_text"].clone()).unwrap(); assert_eq!( characters(after, text), characters(native, text), @@ -400,8 +431,8 @@ fn splits_partition_native_paragraphs_at_every_scalar_boundary() { case["case"] ); assert_eq!( - characters(after, intent.text_object()), - characters(native, new_text), + characters(after, new_text), + characters(native, native_text), "{}", case["case"] ); @@ -409,31 +440,31 @@ fn splits_partition_native_paragraphs_at_every_scalar_boundary() { let mut children = parent_node.children.clone(); children.insert( children.iter().position(|id| *id == paragraph).unwrap() + 1, - intent.object(), + new_paragraph, ); assert_eq!(after.nodes[parent].children, children); assert_eq!(after.nodes[¶graph].content, [text]); assert!(after.nodes[¶graph].children.is_empty()); assert_eq!( - after.nodes[&intent.object()].content, - [intent.text_object()] + after.nodes[&new_paragraph].content, + [new_text] ); assert_eq!( - after.nodes[&intent.object()].children, + after.nodes[&new_paragraph].children, before.nodes[¶graph].children ); assert_eq!( serde_json::to_value(&after.nodes[&text].tags).unwrap(), serde_json::to_value(&before.nodes[&text].tags).unwrap() ); - assert!(after.nodes[&intent.text_object()].tags.is_empty()); + assert!(after.nodes[&new_text].tags.is_empty()); let Kind::Paragraph { lists: old_lists, .. } = &before.nodes[¶graph].kind else { panic!() }; - let Kind::Paragraph { lists, .. } = &after.nodes[&intent.object()].kind else { + let Kind::Paragraph { lists, .. } = &after.nodes[&new_paragraph].kind else { panic!() }; assert_eq!(old_lists.len(), lists.len()); @@ -453,26 +484,23 @@ fn splits_partition_native_paragraphs_at_every_scalar_boundary() { } } } - assert!(PreparedEdit::split(edited.as_bytes(), *sid, &intent).is_err()); + assert!(ops::edited(&edited, vec![op]).is_err()); } for offset in 0..=*offsets.last().unwrap() + 1 { if offsets.contains(&offset) { continue; } - let intent = ParagraphSplit::new(text, offset, "Author").unwrap(); - assert!(PreparedEdit::split(SOURCE, *sid, &intent).is_err()); + assert!(ops::edited(SOURCE, vec![split(SOURCE, *sid, text, offset).0]).is_err()); } } } #[test] fn invalid_split_identities_and_title_targets_are_rejected() { - assert!(ParagraphSplit::new(ExGuid::default(), 0, "Author").is_err()); let manifest: Value = serde_json::from_str(include_str!("../../../corpus/paragraph-edit/manifest.json")).unwrap(); let text: ExGuid = serde_json::from_value(manifest["cases"][0]["original_text"].clone()).unwrap(); - assert!(ParagraphSplit::new(text, 0, "a\0b").is_err()); let store = Store::parse(SOURCE).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); @@ -486,18 +514,19 @@ fn invalid_split_identities_and_title_targets_are_rejected() { }) .unwrap() .0; - let intent = ParagraphSplit::new(text, 1, "Author").unwrap(); - for (field, value) in [ - ("guid", serde_json::to_value([0_u8; 16]).unwrap()), - ("guid", serde_json::to_value(text.guid).unwrap()), - ("text", serde_json::to_value(ExGuid::default()).unwrap()), - ("author", serde_json::json!("a\0b")), - ("offset", serde_json::json!(u32::MAX)), + let (op, ..) = split(SOURCE, sid, text, 1); + assert!(ops::transaction(SOURCE, "a\0b", vec![op.clone()]).is_err()); + let Op::Page { op: PageOp::Split { paragraph, right, lists, .. }, .. } = op else { + unreachable!() + }; + for (text, at, paragraph) in [ + (ExGuid::default(), 1, paragraph), + (text, u32::MAX, paragraph), + (text, 1, ExGuid::default()), + (text, 1, text), ] { - let mut encoded = serde_json::to_value(&intent).unwrap(); - encoded[field] = value; - let forged = serde_json::from_value(encoded).unwrap(); - assert!(PreparedEdit::split(SOURCE, sid, &forged).is_err()); + let op = PageOp::Split { text, at, paragraph, right, lists: lists.clone() }; + assert!(ops::page_edited(SOURCE, sid, vec![op]).is_err()); } let mut titles = 0; for (sid, _) in document.pages().unwrap() { @@ -516,8 +545,7 @@ fn invalid_split_identities_and_title_targets_are_rejected() { let node = &view.nodes[&id]; pending.extend(node.children.iter().chain(&node.content).copied()); if matches!(node.kind, Kind::RichText { .. }) { - let intent = ParagraphSplit::new(id, 0, "Author").unwrap(); - assert!(PreparedEdit::split(SOURCE, sid, &intent).is_err()); + assert!(ops::edited(SOURCE, vec![split(SOURCE, sid, id, 0).0]).is_err()); titles += 1; } } @@ -551,16 +579,14 @@ fn export_native_paragraph_splits() { }) .unwrap() .0; - let intent = ParagraphSplit::new( - text, - serde_json::from_value(case["offset_utf16"].clone()).unwrap(), - "Rust split author", - ) - .unwrap(); - let edited = PreparedEdit::split(&source, sid, &intent).unwrap(); - written.push(serde_json::json!({"case": case["case"], "intent": intent, - "new_paragraph": intent.object(), "new_text": intent.text_object()})); - source = edited.as_bytes().to_vec(); + let at = serde_json::from_value(case["offset_utf16"].clone()).unwrap(); + let (op, new_paragraph, new_text) = split(&source, sid, text, at); + let transaction = ops::transaction(&source, "Rust split author", vec![op.clone()]) + .unwrap() + .unwrap(); + written.push(serde_json::json!({"case": case["case"], "op": op, + "new_paragraph": new_paragraph, "new_text": new_text})); + transaction.apply(&mut source).unwrap(); } let candidate = output.join("candidate"); fs::create_dir(&candidate).unwrap(); @@ -587,18 +613,36 @@ fn interrupted_splits_publish_a_complete_graph_or_retain_the_original() { .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) .unwrap(); let paragraph = view.nodes[outline].children[0]; - let insertion = onestore::Insertion::paragraph(paragraph, None, "a🦀b", "Author") - .unwrap() - .with_formatting(1..3, &[onestore::TextAttribute::Bold(true)]) - .unwrap(); - let inserted = PreparedEdit::insert(&original, sid, &insertion).unwrap(); - let source = inserted.as_bytes(); - let checkpoint = checkpoint::pending(source, sid, insertion.text_object(), 0x14001d7a); + let mut child = ops::paragraph("a🦀b"); + child.level = 2; + let text = child.text().unwrap().id; + let inserted = ops::page_edited( + &original, + sid, + vec![ + PageOp::Insert { + container: paragraph, + before: None, + paragraphs: vec![child], + }, + PageOp::Format { + text, + range: 1..3, + set: vec![onestore::TextAttribute::Bold(true)], + clear: Vec::new(), + }, + ], + ) + .unwrap(); + let source = inserted.as_slice(); + let checkpoint = checkpoint::pending(source, sid, text); for source in [source, &checkpoint] { - let intent = ParagraphSplit::new(insertion.text_object(), 1, "Author").unwrap(); - let edit = PreparedEdit::split(source, sid, &intent).unwrap(); + let (op, ..) = split(source, sid, text, 1); + let edit = ops::transaction(source, "Author", vec![op]).unwrap().unwrap(); + let mut written = source.to_vec(); + edit.apply(&mut written).unwrap(); let before = current::current(source); - let after = current::current(edit.as_bytes()); + let after = current::current(&written); for write_limit in [17, 4096] { let disk = |fail_at| disk::Disk { visible: source.to_vec(), @@ -610,7 +654,7 @@ fn interrupted_splits_publish_a_complete_graph_or_retain_the_original() { }; let mut successful = disk(None); edit.commit(&mut successful).unwrap(); - assert_eq!(successful.durable, edit.as_bytes()); + assert_eq!(successful.durable, written); for at in 1..=successful.operation { let mut interrupted = disk(Some(at)); let error = edit.commit(&mut interrupted).unwrap_err(); @@ -654,10 +698,12 @@ fn export_native_paragraph_joins() { }) .unwrap() .0; - let intent = ParagraphJoin::new(left, right, "Rust join author").unwrap(); - let edit = PreparedEdit::join(&source, sid, &intent).unwrap(); - source = edit.as_bytes().to_vec(); - manifest.push(serde_json::json!({"case":name,"space":sid,"intent":intent})); + let op = Op::Page { space: sid, op: PageOp::Join { left, right } }; + let transaction = ops::transaction(&source, "Rust join author", vec![op.clone()]) + .unwrap() + .unwrap(); + transaction.apply(&mut source).unwrap(); + manifest.push(serde_json::json!({"case":name,"space":sid,"op":op})); } let folder = output.join(["split", "inheritance", "tags"][number]); fs::create_dir_all(folder.join("candidate")).unwrap(); @@ -685,22 +731,48 @@ fn interrupted_joins_preserve_complete_graphs_and_empty_text_adoption() { .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) .unwrap(); let left = view.nodes[&view.nodes[&outline].children[0]].content[0]; - let right = onestore::Insertion::paragraph(outline, None, "Right 🦀", "Author") - .unwrap() - .with_formatting(0..5, &[onestore::TextAttribute::Bold(true)]) - .unwrap(); - let inserted = PreparedEdit::insert(&source, sid, &right).unwrap(); - let child = - onestore::Insertion::paragraph(right.object(), None, "Retained child", "Author").unwrap(); - let original = PreparedEdit::insert(inserted.as_bytes(), sid, &child).unwrap(); - let original = original.as_bytes(); - let empty = onestore::replace_text(original, sid, left, 0..4, "").unwrap(); - let checkpoint = checkpoint::pending(original, sid, left, 0x14001d7a); + let right = ops::paragraph("Right 🦀"); + let right_text = right.text().unwrap().id; + let mut child = ops::paragraph("Retained child"); + child.parent = Some(right.id); + child.level = 2; + let original = ops::page_edited( + &source, + sid, + vec![ + PageOp::Insert { + container: outline, + before: None, + paragraphs: vec![right, child], + }, + PageOp::Format { + text: right_text, + range: 0..5, + set: vec![onestore::TextAttribute::Bold(true)], + clear: Vec::new(), + }, + ], + ) + .unwrap(); + let original = original.as_slice(); + let empty = ops::page_edited( + original, + sid, + vec![PageOp::Text { + text: left, + range: 0..4, + with: String::new(), + }], + ) + .unwrap(); + let checkpoint = checkpoint::pending(original, sid, left); for source in [original, &empty, &checkpoint] { - let intent = ParagraphJoin::new(left, right.text_object(), "Join author").unwrap(); - let edit = PreparedEdit::join(source, sid, &intent).unwrap(); + let op = Op::Page { space: sid, op: PageOp::Join { left, right: right_text } }; + let edit = ops::transaction(source, "Join author", vec![op]).unwrap().unwrap(); + let mut written = source.to_vec(); + edit.apply(&mut written).unwrap(); let before = current::current(source); - let after = current::current(edit.as_bytes()); + let after = current::current(&written); for write_limit in [17, 4096] { let disk = |fail_at| disk::Disk { visible: source.to_vec(), @@ -712,8 +784,8 @@ fn interrupted_joins_preserve_complete_graphs_and_empty_text_adoption() { }; let mut successful = disk(None); edit.commit(&mut successful).unwrap(); - assert_eq!(successful.durable, edit.as_bytes()); - for at in std::iter::once(1).chain(source.len().div_ceil(193)..=successful.operation) { + assert_eq!(successful.durable, written); + for at in 1..=successful.operation { let mut interrupted = disk(Some(at)); let error = edit.commit(&mut interrupted).unwrap_err(); let recovered = current::current(&interrupted.durable); @@ -748,26 +820,16 @@ fn joins_reject_invalid_identities_wrong_order_and_unrelated_pages() { .contains_key(left) }) .unwrap(); - assert!(ParagraphJoin::new(*left, *left, "Author").is_err()); - assert!(ParagraphJoin::new(ExGuid::default(), *right, "Author").is_err()); - assert!(ParagraphJoin::new(*left, *right, "a\0b").is_err()); - let intent = ParagraphJoin::new(*left, *right, "Author").unwrap(); - for (field, value) in [ - ("left", serde_json::to_value(ExGuid::default()).unwrap()), - ("right", serde_json::to_value(left).unwrap()), - ("author", serde_json::json!("a\0b")), + for (a, b) in [ + (*left, *left), + (ExGuid::default(), *right), + (*right, *left), + (*left, cases[1].2), ] { - let mut encoded = serde_json::to_value(&intent).unwrap(); - encoded[field] = value; - let invalid = serde_json::from_value(encoded).unwrap(); - assert!(PreparedEdit::join(source, *sid, &invalid).is_err()); - } - for intent in [ - ParagraphJoin::new(*right, *left, "Author").unwrap(), - ParagraphJoin::new(*left, cases[1].2, "Author").unwrap(), - ] { - assert!(PreparedEdit::join(source, *sid, &intent).is_err()); + assert!(join(source, *sid, a, b).is_err()); } + let op = Op::Page { space: *sid, op: PageOp::Join { left: *left, right: *right } }; + assert!(ops::transaction(source, "a\0b", vec![op]).is_err()); let view = &space.revisions[&space.contexts[&ExGuid::default()]]; let paragraph = *view .nodes @@ -781,8 +843,7 @@ fn joins_reject_invalid_identities_wrong_order_and_unrelated_pages() { .find(|node| node.children.contains(¶graph)) .unwrap(); let last = view.nodes[parent.children.last().unwrap()].content[0]; - let nonadjacent = ParagraphJoin::new(*left, last, "Author").unwrap(); - assert!(PreparedEdit::join(source, *sid, &nonadjacent).is_err()); + assert!(join(source, *sid, *left, last).is_err()); let title = view .nodes .iter() @@ -794,8 +855,7 @@ fn joins_reject_invalid_identities_wrong_order_and_unrelated_pages() { let node = &view.nodes[&id]; pending.extend(node.children.iter().chain(&node.content).copied()); if matches!(node.kind, Kind::RichText { .. }) { - let intent = ParagraphJoin::new(id, *right, "Author").unwrap(); - assert!(PreparedEdit::join(source, *sid, &intent).is_err()); + assert!(join(source, *sid, id, *right).is_err()); rejected += 1; } } diff --git a/crates/onestore/tests/paragraph_formatting.rs b/crates/onestore/tests/paragraph_formatting.rs index 2ef24da7969f4fa40082a3f0734871722c379972..6e2f855de5a431223628d61740988d6e8f96cac1 100644 --- a/crates/onestore/tests/paragraph_formatting.rs +++ b/crates/onestore/tests/paragraph_formatting.rs @@ -1,5 +1,7 @@ +#[path = "support/ops.rs"] +mod ops; use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + ExGuid, RevisionIndex, Store, document::{Document, Kind}, page::{Page, PageObject, Paragraph}, }; @@ -48,12 +50,12 @@ fn paragraph_properties_preserve_native_mixed_runs_and_shared_styles() { } } assert_eq!(count, 6); - let prepared = PreparedEdit::page(SOURCE, space, &after, "Format author").unwrap(); - assert_eq!(page(prepared.as_bytes()).1, after); + let prepared = ops::saved(SOURCE, space, &after).unwrap(); + assert_eq!(page(prepared.as_slice()).1, after); let before_store = Store::parse(SOURCE).unwrap(); let before_index = RevisionIndex::parse(&before_store).unwrap(); let before_doc = Document::parse(&before_index).unwrap(); - let after_store = Store::parse(prepared.as_bytes()).unwrap(); + let after_store = Store::parse(prepared.as_slice()).unwrap(); let after_index = RevisionIndex::parse(&after_store).unwrap(); let old = before_index.resolve_active(space).unwrap(); let new = after_index.resolve_active(space).unwrap(); @@ -65,7 +67,7 @@ fn paragraph_properties_preserve_native_mixed_runs_and_shared_styles() { if let Some(directory) = std::env::var_os("ONESTORE_PARAGRAPH_FORMAT_EXPORT") { let directory = std::path::PathBuf::from(directory); std::fs::create_dir(&directory).unwrap(); - std::fs::write(directory.join("synthetic.one"), prepared.as_bytes()).unwrap(); + std::fs::write(directory.join("synthetic.one"), prepared.as_slice()).unwrap(); std::fs::write( directory.join("Open Notebook.onetoc2"), onestore::create_table_of_contents( @@ -103,7 +105,7 @@ fn paragraph_formatting_rejects_invalid_values_and_conflicting_run_settings() { let mut format = text.text.format_at(0).unwrap().clone(); change(&mut format); text.text = Paragraph::new(text.text.text().to_owned(), format); - assert!(PreparedEdit::page(SOURCE, space, &after, "Author").is_err()); + assert!(ops::saved(SOURCE, space, &after).is_err()); } let mut after = before.clone(); let text = after @@ -121,5 +123,5 @@ fn paragraph_formatting_rejects_invalid_values_and_conflicting_run_settings() { left.alignment = Some(1); right.alignment = Some(2); text.text = Paragraph::from_runs([("Left".into(), left), ("Right".into(), right)]); - assert!(PreparedEdit::page(SOURCE, space, &after, "Author").is_err()); + assert!(ops::saved(SOURCE, space, &after).is_err()); } diff --git a/crates/onestore/tests/protected.rs b/crates/onestore/tests/protected.rs index 5f676566a198e5ea31a33d50a6c003d331c5ba09..3abf6caf9a2d75ad1b4f5f0a314ba73ead315bef 100644 --- a/crates/onestore/tests/protected.rs +++ b/crates/onestore/tests/protected.rs @@ -3,6 +3,7 @@ use onestore::{ RevisionIndex, Store, document::{Document, Kind}, + op::{Edit, Op, PageOp}, protected::{Error, Limits, UnlockedSection}, }; use std::{fs, path::Path}; @@ -33,10 +34,19 @@ fn known_passwords_open_independent_native_fixtures() { .iter() .find(|(_, node)| matches!(node.kind, Kind::RichText { .. })) .unwrap(); - assert!( - onestore::PreparedEdit::text(&bytes, space, *object, 0..0, "Must remain protected") - .is_err() - ); + // The ordinary section writer refuses a protected section. + let arena = onestore::Arena::default(); + assert!(onestore::Section::open(&arena, bytes.clone()).is_err()); + let op = PageOp::Text { + text: *object, + range: 0..0, + with: "Must remain protected".into(), + }; + let edit = Edit { + at: 134_000_000_000_000_000, + ops: vec![Op::Page { space, op }], + }; + assert!(unlocked.apply("Rust", &edit).is_ok()); assert!( document .spaces @@ -139,6 +149,7 @@ fn a_protected_page_edit_is_stored_under_the_section_key() { let mut expected = pages(&bytes); let mut edited = 0; for (space, page) in &mut expected { + let before = page.clone(); let paragraphs = page.objects.iter_mut().find_map(|object| match object { PageObject::Outline(outline) if outline.paragraphs.iter().any(|p| p.text().is_some()) => @@ -174,14 +185,21 @@ fn a_protected_page_edit_is_stored_under_the_section_key() { text.text .append(Paragraph::new(" still protected".to_owned(), format)) .unwrap(); - let edit = - onestore::PreparedEdit::page_protected(&bytes, password, *space, page, "Rust") - .unwrap(); + let ops = onestore::op::lower_page(&before, page).unwrap(); + let edit = Edit { + at: 134_000_000_000_000_000, + ops: ops.into_iter().map(|op| Op::Page { space: *space, op }).collect(), + }; + let store = Store::parse(&bytes).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); assert!(matches!( - onestore::PreparedEdit::page_protected(&bytes, "wrong", *space, page, "Rust"), + UnlockedSection::open(&index, "wrong", Limits::default()), Err(Error::PasswordMismatch) )); - let written = edit.as_bytes().to_vec(); + let unlocked = UnlockedSection::open(&index, password, Limits::default()).unwrap(); + let transaction = unlocked.apply("Rust", &edit).unwrap(); + let mut written = bytes.clone(); + transaction.apply(&mut written).unwrap(); for clear in [&b" still protected"[..], b"stays sealed"] { assert!(!written.windows(clear.len()).any(|w| w == clear)); } diff --git a/crates/onestore/tests/shared_snapshot.rs b/crates/onestore/tests/shared_snapshot.rs index ba87504a4d63e614baf6d71eb86f48fc2a361bcf..12cb6c3134a9c09d13806f6800dd2f56be4ec254 100644 --- a/crates/onestore/tests/shared_snapshot.rs +++ b/crates/onestore/tests/shared_snapshot.rs @@ -4,6 +4,8 @@ use current::current; #[path = "support/checkpoint.rs"] mod checkpoint; +#[path = "support/ops.rs"] +mod ops; #[path = "support/trace.rs"] mod trace; @@ -15,6 +17,26 @@ use onestore::{ use std::{fs, io}; use trace::{Event, Trace}; +/// Types `with` over `range` of the text object in `source` and commits it to `io`. +fn commit_text( + io: &mut impl onestore::CommitIo, + source: &[u8], + space: ExGuid, + text: ExGuid, + range: std::ops::Range, + with: &str, +) -> Result<(), onestore::CommitError> { + let op = onestore::op::PageOp::Text { + text, + range, + with: with.into(), + }; + ops::transaction(source, "Author", vec![onestore::op::Op::Page { space, op }]) + .unwrap() + .unwrap() + .commit(io) +} + #[test] fn storage_inspection_preserves_opaque_images_without_claiming_edit_readiness() { for path in [ @@ -111,7 +133,7 @@ fn version_cached_readers_cannot_miss_a_completed_publication() { bytes: source.clone(), events: Vec::new(), }; - onestore::commit_text(&mut trace, &source, sid, oid, end..end, " [cached reader]").unwrap(); + commit_text(&mut trace, &source, sid, oid, end..end, " [cached reader]").unwrap(); let final_content = current(&trace.bytes); let mut visible = source; for event in &trace.events { @@ -168,7 +190,7 @@ fn published_snapshots_survive_interleaved_commit_io() { let mut source = fs::read(format!("../../corpus/{path}")).unwrap(); let (sid, oid, _) = target(&source); if name == "checkpoint" { - source = checkpoint::pending(&source, sid, oid, 0x14001d7a); + source = checkpoint::pending(&source, sid, oid); } let (_, _, end) = target(&source); let before = current(&source); @@ -176,7 +198,7 @@ fn published_snapshots_survive_interleaved_commit_io() { bytes: source.clone(), events: Vec::new(), }; - onestore::commit_text(&mut trace, &source, sid, oid, end..end, " [reader café 🦀]") + commit_text(&mut trace, &source, sid, oid, end..end, " [reader café 🦀]") .unwrap(); let after = current(&trace.bytes); assert_ne!(before, after); @@ -407,7 +429,7 @@ fn unpublished_append_remains_available_for_retry() { bytes: source.clone(), events: Vec::new(), }; - onestore::commit_text(&mut trace, &source, sid, oid, end..end, " abandoned").unwrap(); + commit_text(&mut trace, &source, sid, oid, end..end, " abandoned").unwrap(); let Event::Write(offset, append) = &trace.events[0] else { panic!() }; @@ -432,7 +454,7 @@ fn unpublished_append_remains_available_for_retry() { bytes: persisted, events: Vec::new(), }; - onestore::commit_text(&mut retry, &snapshot, sid, oid, end..end, " retry").unwrap(); + commit_text(&mut retry, &snapshot, sid, oid, end..end, " retry").unwrap(); assert_ne!(current(&retry.bytes), current(&source)); } } diff --git a/crates/onestore/tests/storage.rs b/crates/onestore/tests/storage.rs index 49d0c2040b567c6dadabeac7ede732559a33f1b4..8e49748e40d1ef3b7f4add8b1786ccb7f622c23c 100644 --- a/crates/onestore/tests/storage.rs +++ b/crates/onestore/tests/storage.rs @@ -23,7 +23,7 @@ fn native_file_data_alignment_is_relative_to_the_object() { assert_eq!(payload.len(), 10056); assert_eq!( format!("{:x}", md5::compute(payload)), - "e1b57a8851177dd25dc05b50b904656a" + "fec8f53bd6db9305eabac4cd4b71fb08" ); let blob = store.chunk_data(chunk).unwrap(); for alignment in 0..8 { diff --git a/crates/onestore/tests/support/checkpoint.rs b/crates/onestore/tests/support/checkpoint.rs index 4c048644b3445f3223cc7d0b396ea925597c873b..03ccf9f8e8d770536dfe2cb2ff2ea81d7d50d137 100644 --- a/crates/onestore/tests/support/checkpoint.rs +++ b/crates/onestore/tests/support/checkpoint.rs @@ -1,21 +1,38 @@ -use onestore::{ExGuid, RevisionIndex, Store}; +use onestore::{ + Arena, ExGuid, RevisionIndex, Section, Store, + op::{Edit, Op, PageOp}, +}; -pub fn pending(source: &[u8], sid: ExGuid, oid: ExGuid, property: u32) -> Vec { - let mut source = source.to_vec(); - for value in 1_000_000_u32.. { - let store = Store::parse(&source).unwrap(); +/// `source` with revisions of `space` appended until its chain is 512 deep, so its next +/// revision is a checkpoint. Each retouches the text object `text`, typing a character and +/// removing it again. +pub fn pending(source: &[u8], space: ExGuid, text: ExGuid) -> Vec { + let depth = |image: &[u8]| { + let store = Store::parse(image).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); - let rid = index.spaces[&sid].labels[&(ExGuid::default(), 1)]; - let depth = - std::iter::successors(Some(rid), |id| index.spaces[&sid].revisions[id].dependency) - .count(); - assert!(depth <= 512); - if depth == 512 { - return source; - } - source = - onestore::replace_property_bytes(&source, sid, oid, property, &value.to_le_bytes()) - .unwrap(); + let rid = index.spaces[&space].labels[&(ExGuid::default(), 1)]; + std::iter::successors(Some(rid), |id| { + index.spaces[&space].revisions[id].dependency + }) + .count() + }; + let arena = Arena::default(); + let mut section = Section::open(&arena, source.to_vec()).unwrap(); + let retouch = |range, with: &str| Op::Page { + space, + op: PageOp::Text { + text, + range, + with: with.into(), + }, + }; + for at in 0..512 - depth(source) as u64 { + let ops = vec![retouch(0..0, "x"), retouch(0..1, "")]; + let at = 134_000_000_000_000_000 + at * 10_000_000; + section.apply("Author", &Edit { at, ops }).unwrap(); + section.seal().unwrap().unwrap(); } - unreachable!() + let image = section.image(); + assert_eq!(depth(&image), 512); + image } diff --git a/crates/onestore/tests/support/ops.rs b/crates/onestore/tests/support/ops.rs new file mode 100644 index 0000000000000000000000000000000000000000..6e128ce55bfdcdf92ca0d1b539f700bf071b7cd0 --- /dev/null +++ b/crates/onestore/tests/support/ops.rs @@ -0,0 +1,187 @@ +//! Edits as ops through a `Section`, sealed: the image an edit leaves, for tests that read +//! it back. +#![allow(dead_code)] + +use onestore::{ + Arena, ExGuid, Section, Transaction, + op::{Edit, Op, OpError, PageOp}, + page::Page, +}; + +/// FILETIME now, as an edit's `at`. +pub fn now() -> u64 { + let unix = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap(); + (unix.as_secs() + 11_644_473_600) * 10_000_000 + u64::from(unix.subsec_nanos() / 100) +} + +/// The transaction `ops` seal into on `source`, as one edit by `author`; none when they +/// store nothing. +pub fn transaction( + source: &[u8], + author: &str, + ops: Vec, +) -> Result, OpError> { + let arena = Arena::default(); + let mut section = Section::open(&arena, source.to_vec()).map_err(OpError::Failed)?; + section.apply(author, &Edit { at: now(), ops })?; + section.seal().map_err(OpError::Failed) +} + +/// `source` after `ops`, applied as one edit and sealed. +pub fn edited(source: &[u8], ops: Vec) -> Result, OpError> { + let mut image = source.to_vec(); + if let Some(transaction) = transaction(source, "Author", ops)? { + transaction.apply(&mut image).map_err(OpError::Failed)?; + } + Ok(image) +} + +/// `source` after `ops` on the page in `space`. +pub fn page_edited(source: &[u8], space: ExGuid, ops: Vec) -> Result, OpError> { + edited( + source, + ops.into_iter().map(|op| Op::Page { space, op }).collect(), + ) +} + +/// `source` with the page in `space` taken to `after` by the ops `op::lower_page` finds, +/// as a test states an edit by the page it leaves. +pub fn saved(source: &[u8], space: ExGuid, after: &Page) -> Result, String> { + let arena = Arena::default(); + let section = Section::open(&arena, source.to_vec()).map_err(|error| error.to_string())?; + let before = section.page(space).map_err(|error| error.to_string())?; + let ops = onestore::op::lower_page(&before, after).map_err(|error| error.to_string())?; + page_edited(source, space, ops).map_err(|error| error.to_string()) +} + +/// A plain paragraph holding `text` in OneNote's default text style, with fresh identities. +pub fn paragraph(text: &str) -> onestore::page::PageParagraph { + use onestore::page::{PageParagraph, Paragraph, ParagraphContent, TextObject, text::new_id}; + let format = onestore::document::Format { + font: Some("Calibri".into()), + font_size: Some(11.0), + language: Some(0x409), + ..Default::default() + }; + PageParagraph { + id: new_id().unwrap(), + parent: None, + level: 1, + style: None, + format: Default::default(), + content: ParagraphContent::Text(TextObject { + id: new_id().unwrap(), + date_field: None, + text: Paragraph::new(text.into(), format), + tags: Vec::new(), + }), + lists: Vec::new(), + tags: Vec::new(), + media: Default::default(), + collapsed: false, + } +} + +/// A new outline at `(x, y)` in points holding `paragraphs`. +pub fn outline( + x: f32, + y: f32, + paragraphs: Vec, +) -> onestore::page::PageObject { + use onestore::page::{Outline, PageObject, text::new_id}; + PageObject::Outline(Outline { + id: new_id().unwrap(), + title: false, + min_width: None, + layout: onestore::document::Layout { + x: Some(x), + y: Some(y), + ..Default::default() + }, + indents: Vec::new(), + paragraphs, + unsupported: Vec::new(), + }) +} + +/// An edit applied through a `Section`: the image it leaves and the transaction publishing +/// it, none when it stores nothing. +pub struct Applied { + pub image: Vec, + pub transaction: Option, +} + +impl Applied { + pub fn as_bytes(&self) -> &[u8] { + &self.image + } + + /// Publishes the transaction under the filesystem adapter's exclusion. + #[cfg(any(unix, windows))] + pub fn commit_file( + &self, + path: impl AsRef, + ) -> Result<(), onestore::CommitError> { + self.transaction + .as_ref() + .expect("the edit stores a revision") + .commit_file(path) + } + + /// Publishes the transaction under caller-held exclusion. + pub fn commit(&self, io: &mut impl onestore::CommitIo) -> Result<(), onestore::CommitError> { + self.transaction + .as_ref() + .expect("the edit stores a revision") + .commit(io) + } +} + +/// `ops` applied to `source` as one edit by `author`. +pub fn apply(source: &[u8], author: &str, ops: Vec) -> Result { + let transaction = transaction(source, author, ops)?; + let mut image = source.to_vec(); + if let Some(transaction) = &transaction { + transaction.apply(&mut image).map_err(OpError::Failed)?; + } + Ok(Applied { image, transaction }) +} + +/// `saved` as an `Applied`, to publish it. +pub fn save(source: &[u8], space: ExGuid, after: &Page) -> Result { + let arena = Arena::default(); + let section = Section::open(&arena, source.to_vec()).map_err(|error| error.to_string())?; + let before = section.page(space).map_err(|error| error.to_string())?; + let ops = onestore::op::lower_page(&before, after).map_err(|error| error.to_string())?; + let ops = ops.into_iter().map(|op| Op::Page { space, op }).collect(); + apply(source, "Author", ops).map_err(|error| error.to_string()) +} + +/// `op` on the page in `space`, applied to `source`. +pub fn page_op(source: &[u8], space: ExGuid, op: PageOp) -> Result { + apply(source, "Author", vec![Op::Page { space, op }]) +} + +/// `op` on the section, applied to `source`. +pub fn section_op(source: &[u8], op: onestore::op::SectionOp) -> Result { + apply(source, "Author", vec![Op::Section(op)]) +} + +/// Adds an outline at `(x, y)` holding one paragraph of `text`: the op and the identities +/// of the outline and its text. +pub fn new_outline(x: f32, y: f32, text: &str) -> (PageOp, ExGuid, ExGuid) { + let paragraph = paragraph(text); + let text = paragraph.text().unwrap().id; + let object = outline(x, y, vec![paragraph]); + let id = object.id(); + ( + PageOp::Add { + object, + before: None, + }, + id, + text, + ) +} diff --git a/crates/onestore/tests/support/page_edits.rs b/crates/onestore/tests/support/page_edits.rs index 3db5882ab0acb2d314129d091e815d9b9f97b8ad..d3b6ca0067f4275e3597ed812a96015e374b63eb 100644 --- a/crates/onestore/tests/support/page_edits.rs +++ b/crates/onestore/tests/support/page_edits.rs @@ -1,8 +1,11 @@ -//! Drives random page-model mutations through the page writer and checks its contract: -//! a published model reads back as written, and objects outside the edit keep their bytes. +//! Drives random page-model mutations through `op::lower_page` and a `Section` and checks +//! the contract: the page the section holds is what the model oracle predicts from the ops, +//! the sealed image reads back as the edited model, and objects outside the edit keep their +//! bytes. use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, + Arena, ExGuid, RevisionIndex, Section, Store, + op::{self, Op}, document::{Document, Format, Layout}, page::{ Outline, Page, PageObject, PageParagraph, Paragraph, ParagraphContent, @@ -564,13 +567,31 @@ pub fn run(input: &[u8]) { let Some(mut after) = model(source, space) else { return; }; + let before = after.clone(); mutate(&mut after, &mut bytes); - let Ok(edit) = PreparedEdit::page(source, space, &after, "Fuzz author") else { + let Ok(ops) = onestore::op::lower_page(&before, &after) else { return; }; - let written = edit.as_bytes(); - if written == source.as_slice() { - let before = model(source, space).unwrap(); + let arena = Arena::default(); + let mut section = Section::open(&arena, source.clone()).unwrap(); + let edit = op::Edit { + at: 134_000_000_000_000_000, + ops: ops.iter().map(|op| Op::Page { space, op: op.clone() }).collect(), + }; + if section.apply("Fuzz author", &edit).is_err() { + assert_eq!(section.page(space).unwrap(), before, "a refused edit changes nothing"); + return; + } + let mut predicted = before.clone(); + for op in &ops { + onestore::op::predict(&mut predicted, op).unwrap(); + } + assert_eq!( + projection(§ion.page(space).unwrap()), + projection(&predicted), + "the model oracle" + ); + if section.seal().unwrap().is_none() { assert_eq!( projection(&before), projection(&after), @@ -578,6 +599,7 @@ pub fn run(input: &[u8]) { ); return; } + let written = §ion.image(); current::current(written); let stored = model(written, space).expect("the written page reads back"); let (stored, expected) = (projection(&stored), projection(&after)); diff --git a/crates/onestore/tests/support/page_schedule.rs b/crates/onestore/tests/support/page_schedule.rs index d25a2fa72ac9d86fd4d661d66dcbc159e9ca5d75..71ee9c5729ad21e9665df7009ad01c097746e4d3 100644 --- a/crates/onestore/tests/support/page_schedule.rs +++ b/crates/onestore/tests/support/page_schedule.rs @@ -1,6 +1,7 @@ use onestore::{ - CommitState, ExGuid, Insertion, PageCreation, PageEdit, PreparedEdit, RevisionIndex, Store, + CommitState, ExGuid, PageCreation, PageEdit, RevisionIndex, Store, document::{Document, FieldValue, Kind}, + op::{PageOp, SectionOp}, }; use std::{collections::BTreeMap, sync::LazyLock}; @@ -8,6 +9,8 @@ use std::{collections::BTreeMap, sync::LazyLock}; pub(crate) mod current; #[path = "disk.rs"] pub(crate) mod disk; +#[path = "ops.rs"] +pub(crate) mod ops; static SOURCE: LazyLock> = LazyLock::new(|| { onestore::create_section("pages.one", "Original 🦀 é 東京", "Author").unwrap() @@ -24,17 +27,17 @@ pub fn run(input: &[u8]) { _ => SOURCE.as_slice(), }; if let Ok(intent) = serde_json::from_slice::(input) - && let Ok(prepared) = PreparedEdit::create_page(source, &intent) + && let Ok(prepared) = ops::section_op(source, SectionOp::Create(intent.clone())) { current::current(prepared.as_bytes()); } if let Ok(edits) = serde_json::from_slice::>(input) - && let Ok(prepared) = PreparedEdit::pages(source, &edits) + && let Ok(prepared) = ops::section_op(source, SectionOp::Pages(edits.to_vec())) { current::current(prepared.as_bytes()); } if let Ok(pages) = serde_json::from_slice::>(input) - && let Ok(prepared) = PreparedEdit::delete_pages_permanently(source, &pages) + && let Ok(prepared) = ops::section_op(source, SectionOp::Delete(pages.to_vec())) { current::current(prepared.as_bytes()); } @@ -65,7 +68,7 @@ pub fn run(input: &[u8]) { .get(usize::from(step[2]) % pages.len().max(1)) .copied(); let text = ["", "Same title", "é 🦋 東京", " spaces "][usize::from(step[3]) % 4]; - let existing = if let Some((sid, page)) = selected { + let existing = if let Some((sid, _)) = selected { let space = &document.spaces[&sid]; let view = &space.revisions[&space.contexts[&ExGuid::default()]]; let titles: Vec<_> = view @@ -91,7 +94,7 @@ pub fn run(input: &[u8]) { let removed: Vec<_> = (0..count) .map(|i| pages[(usize::from(step[2]) + i) % pages.len()].0) .collect(); - let prepared = PreparedEdit::delete_pages_permanently(source, &removed).unwrap(); + let prepared = ops::section_op(source, SectionOp::Delete(removed.to_vec())).unwrap(); pages.retain(|page| !removed.contains(&page.0)); for sid in removed { levels.remove(&sid); @@ -128,7 +131,7 @@ pub fn run(input: &[u8]) { let restored: Vec = serde_json::from_value(serde_json::to_value(&edits).unwrap()).unwrap(); assert_eq!(edits, restored); - let prepared = PreparedEdit::pages(source, &restored); + let prepared = ops::section_op(source, SectionOp::Pages(restored.to_vec())); if levels[&pages[0].0] != 1 { assert!(prepared.is_err()); continue; @@ -140,21 +143,15 @@ pub fn run(input: &[u8]) { unreachable!() }; Some(( - PreparedEdit::text( - source, - sid, - object, - 0..u32::try_from(original.encode_utf16().count()).unwrap(), - text, - ) + ops::page_op(source, sid, PageOp::Text { text: object, range: 0..u32::try_from(original.encode_utf16().count()).unwrap(), with: text.into() }) .unwrap(), Some((sid, object, text)), )) } else if step[1] & 16 != 0 { - let intent = Insertion::outline(page, 36.0, 36.0, text, "Page fuzz").unwrap(); + let (add, _, inserted) = ops::new_outline(36.0, 36.0, text); Some(( - PreparedEdit::insert(source, sid, &intent).unwrap(), - Some((sid, intent.text_object(), text)), + ops::page_op(source, sid, add).unwrap(), + Some((sid, inserted, text)), )) } else { None @@ -182,7 +179,7 @@ pub fn run(input: &[u8]) { let intent = PageCreation::new(before, title, "Page fuzz").unwrap(); let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap(); assert_eq!(intent, restored); - let edit = PreparedEdit::create_page(source, &restored).unwrap(); + let edit = ops::section_op(source, SectionOp::Create(restored.clone())).unwrap(); let position = before.map_or(pages.len(), |sid| { pages.iter().position(|p| p.0 == sid).unwrap() }); @@ -257,7 +254,10 @@ pub fn run(input: &[u8]) { write_limit: if step[6] & 1 == 0 { 17 } else { 4096 }, random: u64::from(step[7]) + 1, }; - let result = edit.commit(&mut disk); + let Some(transaction) = &edit.transaction else { + continue; + }; + let result = transaction.commit(&mut disk); let observed = current::current(&disk.durable); match result { Ok(()) => assert_eq!(observed, after), diff --git a/crates/onestore/tests/support/tree_model.rs b/crates/onestore/tests/support/tree_model.rs index 117a41157772e63c51134b0323e30c187d2444bb..9b7680d9ceb309f688d059460db542982a087c68 100644 --- a/crates/onestore/tests/support/tree_model.rs +++ b/crates/onestore/tests/support/tree_model.rs @@ -1,16 +1,18 @@ use onestore::{ - CommitState, ExGuid, Insertion, PreparedEdit, RevisionIndex, Store, TreeEdit, + CommitState, ExGuid, RevisionIndex, Store, document::{Document, Kind, Revision}, + op::{Op, PageOp}, + page::Page, }; use std::{ - collections::{BTreeMap, BTreeSet}, + collections::BTreeMap, sync::LazyLock, }; -use crate::{current, disk}; +use crate::{current, disk, ops}; static SOURCE: LazyLock> = LazyLock::new(|| { - let mut source = onestore::create_section("tree.one", "Original", "Author").unwrap(); + let source = onestore::create_section("tree.one", "Original", "Author").unwrap(); let store = Store::parse(&source).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); @@ -22,26 +24,24 @@ static SOURCE: LazyLock> = LazyLock::new(|| { .iter() .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) .unwrap(); - let other = Insertion::outline(page, 360.0, 72.0, "Other", "Author").unwrap(); - source = PreparedEdit::insert(&source, sid, &other) - .unwrap() - .as_bytes() - .to_vec(); - for parent in [outline, other.object()] { - for text in ["First 🦀 é", "Second 東京", "Third"] { - let insertion = Insertion::paragraph(parent, None, text, "Author").unwrap(); - source = PreparedEdit::insert(&source, sid, &insertion) - .unwrap() - .as_bytes() - .to_vec(); - let child = Insertion::paragraph(insertion.object(), None, "Child", "Author").unwrap(); - source = PreparedEdit::insert(&source, sid, &child) - .unwrap() - .as_bytes() - .to_vec(); + let (add, other, _) = ops::new_outline(360.0, 72.0, "Other"); + let mut edits = vec![add]; + for container in [outline, other] { + let mut paragraphs = Vec::new(); + for text in ["First 🦀 é", "Second 東京", "Third"] { + let paragraph = ops::paragraph(text); + let mut child = ops::paragraph("Child"); + child.parent = Some(paragraph.id); + child.level = 2; + paragraphs.extend([paragraph, child]); } + edits.push(PageOp::Insert { + container, + before: None, + paragraphs, + }); } - source + ops::page_edited(&source, sid, edits).unwrap() }); fn forest(view: &Revision<'_>, root: ExGuid) -> BTreeMap { @@ -69,6 +69,9 @@ fn forest(view: &Revision<'_>, root: ExGuid) -> BTreeMap = before - .keys() - .flat_map(|id| view.nodes[id].children.iter().map(|child| (*child, *id))) - .collect(); - let parent = parents[&target]; + let outline = matches!(view.nodes[&target].kind, Kind::Outline { .. }); let deleting = step[1] & 6 == 0; - let mut expected: BTreeMap<_, _> = before - .iter() - .filter_map(|(id, pos)| { - matches!(view.nodes[id].kind, Kind::Paragraph { .. }) - .then_some((*id, (pos.0, pos.1))) - }) - .collect(); - let mut ordered: Vec<_> = expected.keys().copied().collect(); - ordered.sort_by_key(|id| before[id].2); - let mut expected_order = BTreeMap::<_, Vec<_>>::new(); - for id in ordered { - expected_order.entry(before[&id].0).or_default().push(id); - } - let intent; - let mut reversal = None; - let edit = if deleting { - expected.retain(|id, _| !subtree.contains_key(id)); - for ids in expected_order.values_mut() { - ids.retain(|id| !subtree.contains_key(id)); - } - intent = TreeEdit::delete(target, "Tree schedule").unwrap(); - PreparedEdit::tree(source, sid, &intent) + let (op, cycle) = if deleting { + (PageOp::Delete { object: target }, false) } else { let mut destinations: Vec<_> = before .keys() .copied() .filter(|id| { - if matches!(view.nodes[&target].kind, Kind::Outline { .. }) { + if outline { *id == page } else { matches!( view.nodes[id].kind, - Kind::Outline { .. } - | Kind::OutlineGroup - | Kind::Paragraph { .. } - | Kind::Cell { .. } + Kind::Outline { .. } | Kind::Paragraph { .. } | Kind::Cell { .. } ) } }) .collect(); destinations.sort_by_key(|id| before[id].2); - if destinations.is_empty() { - continue; - } let destination = destinations[usize::from(step[4]) % destinations.len()]; - let children = &view.nodes[&destination].children; - let anchor = children + // Outline groups are the writers' business: anchors are the paragraphs in them. + let mut children = Vec::new(); + let mut pending: Vec = view.nodes[&destination].children.iter().rev().copied().collect(); + while let Some(id) = pending.pop() { + match view.nodes[&id].kind { + Kind::OutlineGroup => pending.extend(view.nodes[&id].children.iter().rev()), + _ if id == target => {} + _ => children.push(id), + } + } + let before = children .get(usize::from(step[5]) % (children.len() + 1)) .copied(); - intent = TreeEdit::move_to(target, destination, anchor, "Tree schedule").unwrap(); - let edit = PreparedEdit::tree(source, sid, &intent); - if subtree.contains_key(&destination) { - assert!(edit.is_err()); - continue; - } - if destination == parent && step[9] & 3 == 0 { - let original = &view.nodes[&parent].children; - let next = original.iter().position(|id| *id == target).unwrap() + 1; - reversal = Some( - TreeEdit::move_to(target, parent, original.get(next).copied(), "Tree schedule") - .unwrap(), - ); - } - if matches!(view.nodes[&target].kind, Kind::Paragraph { .. }) { - let (scope, base, _) = before[&destination]; - let level = base + u32::from(view.nodes[&destination].child_level.unwrap_or(1)); - let (old_scope, old_level, _) = before[&target]; - if anchor != Some(target) { - let moved: Vec<_> = expected_order[&old_scope] - .iter() - .copied() - .filter(|id| subtree.contains_key(id)) - .collect(); - expected_order - .get_mut(&old_scope) - .unwrap() - .retain(|id| !subtree.contains_key(id)); - let boundary = anchor.map(|id| before[&id].2).unwrap_or_else(|| { - forest(view, destination) - .keys() - .map(|id| before[id].2) - .max() - .unwrap() - + 1 - }); - let sequence = expected_order.entry(scope).or_default(); - let position = sequence - .iter() - .position(|id| before[id].2 >= boundary) - .unwrap_or(sequence.len()); - sequence.splice(position..position, moved); - } - for (id, pos) in &mut expected { - if subtree.contains_key(id) && pos.0 == old_scope { - *pos = (scope, level + pos.1 - old_level); - } - } - } - edit + let op = PageOp::Move { + object: target, + parent: (!outline).then_some(destination), + before, + }; + (op, subtree.contains_key(&destination)) }; - let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap(); - assert_eq!(intent, restored); - let edit = match edit { - Ok(edit) => edit, - Err(error) - if error.message - == "Removing this group would exceed 31 child indentation levels" => - { - continue; - } - Err(error) => panic!("{intent:?}: {error:?}"), + let model = Page::from_space(&document, sid).unwrap(); + let ops = vec![Op::Page { space: sid, op: op.clone() }]; + let Ok(edit) = ops::apply(source, "Tree schedule", ops) else { + continue; }; + assert!(!cycle, "{op:?}"); let after_store = Store::parse(edit.as_bytes()).unwrap(); let after_index = RevisionIndex::parse(&after_store).unwrap(); after_index.validate_current().unwrap(); let after_document = Document::parse(&after_index).unwrap(); + let mut predicted = model.clone(); + onestore::op::predict(&mut predicted, &op).unwrap(); + let stored = Page::from_space(&after_document, sid).unwrap(); + if stored != predicted { + let (a, b) = (format!("{stored:?}"), format!("{predicted:?}")); + let at = a.bytes().zip(b.bytes()).take_while(|(x, y)| x == y).count(); + panic!("{op:?}\n stored …{}\n predicted …{}", &a[a.floor_char_boundary(at.saturating_sub(300))..a.floor_char_boundary((at + 200).min(a.len()))], &b[b.floor_char_boundary(at.saturating_sub(300))..b.floor_char_boundary((at + 200).min(b.len()))]); + } let space = &after_document.spaces[&sid]; let after = &space.revisions[&space.contexts[&ExGuid::default()]]; - let actual = forest(after, page); - let mut added = BTreeSet::new(); - for (id, pos) in &actual { - let node = &after.nodes[id]; - if let Kind::Paragraph { .. } = node.kind { - if let Some(wanted) = expected.remove(id) { - assert_eq!((pos.0, pos.1), wanted, "{intent:?}: {id}"); - } else { - assert!(!before.contains_key(id)); - assert!(matches!(view.nodes[&parent].kind, Kind::Cell { .. })); - assert_eq!(view.nodes[&parent].children, [target]); - assert_eq!(node.children.len(), 0); - assert_eq!(node.content.len(), 1); - assert!(node.tags.is_empty()); - assert!( - matches!(&after.nodes[&node.content[0]].kind,Kind::RichText{text,..} if text.is_empty()) - ); - added.insert(*id); - } - } - if before.contains_key(id) { - assert_eq!(node.content, view.nodes[id].content); - let mut expected_kind = serde_json::to_value(&view.nodes[id].kind).unwrap(); - if *id == page && expected_kind["alternate_title"].is_string() { - let mut roots = after.nodes[&page].children.clone(); - roots.sort_by(|a, b| { - let a = &after.nodes[a].layout; - let b = &after.nodes[b].layout; - a.y.unwrap_or(0.0) - .total_cmp(&b.y.unwrap_or(0.0)) - .then_with(|| a.x.unwrap_or(0.0).total_cmp(&b.x.unwrap_or(0.0))) - }); - let mut title = String::new(); - for root in roots { - let mut nodes: Vec<_> = forest(after, root).into_iter().collect(); - nodes.sort_by_key(|(_, pos)| pos.2); - for (id, _) in nodes { - if let Kind::RichText { text, .. } = &after.nodes[&id].kind { - title = text - .trim_start() - .split('\r') - .next() - .unwrap() - .trim_end() - .to_owned(); - if !title.is_empty() { - break; - } - } - } - if !title.is_empty() { - break; - } - } - expected_kind["alternate_title"] = title.into(); - let Kind::Metadata { title, .. } = &after.nodes[&after.roots[&2]].kind else { - panic!() - }; - assert_eq!(title.as_deref(), expected_kind["alternate_title"].as_str()); - } - assert_eq!(serde_json::to_value(&node.kind).unwrap(), expected_kind); + for (id, _) in forest(after, page) { + let node = &after.nodes[&id]; + if before.contains_key(&id) { + assert_eq!(node.content, view.nodes[&id].content); assert_eq!( serde_json::to_value(&node.tags).unwrap(), - serde_json::to_value(&view.nodes[id].tags).unwrap() + serde_json::to_value(&view.nodes[&id].tags).unwrap() ); } if matches!( @@ -311,31 +194,6 @@ pub fn run(input: &[u8]) { ); } } - assert!(expected.is_empty()); - assert!(added.len() <= 1); - let mut ordered: Vec<_> = actual - .iter() - .filter(|(id, _)| { - matches!(after.nodes[id].kind, Kind::Paragraph { .. }) && !added.contains(id) - }) - .collect(); - ordered.sort_by_key(|(_, pos)| pos.2); - let mut actual_order = BTreeMap::<_, Vec<_>>::new(); - for (id, pos) in ordered { - actual_order.entry(pos.0).or_default().push(*id); - } - expected_order.retain(|_, ids| !ids.is_empty()); - assert_eq!(actual_order, expected_order, "{intent:?}"); - if let Some(reversal) = reversal { - let reversed = PreparedEdit::tree(edit.as_bytes(), sid, &reversal).unwrap(); - let replayed = PreparedEdit::tree(reversed.as_bytes(), sid, &intent).unwrap(); - let store = Store::parse(replayed.as_bytes()).unwrap(); - let index = RevisionIndex::parse(&store).unwrap(); - let document = Document::parse(&index).unwrap(); - let space = &document.spaces[&sid]; - let replayed = &space.revisions[&space.contexts[&ExGuid::default()]]; - assert_eq!(forest(replayed, page), actual); - } let before = current::current(&persisted); let after = current::current(edit.as_bytes()); let mut disk = disk::Disk { @@ -346,7 +204,10 @@ pub fn run(input: &[u8]) { write_limit: if step[8] & 1 == 0 { 17 } else { 4096 }, random: u64::from(step[9]) + 1, }; - let result = edit.commit(&mut disk); + let Some(transaction) = &edit.transaction else { + continue; + }; + let result = transaction.commit(&mut disk); let actual = current::current(&disk.durable); match result { Ok(()) => assert_eq!(actual, after), diff --git a/crates/onestore/tests/table_growth.rs b/crates/onestore/tests/table_growth.rs index d57186f5aab72dea0d3a5a1308d49f9dae067450..6f61607ee69f62514c697bc1037e1a67acb56c03 100644 --- a/crates/onestore/tests/table_growth.rs +++ b/crates/onestore/tests/table_growth.rs @@ -1,8 +1,11 @@ //! A rewritten object stores the global id table entries it names, not the table of the //! revision it was read from. +#[path = "support/ops.rs"] +mod ops; + use onestore::{ - PreparedEdit, RevisionIndex, Store, + RevisionIndex, Store, document::Document, page::{Page, PageObject, Paragraph}, }; @@ -64,17 +67,17 @@ fn a_text_edit_stores_the_table_entries_it_names() { text.text .append(Paragraph::new(" and a few more words".to_owned(), format)) .unwrap(); - let written = PreparedEdit::page(&bytes, space, &page, "Rust") + let written = ops::saved(&bytes, space, &page) .unwrap() - .as_bytes() + .as_slice() .to_vec(); for (entries, span) in tables(&written, bytes.len()) { sparse |= (entries as u32) < span; } assert_eq!( - PreparedEdit::page(&written, space, &page, "Rust") + ops::saved(&written, space, &page) .unwrap() - .as_bytes(), + .as_slice(), written ); bytes = written; diff --git a/crates/onestore/tests/toc.rs b/crates/onestore/tests/toc.rs index 7a71d89380e88a3faf41b338025b848435ece205..6a6e2f3b8fcb9db2304458f6177b3ce972fc3b14 100644 --- a/crates/onestore/tests/toc.rs +++ b/crates/onestore/tests/toc.rs @@ -1,4 +1,25 @@ -use onestore::{PreparedEdit, RevisionIndex, Store, TocEdit, document::Document}; +use onestore::{RevisionIndex, Store, TocEdit, document::Document}; + +/// `source` with `edits` applied. +fn edited(source: &[u8], edits: &[TocEdit]) -> Result, onestore::Error> { + let mut image = source.to_vec(); + if let Some(transaction) = onestore::edit_table_of_contents(source, edits)? { + transaction.apply(&mut image)?; + } + Ok(image) +} + +/// The notebook's colour. +fn color(bytes: &[u8]) -> Option { + let store = Store::parse(bytes).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let revision = document.active(document.root).unwrap(); + match &revision.nodes[&revision.roots[&1]].kind { + onestore::document::Kind::Toc { color, .. } => *color, + _ => panic!(), + } +} fn entries(bytes: &[u8]) -> Vec<(String, [u8; 16], u32, Option)> { let store = Store::parse(bytes).unwrap(); @@ -31,7 +52,7 @@ fn entries(bytes: &[u8]) -> Vec<(String, [u8; 16], u32, Option)> { } #[test] -fn sections_and_groups_are_added_renamed_coloured_ordered_and_removed() { +fn sections_and_groups_are_added_renamed_ordered_and_removed_and_the_notebook_coloured() { let a = [1; 16]; let b = [2; 16]; let toc = onestore::create_table_of_contents( @@ -41,7 +62,7 @@ fn sections_and_groups_are_added_renamed_coloured_ordered_and_removed() { .unwrap(); let c = [3; 16]; let group = [4; 16]; - let written = PreparedEdit::table_of_contents( + let written = edited( &toc, &[ TocEdit::Add { @@ -58,7 +79,7 @@ fn sections_and_groups_are_added_renamed_coloured_ordered_and_removed() { ) .unwrap(); assert_eq!( - entries(written.as_bytes()), + entries(&written), [ ("Alpha.one".to_owned(), a, 1, Some(0xffff_ffff)), ("Beta.one".to_owned(), b, 2, Some(0xffff_ffff)), @@ -66,33 +87,31 @@ fn sections_and_groups_are_added_renamed_coloured_ordered_and_removed() { ("Archive".to_owned(), group, 4, None), ] ); - let again = PreparedEdit::table_of_contents( - written.as_bytes(), + let again = edited( + &written, &[ TocEdit::Rename { identity: b, filename: "Renamed.one".into(), }, - TocEdit::Color { - identity: c, - color: Some(0x00d7ff), - }, + TocEdit::Color(0x00d7ff), TocEdit::Order(vec![group, c]), TocEdit::Remove { identity: a }, ], ) .unwrap(); assert_eq!( - entries(again.as_bytes()), + entries(&again), [ ("Archive".to_owned(), group, 1, None), - ("Gamma.one".to_owned(), c, 2, Some(0x00d7ff)), + ("Gamma.one".to_owned(), c, 2, Some(0xffff_ffff)), ("Renamed.one".to_owned(), b, 3, Some(0xffff_ffff)), ] ); + assert_eq!(color(&again), Some(0x00d7ff)); assert!( - PreparedEdit::table_of_contents( - again.as_bytes(), + edited( + &again, &[TocEdit::Add { filename: "renamed.one".into(), identity: [9; 16], @@ -101,8 +120,5 @@ fn sections_and_groups_are_added_renamed_coloured_ordered_and_removed() { ) .is_err() ); - assert!( - PreparedEdit::table_of_contents(again.as_bytes(), &[TocEdit::Remove { identity: a }]) - .is_err() - ); + assert!(edited(&again, &[TocEdit::Remove { identity: a }]).is_err()); } diff --git a/crates/onestore/tests/transaction.rs b/crates/onestore/tests/transaction.rs index 72108fe5cc11db75ac0e9b6c645f4693d9b16fde..14671a5bb4f3a73e18c5faba354810cd3beffa24 100644 --- a/crates/onestore/tests/transaction.rs +++ b/crates/onestore/tests/transaction.rs @@ -1,6 +1,10 @@ +#[path = "support/ops.rs"] +mod ops; + use onestore::{ - CommitIo, CommitState, ExGuid, PreparedEdit, RevisionIndex, Stamp, Store, + CommitIo, CommitState, ExGuid, RevisionIndex, Stamp, Store, Transaction, document::{Document, Kind}, + op::{Op, PageOp}, }; use std::io; @@ -23,6 +27,21 @@ fn text(source: &[u8]) -> (ExGuid, ExGuid) { panic!("Missing text fixture") } +/// The transaction typing `with` at the start of the text object, and the image it leaves. +fn typed(space: ExGuid, text: ExGuid, with: &str) -> (Transaction, Vec) { + let op = PageOp::Text { + text, + range: 0..0, + with: with.into(), + }; + let transaction = ops::transaction(SOURCE, "Author", vec![Op::Page { space, op }]) + .unwrap() + .unwrap(); + let mut image = SOURCE.to_vec(); + transaction.apply(&mut image).unwrap(); + (transaction, image) +} + /// A file that records how many bytes a commit reads and writes. struct Counted { bytes: Vec, @@ -54,20 +73,19 @@ impl CommitIo for Counted { #[test] fn a_transaction_reads_the_header_and_writes_only_its_edit() { let (sid, oid) = text(SOURCE); - let edit = PreparedEdit::text(SOURCE, sid, oid, 0..0, "é").unwrap(); - let transaction = edit.transaction(); + let (transaction, written) = typed(sid, oid, "é"); let mut image = SOURCE.to_vec(); transaction.apply(&mut image).unwrap(); - assert_eq!(image, edit.as_bytes()); + assert_eq!(image, written); let mut file = Counted { bytes: SOURCE.to_vec(), read: 0, written: 0, }; transaction.commit(&mut file).unwrap(); - assert_eq!(file.bytes, edit.as_bytes()); + assert_eq!(file.bytes, written); assert!(file.read <= 1024 + 2, "read {} bytes", file.read); - let appended = edit.as_bytes().len() - SOURCE.len(); + let appended = written.len() - SOURCE.len(); assert!( file.written < appended + 2048, "wrote {} bytes for {appended} appended", @@ -86,9 +104,9 @@ fn a_transaction_reads_the_header_and_writes_only_its_edit() { #[test] fn stamps_name_the_committed_image() { let (sid, oid) = text(SOURCE); - let edit = PreparedEdit::text(SOURCE, sid, oid, 0..0, "x").unwrap(); + let (transaction, written) = typed(sid, oid, "x"); let before = Stamp::of(SOURCE).unwrap(); - let after = Stamp::of(edit.as_bytes()).unwrap(); + let after = Stamp::of(&written).unwrap(); assert_ne!(before, after); assert_eq!(before.length, SOURCE.len() as u64); // An unpublished tail changes the length, so a commit built without it is refused. @@ -100,7 +118,7 @@ fn stamps_name_the_committed_image() { read: 0, written: 0, }; - let error = edit.commit(&mut file).unwrap_err(); + let error = transaction.commit(&mut file).unwrap_err(); assert_eq!(error.error.kind(), io::ErrorKind::ResourceBusy); assert_eq!(file.written, 0); assert!(Stamp::of(&SOURCE[..1023]).is_err()); diff --git a/crates/onestore/tests/tree.rs b/crates/onestore/tests/tree.rs index 13d30afbc15613b51832d82b13f66bddeca68376..1041f367712c9b3cb17fddd6000abe3a4bb54905 100644 --- a/crates/onestore/tests/tree.rs +++ b/crates/onestore/tests/tree.rs @@ -1,6 +1,7 @@ use onestore::{ - ExGuid, PreparedEdit, RevisionIndex, Store, TreeEdit, + ExGuid, RevisionIndex, Store, document::{Document, Kind, Revision}, + op::{Op, PageOp}, }; use std::collections::{BTreeMap, BTreeSet}; @@ -8,6 +9,8 @@ use std::collections::{BTreeMap, BTreeSet}; mod current; #[path = "support/disk.rs"] mod disk; +#[path = "support/ops.rs"] +mod ops; #[path = "support/tree_model.rs"] mod tree_model; @@ -55,7 +58,35 @@ fn active(view: &Revision<'_>, page: ExGuid) -> BTreeSet { ids } -fn operation(view: &Revision<'_>, page: ExGuid) -> Option<(String, ExGuid, TreeEdit)> { +/// The container `id` lies in, outline groups passed over. +fn container(view: &Revision<'_>, ids: &BTreeSet, id: ExGuid) -> ExGuid { + let mut at = *ids + .iter() + .find(|parent| view.nodes[parent].children.contains(&id)) + .unwrap(); + while matches!(view.nodes[&at].kind, Kind::OutlineGroup) { + at = container(view, ids, at); + } + at +} + +/// The outline level of each paragraph in `outline`. +fn levels(view: &Revision<'_>, outline: ExGuid) -> BTreeMap { + let mut levels = BTreeMap::new(); + let mut pending = vec![(outline, 0)]; + while let Some((id, level)) = pending.pop() { + let node = &view.nodes[&id]; + if matches!(node.kind, Kind::Paragraph { .. }) { + levels.insert(id, level); + } + let child = level + u32::from(node.child_level.unwrap_or(1)); + pending.extend(node.children.iter().map(|id| (*id, child))); + } + levels +} + +/// The named edit of a fixture page as ops, with the object it acts on. +fn operation(view: &Revision<'_>, page: ExGuid) -> Option<(String, ExGuid, Vec)> { let Kind::Metadata { title: Some(name), .. } = &view.nodes[&view.roots[&2]].kind @@ -85,32 +116,46 @@ fn operation(view: &Revision<'_>, page: ExGuid) -> Option<(String, ExGuid, TreeE .iter() .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) .unwrap(); - let parent = *ids - .iter() - .find(|id| view.nodes[id].children.contains(&target)) - .unwrap(); + let parent = container(view, &ids, target); + let move_to = |parent, before| PageOp::Move { + object: target, + parent: Some(parent), + before, + }; let edit = match name.as_str() { - "Delete outline" => TreeEdit::delete(outline, "Tree author"), - name if name.starts_with("Delete") => TreeEdit::delete(target, "Tree author"), + "Delete outline" => vec![PageOp::Delete { object: outline }], + "Delete sole cell paragraph" => vec![ + PageOp::Insert { + container: parent, + before: None, + paragraphs: vec![ops::paragraph("")], + }, + PageOp::Delete { object: target }, + ], + name if name.starts_with("Delete") => vec![PageOp::Delete { object: target }], "Move leaf down" | "Move subtree down" | "Move numbered subtree down" - | "Move cell subtree down" => TreeEdit::move_to(target, parent, None, "Tree author"), - "Move subtree up" => { - TreeEdit::move_to(target, parent, Some(paragraphs["Anchor"]), "Tree author") + | "Move cell subtree down" => vec![move_to(parent, None)], + "Move subtree up" => vec![move_to(parent, Some(paragraphs["Anchor"]))], + "Indent subtree" | "Indent bullet subtree" => vec![move_to(paragraphs["Anchor"], None)], + "Outdent subtree" | "Outdent first group" => { + // Levels are absolute: the subtree moves up to the outline's first level. + let levels = levels(view, outline); + let shift = levels[&target] - 1; + let mut ops = vec![move_to(outline, Some(paragraphs["Trailing sibling"]))]; + let mut pending = vec![target]; + while let Some(paragraph) = pending.pop() { + ops.push(PageOp::Level { + paragraph, + level: levels[¶graph] - shift, + }); + pending.extend(view.nodes[¶graph].children.iter().rev()); + } + ops } - "Indent subtree" | "Indent bullet subtree" => { - TreeEdit::move_to(target, paragraphs["Anchor"], None, "Tree author") - } - "Outdent subtree" | "Outdent first group" => TreeEdit::move_to( - target, - outline, - Some(paragraphs["Trailing sibling"]), - "Tree author", - ), _ => return None, - } - .unwrap(); + }; Some(( name.clone(), if name == "Delete outline" { @@ -139,9 +184,9 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { let Some((name, object, edit)) = operation(before, page) else { continue; }; - let restored = serde_json::from_value(serde_json::to_value(&edit).unwrap()).unwrap(); - assert_eq!(edit, restored); - let prepared = PreparedEdit::tree(source, sid, &restored).unwrap(); + let edit: Vec = edit.into_iter().map(|op| Op::Page { space: sid, op }).collect(); + let prepared = ops::apply(source, "Tree author", edit.clone()) + .unwrap_or_else(|error| panic!("{name}: {error}")); let after_store = Store::parse(prepared.as_bytes()).unwrap(); assert!(after_store.checksum_mismatches.is_empty()); let after_index = RevisionIndex::parse(&after_store).unwrap(); @@ -267,11 +312,19 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { serde_json::to_value(&old.tags).unwrap(), "{name}: tags {id}" ); - } else if matches!(node.kind, Kind::RichText { .. }) { - assert_eq!( - serde_json::to_value(&node.kind).unwrap(), - serde_json::to_value(&wanted.kind).unwrap() - ); + } else if let ( + Kind::RichText { + text, boilerplate, .. + }, + Kind::RichText { + text: native, + boilerplate: native_boilerplate, + .. + }, + ) = (&node.kind, &wanted.kind) + { + // A new run carries the language MS-ONE requires, which OneNote omits. + assert_eq!((text, boilerplate), (native, native_boilerplate)); } } for (sid, space) in &index.spaces { @@ -284,7 +337,7 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { } } } - let another = PreparedEdit::tree(source, sid, &edit).unwrap(); + let another = ops::apply(source, "Tree author", edit.clone()).unwrap(); let second_store = Store::parse(another.as_bytes()).unwrap(); let second_index = RevisionIndex::parse(&second_store).unwrap(); let second_doc = Document::parse(&second_index).unwrap(); @@ -293,10 +346,7 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { actual, active(&space.revisions[&space.contexts[&ExGuid::default()]], page) ); - candidate = PreparedEdit::tree(&candidate, sid, &edit) - .unwrap() - .as_bytes() - .to_vec(); + candidate = ops::apply(&candidate, "Tree author", edit).unwrap().image; count += 1; } if let Some(output) = std::env::var_os("ONESTORE_TREE_OUTPUT") { @@ -329,22 +379,24 @@ fn moves_reject_cycles_wrong_parents_and_stale_siblings() { .unwrap(); let paragraph = view.nodes[&outline].children[0]; let text = view.nodes[¶graph].content[0]; - for intent in [ - TreeEdit::delete(page, "Author"), - TreeEdit::delete(text, "Author"), - TreeEdit::move_to(outline, paragraph, None, "Author"), - TreeEdit::move_to(paragraph, paragraph, None, "Author"), - TreeEdit::move_to(paragraph, text, None, "Author"), - TreeEdit::move_to(paragraph, outline, Some(page), "Author"), + let move_to = |object, parent, before| PageOp::Move { + object, + parent: Some(parent), + before, + }; + for op in [ + PageOp::Delete { object: page }, + PageOp::Delete { object: text }, + move_to(outline, paragraph, None), + move_to(paragraph, paragraph, None), + move_to(paragraph, text, None), + move_to(paragraph, outline, Some(page)), ] { - assert!(PreparedEdit::tree(&source, sid, &intent.unwrap()).is_err()); + assert!(ops::page_op(&source, sid, op).is_err()); } for before in [None, Some(paragraph)] { - let edit = TreeEdit::move_to(paragraph, outline, before, "Author").unwrap(); - assert_eq!( - PreparedEdit::tree(&source, sid, &edit).unwrap().as_bytes(), - source - ); + let op = move_to(paragraph, outline, before); + assert_eq!(ops::page_op(&source, sid, op).unwrap().as_bytes(), source); } } @@ -370,7 +422,8 @@ fn publication_interruptions_expose_complete_old_or_new_trees() { ) { continue; } - let edit = PreparedEdit::tree(source, sid, &intent).unwrap(); + let intent = intent.into_iter().map(|op| Op::Page { space: sid, op }).collect(); + let edit = ops::apply(source, "Tree author", intent).unwrap(); let after = current::current(edit.as_bytes()); for write_limit in [17, 4096] { let disk = |fail_at| disk::Disk { @@ -453,8 +506,24 @@ fn cross_container_moves_keep_tables_and_replace_emptied_cells() { "Move cell subtree down" => (target, outlines[1]), _ => (view.nodes[&outlines[0]].children[0], outlines[1]), }; - let intent = TreeEdit::move_to(object, destination, None, "Tree author").unwrap(); - let edit = PreparedEdit::tree(source, sid, &intent).unwrap(); + let mut intent = vec![Op::Page { + space: sid, + op: PageOp::Move { + object, + parent: Some(destination), + before: None, + }, + }]; + // A cell keeps a paragraph: its replacement goes in first. + if name == "Delete sole cell paragraph" { + let op = PageOp::Insert { + container: parents[&target], + before: None, + paragraphs: vec![ops::paragraph("")], + }; + intent.insert(0, Op::Page { space: sid, op }); + } + let edit = ops::apply(source, "Tree author", intent.clone()).unwrap(); let store = Store::parse(edit.as_bytes()).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); let document = Document::parse(&index).unwrap(); @@ -503,10 +572,7 @@ fn cross_container_moves_keep_tables_and_replace_emptied_cells() { "Delete cell subtree" => assert!(!actual.contains(&outlines[0])), _ => assert_eq!(after.nodes[&parents[&target]].children.len(), 1), } - candidate = PreparedEdit::tree(&candidate, sid, &intent) - .unwrap() - .as_bytes() - .to_vec(); + candidate = ops::apply(&candidate, "Tree author", intent).unwrap().image; count += 1; } assert_eq!(count, 4); diff --git a/crates/onestore/tests/writer.rs b/crates/onestore/tests/writer.rs index 72f7619da77b191bf78333e2e0e836fc09aa15f4..b515b41859e354db80e52b00cd52eb13f01ba21e 100644 --- a/crates/onestore/tests/writer.rs +++ b/crates/onestore/tests/writer.rs @@ -1,39 +1,63 @@ +#[path = "support/ops.rs"] +mod ops; + use onestore::{ - ExGuid, ObjectData, PropertySets, RevisionIndex, Store, Value, replace_property_bytes, + ExGuid, ObjectData, PropertySets, RevisionIndex, Store, TocEdit, Value, + document::{Document, Kind}, + op::{Edit, Op, PageOp}, }; use std::fs; +/// The text object holding `text` in `source` and its page space. +fn text_object(source: &[u8], text: &str) -> (ExGuid, ExGuid) { + let store = Store::parse(source).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let mut found = document.spaces.iter().flat_map(|(space, view)| { + view.active() + .unwrap() + .nodes + .iter() + .filter(|(_, node)| matches!(&node.kind, Kind::RichText { text: stored, .. } if stored == text)) + .map(|(id, _)| (*space, *id)) + .collect::>() + }); + let target = found.next().unwrap(); + assert!(found.next().is_none()); + target +} + +/// `source`, a table of contents, with the notebook recoloured. +fn recolored(source: &[u8], color: u32) -> Vec { + let mut image = source.to_vec(); + let edit = TocEdit::Color(color); + if let Some(transaction) = onestore::edit_table_of_contents(source, &[edit]).unwrap() { + transaction.apply(&mut image).unwrap(); + } + image +} + #[test] -fn scalar_edit_appends_a_revision_and_preserves_every_prior_object() { +fn text_edit_appends_a_revision_and_preserves_every_prior_object() { let source = fs::read("../../corpus/native/20260905-05/snapshots/02-text/notebook/synthetic.one") .unwrap(); let store = Store::parse(&source).unwrap(); let index = RevisionIndex::parse(&store).unwrap(); - let mut selected = None; - for (osid, space) in &index.spaces { - let revision = index - .resolve(*osid, space.labels[&(ExGuid::default(), 1)]) - .unwrap(); - for oid in revision.reachable().unwrap() { - let object = &revision.objects[&oid]; - if let ObjectData::Properties(bytes) = object.data { - for property in &PropertySets::parse(bytes).unwrap().sets[0] { - if property.value == Value::Bytes(b"Fictitious plain text.") { - assert!(selected.replace((*osid, oid, property.id)).is_none()); - } - } - } - } - } - let (osid, oid, property) = selected.unwrap(); - assert_eq!( - replace_property_bytes(&source, osid, oid, property, b"Fictitious plain text.").unwrap(), - source - ); + let (osid, oid) = text_object(&source, "Fictitious plain text."); + let whole = 0.."Fictitious plain text.".encode_utf16().count() as u32; + let typed = |with: &str| { + let op = PageOp::Text { + text: oid, + range: whole.clone(), + with: with.into(), + }; + ops::page_edited(&source, osid, vec![op]).unwrap() + }; + assert_eq!(typed("Fictitious plain text."), source); for length in 0..=40 { - let value = vec![b'x'; length]; - let written = replace_property_bytes(&source, osid, oid, property, &value).unwrap(); + let value = "x".repeat(length); + let written = typed(&value); assert_preserved_committed_bytes(&source, &written); let parsed = Store::parse(&written).unwrap(); assert!(parsed.checksum_mismatches.is_empty()); @@ -64,22 +88,11 @@ fn scalar_edit_appends_a_revision_and_preserves_every_prior_object() { } } } - let space = &after.spaces[&osid]; - let active = after - .resolve(osid, space.labels[&(ExGuid::default(), 1)]) - .unwrap(); - let ObjectData::Properties(data) = active.objects[&oid].data else { + let document = Document::parse(&after).unwrap(); + let Kind::RichText { text, .. } = &document.active(osid).unwrap().nodes[&oid].kind else { panic!() }; - let properties = PropertySets::parse(data).unwrap(); - assert_eq!( - properties.sets[0] - .iter() - .find(|candidate| candidate.id == property) - .unwrap() - .value, - Value::Bytes(&value) - ); + assert_eq!(*text, value); } } @@ -93,26 +106,33 @@ fn toc_color_edit_uses_its_native_revision_encoding_and_crc() { let index = RevisionIndex::parse(&store).unwrap(); let rid = index.spaces[&index.root].labels[&(ExGuid::default(), 1)]; let revision = index.resolve(index.root, rid).unwrap(); - let oid = revision.roots[&1]; - let written = - replace_property_bytes(&source, index.root, oid, 0x14001cbe, &[0x33, 0x66, 0x99, 0]) - .unwrap(); + let written = recolored(&source, 0x996633); let after = Store::parse(&written).unwrap(); assert!(after.checksum_mismatches.is_empty()); assert_preserved_committed_bytes(&source, &written); let current = RevisionIndex::parse(&after).unwrap(); current.validate_current().unwrap(); - assert_eq!( - current.resolve(index.root, rid).unwrap().objects[&oid].data, - revision.objects[&oid].data - ); let active = current .resolve( index.root, current.spaces[&index.root].labels[&(ExGuid::default(), 1)], ) .unwrap(); - let ObjectData::Properties(blob) = active.objects[&oid].data else { + let recoloured: Vec<_> = active + .objects + .iter() + .filter(|(id, object)| { + revision + .objects + .get(id) + .is_none_or(|old| old.data != object.data) + }) + .map(|(_, object)| object) + .collect(); + let [entry] = recoloured[..] else { + panic!("{} objects changed", recoloured.len()) + }; + let ObjectData::Properties(blob) = entry.data else { panic!() }; assert!( @@ -302,13 +322,8 @@ fn toc_writes_cross_fragment_and_counter_boundaries_without_crc_drift() { let store = Store::parse(&bytes).unwrap(); assert!(store.checksum_mismatches.is_empty(), "edit {i}"); let index = RevisionIndex::parse(&store).unwrap(); - let root = index.root; - let revision = index - .resolve(root, index.spaces[&root].labels[&(ExGuid::default(), 1)]) - .unwrap(); - let oid = revision.roots[&1]; - let written = - replace_property_bytes(&bytes, root, oid, 0x14001cbe, &i.to_le_bytes()).unwrap(); + index.validate_current().unwrap(); + let written = recolored(&bytes, i); assert_preserved_committed_bytes(&bytes, &written); bytes = written; } @@ -332,31 +347,31 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { let mut bytes = fs::read(path).unwrap(); let initial = Store::parse(&bytes).unwrap(); let index = RevisionIndex::parse(&initial).unwrap(); + // A section retypes text objects of one page; a table of contents recolours the notebook. + let document = Document::parse(&index).unwrap(); let mut candidates = Vec::new(); - for (sid, space) in &index.spaces { - let revision = index - .resolve(*sid, space.labels[&(ExGuid::default(), 1)]) - .unwrap(); - for oid in revision.reachable().unwrap() { - let object = &revision.objects[&oid]; - if object.jcid != if section { 0x6000e } else { 0x20001 } { - continue; - } - let ObjectData::Properties(blob) = object.data else { - panic!() - }; - let property = if section { 0x14001d7a } else { 0x14001cbe }; - if PropertySets::parse(blob).unwrap().sets[0] - .iter() - .any(|p| p.id == property) - { - candidates.push((*sid, oid, property)); + for (sid, view) in &document.spaces { + for (oid, node) in &view.active().unwrap().nodes { + match &node.kind { + Kind::RichText { + text, + boilerplate: false, + .. + } if section && !text.is_empty() => { + candidates.push((*sid, *oid)); + } + Kind::Toc { entries, .. } if !section && !entries.is_empty() => { + candidates.push((*sid, *oid)); + } + _ => {} } } } assert!(!candidates.is_empty()); let sid = candidates[0].0; candidates.retain(|candidate| candidate.0 == sid); + // What each edit rewrites: modification times, or the notebook's colour. + let property = if section { 0x14001d7a } else { 0x14001cbe }; if section { let revision = index .resolve(sid, index.spaces[&sid].labels[&(ExGuid::default(), 1)]) @@ -371,12 +386,30 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { } let mut checkpoints = 0; for operation in 0_u32..1025 { - let (_, oid, property) = candidates[operation as usize % candidates.len()]; + let (_, oid) = candidates[operation as usize % candidates.len()]; let store = Store::parse(&bytes).unwrap(); let before = RevisionIndex::parse(&store).unwrap(); let rid = before.spaces[&sid].labels[&(ExGuid::default(), 1)]; - let value = (1_000_000 + operation).to_le_bytes(); - let written = replace_property_bytes(&bytes, sid, oid, property, &value).unwrap(); + let written = if section { + let typed = |range, with: &str| Op::Page { + space: sid, + op: PageOp::Text { + text: oid, + range, + with: with.into(), + }, + }; + // Modification times count seconds: each edit is a second later. + let at = 134_000_000_000_000_000 + u64::from(operation) * 10_000_000; + let ops = vec![typed(0..0, "x"), typed(0..1, "")]; + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, bytes.clone()).unwrap(); + section.apply("Author", &Edit { at, ops }).unwrap(); + section.seal().unwrap().unwrap(); + section.image() + } else { + recolored(&bytes, 1_000_000 + operation) + }; let updated = Store::parse(&written).unwrap(); assert!(updated.checksum_mismatches.is_empty()); assert_eq!( @@ -412,9 +445,6 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { .iter() .all(|(entry, guid)| object.global_ids.get(entry) == Some(guid)) ); - if *id != oid { - assert_eq!(object.data, same.data); - } } if let (ObjectData::Properties(a), ObjectData::Properties(b)) = (object.data, same.data) @@ -426,10 +456,9 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { assert_eq!(a.len(), b.len()); for (a, b) in a.iter().zip(b) { assert_eq!(a.id, b.id); - if *id == oid && a.id == property { - assert_eq!(b.value, Value::Bytes(&value)); - } else if !matches!(a.value, Value::References { .. }) { - assert_eq!(a.value, b.value); + if a.id != property && !matches!(a.value, Value::References { .. }) + { + assert_eq!(a.value, b.value, "{id} {:#x}", a.id); } } } diff --git a/tools/canvas/README.md b/tools/canvas/README.md index 38db69057cc63274e27bd39ce70373baeb5904d8..db8d615275c3f880c97e845b47b1777660446c0e 100644 --- a/tools/canvas/README.md +++ b/tools/canvas/README.md @@ -82,9 +82,11 @@ Superscript and subscript runs draw at two thirds of their size, a third of the A paragraph stored as an equation draws in two dimensions from the tree `onestore::page::Math` parses: sub- and superscripts at 70%, fractions over a rule on the math axis, radicals drawn with the pen, n-ary operators at display size with limits above and below (integrals take them as scripts), stretched fences, accents and bars above, boxes, matrices and equation arrays. Latin letters draw in mathematical italic. Cambria Math falls back to STIX Two Math on macOS, and atoms are placed by their glyph ink rather than the math font's tall line box; an equation line is at least as tall as a line of text. The linear text stays the editable source, laid out in the body font so the caret keeps a text height; editing equations in two dimensions is a follow-up. -## Optional font substitution +## Font substitution -The host and `layout-probe` accept repeated `--substitute-font FONT_FILE` options. Supply Arimo regular/italic variable fonts or Carlito regular/bold/italic/bold-italic files. Each file is registered only in this process, as Arial or Calibri respectively; this explicitly replaces that family even when a system copy exists. Stored `Format.font` values remain unchanged. No font files are installed or bundled by this option. +`TextEngine::default()` lays out Calibri, Arial, Times New Roman and Courier New in the bundled Carlito 1.104, Arimo 1.341, Tinos 1.340 and Cousine 1.241 (`crates/canvas/assets/fonts`, SIL Open Font License) wherever the system lacks them. The font box names such a family with its substitute, as "Carlito (Calibri)"; stored `Format.font` values keep the document's name. + +The host and `layout-probe` also accept repeated `--substitute-font FONT_FILE` options for another Arimo, Carlito, Tinos or Cousine file. Each file is registered only in this process, as its family's counterpart; this explicitly replaces that family even when a system copy exists. No font files are installed. ```sh "target/Snowbound.app/Contents/MacOS/Snowbound" TEXT_FILE 240 --substitute-font ARIMO_FILE diff --git a/tools/document_model.py b/tools/document_model.py index f497a67eec36569e411ec3a0ea8374b03a95ca84..843438411d85558ec843aca54b8397a2da092def 100644 --- a/tools/document_model.py +++ b/tools/document_model.py @@ -1,8 +1,24 @@ """Traversal of the exported document's referenced revisions.""" import os from pathlib import Path +import shutil +import subprocess -EXPORTER = Path(os.environ.get('ONESTORE_DOCUMENT', Path(__file__).resolve().parent.parent / 'target/debug/examples/document')) + +def _exporter(): + """The document exporter built with protected sections, copied aside because a workspace + build without the feature overwrites target/debug/examples/document.""" + if 'ONESTORE_DOCUMENT' in os.environ: + return Path(os.environ['ONESTORE_DOCUMENT']) + root = Path(__file__).resolve().parent.parent + subprocess.run(['cargo', 'build', '--quiet', '-p', 'notebook', '--features', 'protected', + '--example', 'document'], cwd=root, check=True) + protected = root / 'target/debug/examples/document-protected' + shutil.copy2(root / 'target/debug/examples/document', protected) + return protected + + +EXPORTER = _exporter() BRIDGE = Path(__file__).resolve().parent.parent / 'target/debug/onestore-diagnostic' DEFAULT_CONTEXT = '{00000000-0000-0000-0000-000000000000},0' diff --git a/tools/native/collaborate.ps1 b/tools/native/collaborate.ps1 index 4a13a4320675250d49eac958bd3a020ca3b882f6..6a42a3d526db486f022b032583704b0f4aee26d3 100644 --- a/tools/native/collaborate.ps1 +++ b/tools/native/collaborate.ps1 @@ -47,6 +47,46 @@ try { Invoke-Stress $app $section $command $output $shared } elseif ($command.action -eq 'sync') { $app.SyncHierarchy($notebook) } + elseif ($command.action -in @('pages', 'move', 'delete-page')) { + $namespace = 'http://schemas.microsoft.com/office/onenote/2010/onenote' + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$tree = $hierarchy + $listed = @($tree.SelectNodes('//*[local-name()="Page"]')) + $named = { param($title) @($listed | Where-Object { $_.GetAttribute('name') -eq $title }) } + if ($command.action -eq 'pages') { + # New titled pages with one body paragraph each, appended in order. + foreach ($title in $command.titles) { + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $body = [Security.SecurityElement]::Escape('Body ' + $title + '.') + $name = [Security.SecurityElement]::Escape($title) + $xml = '' + $name + '' + $body + '' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + } + } elseif ($command.action -eq 'move') { + # Moves the page titled `page` before the one titled `before`, or last. + $moved = @(& $named $command.page) + if ($moved.Count -ne 1) { throw 'Expected one page to move.' } + $order = [Collections.ArrayList]@($listed | Where-Object { $_ -ne $moved[0] }) + $at = $order.Count + if ($command.before) { + $anchor = @(& $named $command.before) + if ($anchor.Count -ne 1) { throw 'Expected one anchor page.' } + $at = $order.IndexOf($anchor[0]) + } + $order.Insert($at, $moved[0]) + $xml = '' + foreach ($node in $order) { + $xml += '' + } + $app.UpdateHierarchy($xml + '', 1) + } else { + $doomed = @(& $named $command.page) + if ($doomed.Count -ne 1) { throw 'Expected one page to delete.' } + $app.DeleteHierarchy($doomed[0].GetAttribute('ID'), [DateTime]::MinValue, $false) + } + } elseif ($command.action -eq 'kill-process') { Get-Process ONENOTE | Stop-Process -Force [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) diff --git a/tools/native_collaboration.py b/tools/native_collaboration.py index e716c23436a708b6dd718b9f80da5a51941fca2d..cadcd2ed5eaaa6501a8962801b5e1ea5036a3079 100644 --- a/tools/native_collaboration.py +++ b/tools/native_collaboration.py @@ -18,7 +18,7 @@ import xml.etree.ElementTree as ET from native_runner import ROOT, clone, command, windows, vm, collect_artifacts from native_xml import texts -from document_model import ordered_pages, view, walk +from document_model import EXPORTER, ordered_pages, view, walk import linux_vm @@ -260,7 +260,7 @@ def replay(output, server, stress_clients=0, stress_operations=30, sync_every=1, target = output / label / 'notebook' target.mkdir(parents=True) for name, data in snapshot.items(): (target / name).write_bytes(data) - result = subprocess.run([ROOT / 'target/debug/examples/document', target / 'synthetic.one', target.parent / 'model'], capture_output=True, text=True) + result = subprocess.run([EXPORTER, target / 'synthetic.one', target.parent / 'model'], capture_output=True, text=True) if result.returncode == 0: (target.parent / 'locks.txt').write_text(ssh('sudo smbstatus --locks')) print('Captured collaboration state:', label, flush=True) @@ -318,7 +318,7 @@ def replay(output, server, stress_clients=0, stress_operations=30, sync_every=1, recovered.mkdir() with (recovered / 'synthetic.one').open('wb') as stream: subprocess.run(linux_vm.ssh_argv(server, 'cat /srv/agent/m6-collaboration/synthetic.one'), stdout=stream, check=True, timeout=60) - subprocess.run([ROOT / 'target/debug/examples/document', recovered / 'synthetic.one', recovered / 'model'], check=True) + subprocess.run([EXPORTER, recovered / 'synthetic.one', recovered / 'model'], check=True) model = json.loads((recovered / 'model/document.json').read_text()) text = active_text(model) retained = verify_text('Concurrent edits:', text, logs) @@ -459,7 +459,7 @@ def replay(output, server, stress_clients=0, stress_operations=30, sync_every=1, if temporary.exists(): shutil.rmtree(temporary) snapshot = output / 'conflict.one' snapshot.write_bytes(snapshot_file('synthetic.one')) - subprocess.run([ROOT / 'target/debug/examples/document', snapshot, temporary], check=True) + subprocess.run([EXPORTER, snapshot, temporary], check=True) model = json.loads((temporary / 'document.json').read_text()) values = [node['kind'].get('text') for space in model['spaces'].values() for revision in space['revisions'].values() for node in revision['nodes'].values()] if 'Client A concurrent edit.' in values and 'Client B concurrent edit.' in values: break diff --git a/tools/native_conflict.py b/tools/native_conflict.py new file mode 100644 index 0000000000000000000000000000000000000000..ccb4026acf69f3df84fdfbb01e9b6e39461762d3 --- /dev/null +++ b/tools/native_conflict.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +"""OneNote 2010 conflict pages: how they arise, what they store and how OneNote shows them. + +`sync LINUX OUTPUT` runs two disposable OneNote clients on one Samba notebook: client A edits +two paragraphs offline while client B edits one of them and a table cell online; A then +reconnects, which makes OneNote write a conflict page. The server file is kept before and +after, A and B are screenshotted with the information bar, the page list shown, the conflict +page open and its menu open, and A then deletes the conflict page through that menu. + +`pages LINUX OUTPUT` runs the same two clients on page-list changes: client A, offline, moves +a page that client B also moves, moves another page, and edits a page B deletes; A then +reconnects. The server's page order and pages, with the recycle bin, are kept at each step. + +`restore LINUX OUTPUT` runs them on a page in the middle of the list: client A, offline, edits +it and moves a page B leaves alone while B deletes it; A then reconnects. + +`cold NOTEBOOK OUTPUT [--delete]` cold-opens a notebook holding a conflict page in a fresh +clone and screenshots the same views; `--delete` deletes the conflict page through the menu +and captures the notebook OneNote leaves. +""" +import argparse +from contextlib import ExitStack +from concurrent.futures import ThreadPoolExecutor +import base64 +import json +from pathlib import Path +import shutil +import subprocess +import sys +import time +import xml.etree.ElementTree as ET + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +sys.path.insert(0, str(Path(__file__).resolve().parent / 'w7')) +import native_runner as runner +from native_runner import ROOT, clone, command, windows, vm +import linux_vm + +FIXTURE = ROOT / 'corpus/native-ink/cold-ui-ink/notebook' +SHARE = 'm6-conflict' +ORIGINAL = 'Fictitious: café, 東京, مرحبا' +OTHER_OUTLINE = 'Fictitious positioned outline.' +CELL = 'Left cell' + +# Coordinates on OneNote 2010 maximized at 800x600, from the stage-5, m8 and sync-01 captures: +# the information bar's text, and the first conflict page listed under its page. +BAR = (332, 100) +FIRST_CONFLICT = (725, 142) +# Delete Conflict Page, first in the menu the bar opens on a conflict page. +DELETE = (420, 113) + + +def shot(name, output, label, script): + """Runs an AutoHotkey step on the clone, keeping its screenshot as `label.png`.""" + (output / f'{label}.ahk').write_text(script) + result = windows.do_exec(script, target=name, timeout_ms=120000, shot_delay_ms=1500) + screenshot = result.pop('png_b64', None) + if screenshot: + (output / f'{label}.png').write_bytes(base64.b64decode(screenshot)) + (output / f'{label}.json').write_text(json.dumps(result, indent=2)) + if result.get('error') or result.get('exit') != 0: + raise RuntimeError(f'{label}: {result}') + return result + + +def click(x, y): + return ('WinActivate("ahk_exe ONENOTE.EXE")\nCoordMode("Mouse", "Screen")\n' + f'Click({x}, {y})\nSleep(1200)\n') + + +# Reports any dialog OneNote raised, so a confirmation shows in the step's record. +DIALOG = ('if WinExist("ahk_class #32770 ahk_exe ONENOTE.EXE") {\n' + ' FileAppend("dialog: " WinGetTitle() " | " WinGetText() "`n", "*", "UTF-8")\n' + '}\n') + + +def conflict_views(name, output, page, prefix, delete=False): + """Screenshots a page with a conflict page: the bar, the list shown, the conflict page and + its menu; `delete` then chooses Delete Conflict Page.""" + shot(name, output, f'{prefix}-main', runner.navigation_script(page) + 'Sleep(2000)\n') + shot(name, output, f'{prefix}-shown', click(*BAR)) + shot(name, output, f'{prefix}-conflict', click(*FIRST_CONFLICT)) + shot(name, output, f'{prefix}-menu', click(*BAR)) + if delete: + shot(name, output, f'{prefix}-delete', click(*DELETE) + DIALOG) + else: + shot(name, output, f'{prefix}-closed', 'WinActivate("ahk_exe ONENOTE.EXE")\nSend("{Esc}")\nSleep(800)\n') + + +def cold(notebook, output, delete): + def interaction(name, output): + page = ET.parse(sorted((output / 'read').glob('page-*.xml'))[0]).getroot().attrib['ID'] + conflict_views(name, output, page, 'cold', delete) + if delete: + # OneNote writes the deletion in the background; give it time before the re-read. + time.sleep(20) + runner.capture(notebook, output, collect_notebook=True, interaction=interaction) + + +class Lab: + """Two OneNote clients on one Samba notebook: `action` runs a controller command, + `state` copies the share and exports its section, `settle` syncs until a check holds.""" + + +def page_list(model): + """The section's pages in order, each as its sorted texts.""" + from document_model import ordered_pages, walk + return [sorted(node['kind']['text'] for _, node in walk(page, oid) if node['kind']['type'] == 'RichText') + for _, _, page, oid in ordered_pages(model)] + + +def edits(lab): + from native_collaboration import reachable_page_text + a, b, output, action, settle = lab.a, lab.b, lab.output, lab.action, lab.settle + state = lambda label: reachable_page_text(lab.state(label)) + texts = lambda state: {text for values in state.values() for text in values} + state('initial') + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': False}) + try: + action(a, 'edit', expected=ORIGINAL, text='Client A conflicting edit.') + action(a, 'edit', expected=OTHER_OUTLINE, text='Client A disjoint edit.') + action(a, 'snapshot') + action(b, 'edit', expected=ORIGINAL, text='Client B conflicting edit.') + action(b, 'edit', expected=CELL, text='Client B disjoint cell.') + settle(b, lambda label: {'Client B conflicting edit.', 'Client B disjoint cell.'} <= texts(state(label)), 'b-published') + finally: + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': True}) + settle(a, lambda label: len(found := state(label)) > 1 and 'Client A disjoint edit.' in texts(found), 'conflict') + (output / 'conflict.json').write_text(json.dumps(state('conflict'), indent=2, ensure_ascii=False)) + page = action(a, 'snapshot')[0].attrib['ID'] + conflict_views(a['name'], output / 'a', page, 'a') + action(b, 'sync') + time.sleep(5) + page_b = action(b, 'snapshot')[0].attrib['ID'] + conflict_views(b['name'], output / 'b', page_b, 'b') + conflict_views(a['name'], output / 'a', page, 'a-again', delete=True) + settle(a, lambda label: len(state(label)) == 1, 'deleted') + action(b, 'sync') + time.sleep(5) + shot(b['name'], output / 'b', 'b-after-delete', runner.navigation_script(page_b) + 'Sleep(2000)\n') + + +PAGES = ['One', 'Two', 'Three', 'Four', 'Target'] + + +def titled(model, titles): + """The section's pages in order, each named by the title among `titles` it holds.""" + return [next((text for text in texts if text in titles), '') for texts in page_list(model)] + + +def offline(lab, titles, a_actions, b_actions): + """Creates the titled pages; A runs its actions offline while B runs and publishes its + own; A then reconnects. Keeps the merged order and pages, and what each client lists.""" + a, b, output, action = lab.a, lab.b, lab.output, lab.action + action(a, 'pages', titles=titles) + lab.settle(a, lambda label: 'Body Target.' in str(page_list(lab.state(label))), 'initial') + deadline = time.monotonic() + 300 + while not any(node.get('name') == 'Target' for node in action(b, 'snapshot')): + action(b, 'sync') + if time.monotonic() > deadline: raise TimeoutError('B never listed the new pages') + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': False}) + try: + for kind, parameters in a_actions: + action(a, kind, **parameters) + action(a, 'snapshot') + for kind, parameters in b_actions: + action(b, kind, **parameters) + action(b, 'snapshot') + lab.settle(b, lambda label: 'Body Target.' not in str(page_list(lab.state(label))), 'b-published') + finally: + vm.qmp(a['name'], 'set_link', {'name': 'lab', 'up': True}) + # A's changes reach the server when its sync writes; wait until the file stops changing. + previous, steady = None, 0 + deadline = time.monotonic() + 300 + while steady < 3 and time.monotonic() < deadline: + action(a, 'sync') + time.sleep(10) + current = page_list(lab.state('merged')) + steady = steady + 1 if current == previous else 0 + previous = current + (output / 'merged.json').write_text(json.dumps({'order': titled(lab.state('merged'), titles), 'pages': previous}, indent=2, ensure_ascii=False)) + for client, label in ((a, 'a'), (b, 'b')): + action(client, 'sync') + time.sleep(5) + listed = action(client, 'snapshot') + (output / label / 'listed.json').write_text(json.dumps([node.get('name') for node in listed], ensure_ascii=False)) + if listed: + shot(client['name'], output / label, f'{label}-merged', runner.navigation_script(listed[0].get('ID')) + 'Sleep(2000)\n') + + +def pages(lab): + """Competing page moves, and an offline edit of a page the other client deletes.""" + offline(lab, PAGES, [ + ('move', {'page': 'Four', 'before': 'One'}), + ('move', {'page': 'Two', 'before': None}), + ('edit', {'expected': 'Body Target.', 'text': 'Body Target edited offline.'}), + ], [ + ('move', {'page': 'Four', 'before': None}), + ('move', {'page': 'Three', 'before': 'One'}), + ('delete-page', {'page': 'Target'}), + ]) + + +def restore(lab): + """An offline edit of a page in the middle of the list the other client deletes, with an + offline move of a page the other client leaves alone.""" + offline(lab, ['One', 'Two', 'Target', 'Three', 'Four'], [ + ('edit', {'expected': 'Body Target.', 'text': 'Body Target edited offline.'}), + ('move', {'page': 'Four', 'before': 'One'}), + ], [ + ('delete-page', {'page': 'Target'}), + ]) + + +def sync(server, output, scenario): + from document_model import EXPORTER + + if linux_vm.instance_path(server).exists(): + raise ValueError('Choose a new Linux VM name; existing machines are not owned by this run.') + output = output.resolve() + output.mkdir(parents=True, exist_ok=False) + scripts = output / 'scripts' + scripts.mkdir() + for name in ('cold.ps1', 'collaborate.ps1', 'network.ps1', 'text.ps1', 'stress.ps1'): + shutil.copyfile(ROOT / 'tools/native' / name, scripts / name) + shutil.copyfile(Path(__file__), output / 'native_conflict.py') + + def ssh(text, timeout=90): + result = linux_vm.run_ssh(server, text, timeout=timeout) + with (output / 'server.jsonl').open('a') as log: + log.write(json.dumps({'command': text, 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr}) + '\n') + result.check_returncode() + return result.stdout + + def server_state(label): + """Copies the server's notebook, recycle bin included, until its section parses, + keeping it as `label/notebook`; returns the section's exported model.""" + deadline = time.monotonic() + 120 + while True: + target = output / label + shutil.rmtree(target, ignore_errors=True) + (target / 'notebook').mkdir(parents=True) + archive = subprocess.run(linux_vm.ssh_argv(server, f"tar cf - -C '/srv/agent/{SHARE}' ."), capture_output=True, check=True, timeout=60).stdout + subprocess.run(['tar', 'xf', '-', '-C', target / 'notebook'], input=archive, check=True) + result = subprocess.run([EXPORTER, target / 'notebook/synthetic.one', target / 'model'], capture_output=True, text=True) + if result.returncode == 0: + return json.loads((target / 'model/document.json').read_text()) + if time.monotonic() > deadline: + raise RuntimeError(f'{label}: {result.stderr}') + time.sleep(1) + + linux_vm.create_instance(server) + try: + linux_vm.launch(server) + linux_vm.wait_instance(server, 600) + (output / 'linux.json').write_text(json.dumps(linux_vm.load_instance(server), indent=2)) + ssh(f'mkdir /srv/agent/{SHARE}') + for name in ('synthetic.one', 'Open Notebook.onetoc2'): + with (FIXTURE / name).open('rb') as stream: + subprocess.run(linux_vm.ssh_argv(server, f"cat > '/srv/agent/{SHARE}/{name}'"), stdin=stream, check=True) + ssh(f'chmod u+w /srv/agent/{SHARE}/*') + with ExitStack() as stack: + labels = ['a', 'b'] + for label in labels: + (output / label).mkdir() + + def start(label): + manager = clone(output / label) + name = manager.__enter__() + return label, name, manager + with ThreadPoolExecutor(max_workers=2) as pool: + started = list(pool.map(start, labels)) + for _, _, manager in started: + stack.push(manager) + clients = {} + for label, name, _ in started: + folder = output / label + for local in scripts.iterdir(): + remote = 'cold-current.ps1' if local.name == 'cold.ps1' else local.name + result = windows.do_put(local, 'C:\\one-tests\\' + remote, name) + if result.get('error'): raise RuntimeError(result['error']) + command(name, 'mkdir C:\\one-tests\\runs\\capture', folder) + command(name, 'powershell -NoProfile -ExecutionPolicy Bypass -File C:\\one-tests\\network.ps1 -LabMac ' + vm.lab_mac(name), folder) + result = windows.do_spawn('cmd /c powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\collaborate.ps1 -Root C:\\one-tests\\runs\\capture -SharedPath \\\\192.168.77.1\\agent\\' + SHARE + ' -CloneHost ONE-' + name.upper() + ' > C:\\one-tests\\runs\\capture\\controller.log 2>&1', name) + if result.get('error'): raise RuntimeError(result['error']) + deadline = time.monotonic() + 180 + while windows.do_cmd('if exist C:\\one-tests\\runs\\capture\\ready (echo ready)', target=name).get('stdout', '').find('ready') < 0: + if time.monotonic() > deadline: raise RuntimeError('The collaboration controller did not open the notebook') + time.sleep(1) + clients[label] = {'name': name, 'folder': folder, 'sequence': 0} + (output / 'clients.json').write_text(json.dumps({label: client['name'] for label, client in clients.items()}, indent=2)) + + def action(client, kind, **parameters): + client['sequence'] += 1 + sequence = client['sequence'] + local = client['folder'] / f'command-{sequence:04}.json' + local.write_text(json.dumps({'action': kind, **parameters}, ensure_ascii=False)) + inbox = f'C:\\one-tests\\runs\\capture\\inbox\\{sequence:04}' + result = windows.do_put(local, inbox + '.tmp', client['name']) + if result.get('error'): raise RuntimeError(result['error']) + command(client['name'], f'move {inbox}.tmp {inbox}.json', client['folder']) + remote = f'C:\\one-tests\\runs\\capture\\outbox\\{sequence}' + deadline = time.monotonic() + 600 + while True: + result = windows.do_cmd(f'if exist {remote}\\error (type {remote}\\error & exit /b 1) else (if exist {remote}\\done (echo complete))', target=client['name']) + if result.get('exit') != 0: raise RuntimeError(str(result)) + if 'complete' in result.get('stdout', ''): break + if time.monotonic() > deadline: raise TimeoutError(f'{kind} did not complete') + time.sleep(.5) + if kind != 'snapshot': + return None + hierarchy = client['folder'] / f'hierarchy-{sequence:04}.xml' + windows.do_get(remote + '\\hierarchy.xml', hierarchy, client['name']) + pages = [node for node in ET.fromstring(hierarchy.read_text(encoding='utf-8-sig').strip()).iter() if node.tag.endswith('}Page')] + for i in range(len(pages)): + windows.do_get(remote + f'\\page-{i}.xml', client['folder'] / f'snapshot-{sequence:04}-{i}.xml', client['name']) + return pages + + def settle(client, check, label): + deadline = time.monotonic() + 300 + while True: + action(client, 'sync') + if check(label): return + if time.monotonic() > deadline: raise TimeoutError(label) + time.sleep(2) + + lab = Lab() + lab.a, lab.b, lab.output = clients['a'], clients['b'], output + lab.action, lab.state, lab.settle = action, server_state, settle + scenario(lab) + + for client in clients.values(): + try: + action(client, 'close') + except Exception as error: + (client['folder'] / 'close-failure.txt').write_text(str(error)) + server_state('final') + finally: + if linux_vm.running(server): + linux_vm.shutdown(server, 120) + linux_vm.delete_instance(server) + (output / 'linux-teardown.json').write_text(json.dumps({'absent': not linux_vm.instance_path(server).exists()}) + '\n') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + commands = parser.add_subparsers(dest='mode', required=True) + scenarios = {'sync': edits, 'pages': pages, 'restore': restore} + for mode in scenarios: + lab = commands.add_parser(mode) + lab.add_argument('linux') + lab.add_argument('output', type=Path) + reopen = commands.add_parser('cold') + reopen.add_argument('notebook', type=Path) + reopen.add_argument('output', type=Path) + reopen.add_argument('--delete', action='store_true') + args = parser.parse_args() + if args.mode in scenarios: + sync(args.linux, args.output, scenarios[args.mode]) + else: + cold(args.notebook, args.output, args.delete) + print('Captured', args.output) diff --git a/tools/smb-proxy.py b/tools/smb-proxy.py index 1e9024d778b065f85b293946b3d4c9972ffe476d..310d0af8b49a996ed0d04439d9d32ee1562c7ce2 100644 --- a/tools/smb-proxy.py +++ b/tools/smb-proxy.py @@ -8,6 +8,18 @@ import struct import time +def lease_state(frame, at, contexts): + """The lease state of a CREATE's `RqLs` context, if any (MS-SMB2 2.2.13.2.8, 2.2.14.2.10).""" + offset, length = struct.unpack_from('

  • }`_Y{Bm?8jCOer(Isj4}J}NF%9V1|9=4_phTkp literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-pages/b-published/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 b/corpus/conflict-page/native-pages/b-published/notebook/OneNote_RecycleBin/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..6e4b6c9be779af4e6515e1c9facf29cf6c5b792e GIT binary patch literal 3776 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 HnEd;HBulnx literal 0 HcmV?d00001 diff --git a/corpus/conflict-page/native-pages/b/b-merged.json b/corpus/conflict-page/native-pages/b/b-merged.json new file mode 100644 index 0000000000000000000000000000000000000000..2f394e7eaf1defcfe3e06e321b5af76be4f84054 --- /dev/null +++ b/corpus/conflict-page/native-pages/b/b-merged.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Fictitious: caf\u00e9, \u6771\u4eac, \u0645\u0631\u062d\u0628\u0627 - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/conflict-page/native-pages/b/b-merged.png b/corpus/conflict-page/native-pages/b/b-merged.png new file mode 100644 index 0000000000000000000000000000000000000000..33bdd9945f10bcec0adcd5e541085aa7e14e86e6 GIT binary patch literal 47313 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