From c3891b37112bb60d6f963ef3adae5a1f5c3e5e73 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Tue, 8 Sep 2026 12:57:55 -0700 Subject: [PATCH] feat: move and delete page subtrees atomically Add TreeEdit and PreparedEdit::tree for explicit same-page subtree placement and deletion. Preserve surviving identities, fields, tags, tables and history; normalize empty/group-only containers and allocate stable empty-cell replacements. Publish attribution and automatic titles with the graph. Follow resulting ancestry after group normalization, and permit retained intents to reuse identical immutable author records. Share title updates with paragraph editing. Validation: eighteen native control comparisons, four cross-container cases and unequal-group normalization; forty pages cold-open in real OneNote. Core/offline regression lanes, final tree tests, 144 Python tests, workspace Clippy and tool builds pass. Three ASan campaigns complete 1239 schedules with twelve cached clients, ordering/indentation oracles, retained-intent replay and publication interruptions. All owned VMs removed. Assisted-by: gpt-6-astra --- corpus/outline-edit/README.md | 27 + .../cross-container/candidate/synthetic.one | Bin 0 -> 92512 bytes .../cross-container/cold/commands.jsonl | 3 + .../cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 4800 bytes .../cold/notebook/synthetic.one | Bin 0 -> 92512 bytes .../cold/read/environment.json | 7 + .../cross-container/cold/read/hierarchy.xml | 2 + .../cross-container/cold/read/page-000.xml | 5 + .../cross-container/cold/read/page-001.xml | 5 + .../cross-container/cold/read/page-002.xml | 5 + .../cross-container/cold/read/page-003.xml | 5 + .../cross-container/cold/read/page-004.xml | 5 + .../cross-container/cold/read/page-005.xml | 4 + .../cross-container/cold/read/page-006.xml | 20 + .../cross-container/cold/read/page-007.xml | 5 + .../cross-container/cold/read/page-008.xml | 4 + .../cross-container/cold/read/page-009.xml | 5 + .../cross-container/cold/read/page-010.xml | 5 + .../cross-container/cold/read/page-011.xml | 5 + .../cross-container/cold/read/payloads.json | 1 + .../rust-tree/cross-container/cold/run.json | 18 + .../cross-container/cold/scripts/cold.ps1 | 1 + .../cross-container/cold/scripts/read.ps1 | 1 + .../cross-container/cold/source.json | 8 + .../cross-container/cold/teardown.json | 1 + .../groups-cells/candidate/synthetic.one | Bin 0 -> 98760 bytes .../groups-cells/cold/commands.jsonl | 3 + .../cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 4800 bytes .../groups-cells/cold/notebook/synthetic.one | Bin 0 -> 102488 bytes .../groups-cells/cold/read/environment.json | 7 + .../groups-cells/cold/read/hierarchy.xml | 2 + .../groups-cells/cold/read/page-000.xml | 5 + .../groups-cells/cold/read/page-001.xml | 5 + .../groups-cells/cold/read/page-002.xml | 2 + .../groups-cells/cold/read/page-003.xml | 5 + .../groups-cells/cold/read/page-004.xml | 2 + .../groups-cells/cold/read/page-005.xml | 5 + .../groups-cells/cold/read/page-006.xml | 2 + .../groups-cells/cold/read/page-007.xml | 20 + .../groups-cells/cold/read/page-008.xml | 2 + .../groups-cells/cold/read/page-009.xml | 2 + .../groups-cells/cold/read/page-010.xml | 5 + .../groups-cells/cold/read/page-011.xml | 5 + .../groups-cells/cold/read/payloads.json | 1 + .../rust-tree/groups-cells/cold/run.json | 18 + .../groups-cells/cold/scripts/cold.ps1 | 1 + .../groups-cells/cold/scripts/read.ps1 | 1 + .../rust-tree/groups-cells/cold/source.json | 8 + .../rust-tree/groups-cells/cold/teardown.json | 1 + .../ordinary/candidate/synthetic.one | Bin 0 -> 111728 bytes .../rust-tree/ordinary/cold/commands.jsonl | 3 + .../cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 4800 bytes .../ordinary/cold/notebook/synthetic.one | Bin 0 -> 115880 bytes .../ordinary/cold/read/environment.json | 7 + .../ordinary/cold/read/hierarchy.xml | 2 + .../rust-tree/ordinary/cold/read/page-000.xml | 5 + .../rust-tree/ordinary/cold/read/page-001.xml | 5 + .../rust-tree/ordinary/cold/read/page-002.xml | 5 + .../rust-tree/ordinary/cold/read/page-003.xml | 2 + .../rust-tree/ordinary/cold/read/page-004.xml | 4 + .../rust-tree/ordinary/cold/read/page-005.xml | 5 + .../rust-tree/ordinary/cold/read/page-006.xml | 2 + .../rust-tree/ordinary/cold/read/page-007.xml | 5 + .../rust-tree/ordinary/cold/read/page-008.xml | 2 + .../rust-tree/ordinary/cold/read/page-009.xml | 5 + .../rust-tree/ordinary/cold/read/page-010.xml | 20 + .../rust-tree/ordinary/cold/read/page-011.xml | 5 + .../rust-tree/ordinary/cold/read/page-012.xml | 5 + .../rust-tree/ordinary/cold/read/page-013.xml | 2 + .../rust-tree/ordinary/cold/read/page-014.xml | 5 + .../ordinary/cold/read/payloads.json | 1 + .../rust-tree/ordinary/cold/run.json | 18 + .../rust-tree/ordinary/cold/scripts/cold.ps1 | 1 + .../rust-tree/ordinary/cold/scripts/read.ps1 | 1 + .../rust-tree/ordinary/cold/source.json | 8 + .../rust-tree/ordinary/cold/teardown.json | 1 + .../unequal-groups/candidate/groups.one | Bin 0 -> 8784 bytes .../unequal-groups/cold/commands.jsonl | 3 + .../cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 4792 bytes .../unequal-groups/cold/notebook/groups.one | Bin 0 -> 8784 bytes .../unequal-groups/cold/read/environment.json | 7 + .../unequal-groups/cold/read/hierarchy.xml | 2 + .../unequal-groups/cold/read/page-000.xml | 2 + .../unequal-groups/cold/read/payloads.json | 1 + .../rust-tree/unequal-groups/cold/run.json | 18 + .../unequal-groups/cold/scripts/cold.ps1 | 1 + .../unequal-groups/cold/scripts/read.ps1 | 1 + .../rust-tree/unequal-groups/cold/source.json | 8 + .../unequal-groups/cold/teardown.json | 1 + crates/onestore/README.md | 12 + crates/onestore/src/commit.rs | 12 + crates/onestore/src/edit.rs | 56 +- crates/onestore/src/lib.rs | 2 + crates/onestore/src/paragraph.rs | 47 +- crates/onestore/src/tree.rs | 597 ++++++++++++++++++ crates/onestore/tests/support/tree_model.rs | 364 +++++++++++ crates/onestore/tests/tree.rs | 520 +++++++++++++++ fuzz/Cargo.toml | 7 + fuzz/fuzz_targets/tree.rs | 11 + tools/test_outline_edit.py | 30 + 100 files changed, 2016 insertions(+), 49 deletions(-) create mode 100644 corpus/outline-edit/rust-tree/cross-container/candidate/synthetic.one create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/commands.jsonl create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/notebook/synthetic.one create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/environment.json create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/hierarchy.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-000.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-001.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-002.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-003.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-004.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-005.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-006.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-007.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-008.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-009.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-010.xml create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/read/page-011.xml create mode 120000 corpus/outline-edit/rust-tree/cross-container/cold/read/payloads.json create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/run.json create mode 120000 corpus/outline-edit/rust-tree/cross-container/cold/scripts/cold.ps1 create mode 120000 corpus/outline-edit/rust-tree/cross-container/cold/scripts/read.ps1 create mode 100644 corpus/outline-edit/rust-tree/cross-container/cold/source.json create mode 120000 corpus/outline-edit/rust-tree/cross-container/cold/teardown.json create mode 100644 corpus/outline-edit/rust-tree/groups-cells/candidate/synthetic.one create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/commands.jsonl create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/notebook/synthetic.one create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/environment.json create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/hierarchy.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-000.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-001.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-002.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-003.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-004.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-005.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-006.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-007.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-008.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-009.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-010.xml create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/read/page-011.xml create mode 120000 corpus/outline-edit/rust-tree/groups-cells/cold/read/payloads.json create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/run.json create mode 120000 corpus/outline-edit/rust-tree/groups-cells/cold/scripts/cold.ps1 create mode 120000 corpus/outline-edit/rust-tree/groups-cells/cold/scripts/read.ps1 create mode 100644 corpus/outline-edit/rust-tree/groups-cells/cold/source.json create mode 120000 corpus/outline-edit/rust-tree/groups-cells/cold/teardown.json create mode 100644 corpus/outline-edit/rust-tree/ordinary/candidate/synthetic.one create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/commands.jsonl create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/notebook/synthetic.one create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/environment.json create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/hierarchy.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-000.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-001.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-002.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-003.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-004.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-005.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-006.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-007.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-008.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-009.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-010.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-011.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-012.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-013.xml create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/read/page-014.xml create mode 120000 corpus/outline-edit/rust-tree/ordinary/cold/read/payloads.json create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/run.json create mode 120000 corpus/outline-edit/rust-tree/ordinary/cold/scripts/cold.ps1 create mode 120000 corpus/outline-edit/rust-tree/ordinary/cold/scripts/read.ps1 create mode 100644 corpus/outline-edit/rust-tree/ordinary/cold/source.json create mode 120000 corpus/outline-edit/rust-tree/ordinary/cold/teardown.json create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/candidate/groups.one create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/commands.jsonl create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/notebook/groups.one create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/read/environment.json create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/read/hierarchy.xml create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/read/page-000.xml create mode 120000 corpus/outline-edit/rust-tree/unequal-groups/cold/read/payloads.json create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/run.json create mode 120000 corpus/outline-edit/rust-tree/unequal-groups/cold/scripts/cold.ps1 create mode 120000 corpus/outline-edit/rust-tree/unequal-groups/cold/scripts/read.ps1 create mode 100644 corpus/outline-edit/rust-tree/unequal-groups/cold/source.json create mode 120000 corpus/outline-edit/rust-tree/unequal-groups/cold/teardown.json create mode 100644 crates/onestore/src/tree.rs create mode 100644 crates/onestore/tests/support/tree_model.rs create mode 100644 crates/onestore/tests/tree.rs create mode 100644 fuzz/fuzz_targets/tree.rs diff --git a/corpus/outline-edit/README.md b/corpus/outline-edit/README.md index 68a8009af727b6d82fcafe497980eb103a1d4db1..a0ee8573986ceefb25f5f676ddf57ee9dae3748d 100644 --- a/corpus/outline-edit/README.md +++ b/corpus/outline-edit/README.md @@ -101,3 +101,30 @@ Regenerate using the same source notebook and runner, with controller at inspection readiness, move `before-read` to `before/read`, then cold-open the returned notebook with `--expected-pages 12 --collect-notebook`. The exact authoring script has one home in the reference corpus. + +`rust-tree` retains four Rust-generated notebooks and their fresh-cache OneNote +captures. `ordinary` and `groups-cells` apply eighteen moves/deletions from the +native controls. Explicit bullet-subtree movement preserves its original marker. +`cross-container` moves content between cells and outlines, moves a whole table, +and moves a sibling beneath a paragraph while its enclosing group is normalized. +`unequal-groups` deletes a trailing paragraph after two differently indented +groups; native XML reports the surviving paragraphs at indentation levels three +and two. Every surviving content identity, field, tag and table remains intact; +an emptied cell receives new empty paragraph/text identities. + +The four captures cover forty pages. Public tests compare native text and +formatting, the complete active graphs before/after cold reopen, metadata, and +list objects. Core tests additionally compare eighteen native transformations, +every historical object's property bytes, selected move attribution, protected +content, reused intents, and interrupted publication. The shared stateful model +in `crates/onestore/tests/support/tree_model.rs` checks paragraph order, +indentation, content, automatic titles and old/new publication outcomes across +twelve cached clients; `fuzz/fuzz_targets/tree.rs` runs that oracle under ASan. + +Set `ONESTORE_TREE_OUTPUT` to a new absolute directory and run the +`native_subtree_controls_match_with_preserved_fields_and_history` and +`cross_container_moves_keep_tables_and_replace_emptied_cells` integration tests, +plus the `tree::tests::group_normalization_preserves_unequal_indentation_and_overlapping_moves` +unit test. Cold-open the resulting `ordinary`, `groups-cells`, `cross-container` +and `unequal-groups` notebooks with expected page counts 15, 12, 12 and 1, +respectively, using `tools/native_runner.py --collect-notebook`. diff --git a/corpus/outline-edit/rust-tree/cross-container/candidate/synthetic.one b/corpus/outline-edit/rust-tree/cross-container/candidate/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..a8475a2c24296df9206a3fc5398cf1d0f68cbb7a GIT binary patch literal 92512 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 P7!3iOA%I-0!O9;1>Rd!{ literal 0 HcmV?d00001 diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/notebook/synthetic.one b/corpus/outline-edit/rust-tree/cross-container/cold/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..e4377ac28e4c85f300a573f343a9e7d2a6f58392 GIT binary patch literal 92512 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 + diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-000.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..91fce4c458c906398bede2244c9d899d03465108 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-000.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-001.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..d16f631695e6bf6bd8d32a3ab582cb311e5986d4 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-001.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-002.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..eb9054d278c8d7fc8e72cd4f67fea70f02c43989 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-002.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-003.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..0e4b8ee9beb2d8a243b2ed2962b5c837c39aaff4 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-003.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-004.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..631561f4b61231d365d42152230594d0acbd775d --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-004.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-005.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..c28f1296f832943cbdd2d86c932638b8e02ee796 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-005.xml @@ -0,0 +1,4 @@ + +Target 🦀 é Link label]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-006.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..215a527f3e2a76656eda825161c0274995ead202 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-006.xml @@ -0,0 +1,20 @@ + +Bold 🦀 italic é color 東京
+End
]]>
Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-007.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..eec578c1676fe7c101e93a406f86ead968f2e1ce --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-007.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-008.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..5c3de844098eac10d79a8b73987b2f1a61d87a72 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-008.xml @@ -0,0 +1,4 @@ + +Target 🦀 é Link label]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-009.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-009.xml new file mode 100644 index 0000000000000000000000000000000000000000..a71568b190bb7b6b560e38fe32e5665afe73ffed --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-009.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-010.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-010.xml new file mode 100644 index 0000000000000000000000000000000000000000..fe192e08e61dc220f3d091b6d76210b8339bc0ed --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-010.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/page-011.xml b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-011.xml new file mode 100644 index 0000000000000000000000000000000000000000..b8cdbb783a28d7d552dc7907e4eb0d905a579db4 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/page-011.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/read/payloads.json b/corpus/outline-edit/rust-tree/cross-container/cold/read/payloads.json new file mode 120000 index 0000000000000000000000000000000000000000..28e766c3d4f4499afc8ee14682bf4467ff3087cd --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/read/payloads.json @@ -0,0 +1 @@ +../../../../../paragraph-edit/before/read/payloads.json \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/run.json b/corpus/outline-edit/rust-tree/cross-container/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..ea20549de5f1bc4e70a60ef906203d382a72ea25 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/Users/clo/dev/one/evidence/m10/tree-rust-candidates-01/cross-container", + "expected_pages": 12, + "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/outline-edit/rust-tree/cross-container/cold/scripts/cold.ps1 b/corpus/outline-edit/rust-tree/cross-container/cold/scripts/cold.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..9b4c65b63ddad6e65d7acb85fb7d0accbb23d030 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/scripts/cold.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/cold.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/scripts/read.ps1 b/corpus/outline-edit/rust-tree/cross-container/cold/scripts/read.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..113263f9c23ecc3cafa21407510de560fdc964a8 --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/scripts/read.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/read.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/source.json b/corpus/outline-edit/rust-tree/cross-container/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..bf22c131e13b2aa41a8cfe992cc5e019e5d9a0fd --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/source.json @@ -0,0 +1,8 @@ +[ + { + "path": "synthetic.one", + "bytes": 92512, + "sha256": "91c80122b3854e8a91e863638b55c1b8610b80c71c59a2436b3ddae60338c75a", + "mtime_ns": 1788897469441332033 + } +] diff --git a/corpus/outline-edit/rust-tree/cross-container/cold/teardown.json b/corpus/outline-edit/rust-tree/cross-container/cold/teardown.json new file mode 120000 index 0000000000000000000000000000000000000000..dbca0643416e3c7b16e1f46f95489ea4415f20fd --- /dev/null +++ b/corpus/outline-edit/rust-tree/cross-container/cold/teardown.json @@ -0,0 +1 @@ +../../../../paragraph-edit/joined/teardown.json \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/groups-cells/candidate/synthetic.one b/corpus/outline-edit/rust-tree/groups-cells/candidate/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..6566dab9dc28e2136352c0bb2f02fb7446f74bb5 GIT binary patch literal 98760 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 Q5Eu;sTp@s5tij4302>HCI{*Lx literal 0 HcmV?d00001 diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/notebook/synthetic.one b/corpus/outline-edit/rust-tree/groups-cells/cold/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..5b3437570a419c01a7ef7ddeb84769ccafb7e160 GIT binary patch literal 102488 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 literal 0 HcmV?d00001 diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/environment.json b/corpus/outline-edit/rust-tree/groups-cells/cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..fa3d5f00a201d8d86e1dd94d9be96094a215eae6 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-619E88D9", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/hierarchy.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..a8dcfce17fab6bb24cca1f6b4bc2cfe1a30c9fa3 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-000.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..db08775828dd0133d8125c5a037ebfa047d92dcf --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-000.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-001.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..0f9262125344d1057884578e058230efab79f9c6 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-001.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-002.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..ff21a6fde4724f07434d586c90d83101fc35b2b2 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-002.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-003.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..201763c2ce7654f2d6a867cca3635467b3fe0eaa --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-003.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-004.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd592b4fef07256367f6f84f536c41c6ddec90e2 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-004.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-005.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..2946522ab566512e71e1615e82bd601c389560aa --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-005.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-006.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..3f74bfa17af2995f9dd27636796768ee361b41f7 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-006.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-007.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..95b02301a98f8f4924394d24bfdf4cd76b3211ed --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-007.xml @@ -0,0 +1,20 @@ + +Bold 🦀 italic é color 東京
+End
]]>
Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-008.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..e50414c2acad1e7a36ca1caa691a4181f4a4c04d --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-008.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-009.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-009.xml new file mode 100644 index 0000000000000000000000000000000000000000..9582e0deecd8018cea6080f361aeb38c993c3f93 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-009.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-010.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-010.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd9000f0bee61a241bc7c85ea0289a1f73f4e751 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-010.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-011.xml b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-011.xml new file mode 100644 index 0000000000000000000000000000000000000000..389ffdea9c7cb200e43fdb5ce5206a5153947175 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/page-011.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/read/payloads.json b/corpus/outline-edit/rust-tree/groups-cells/cold/read/payloads.json new file mode 120000 index 0000000000000000000000000000000000000000..28e766c3d4f4499afc8ee14682bf4467ff3087cd --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/read/payloads.json @@ -0,0 +1 @@ +../../../../../paragraph-edit/before/read/payloads.json \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/run.json b/corpus/outline-edit/rust-tree/groups-cells/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..428a01ae9b4b6eb37942dde909dd2412695e95cc --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/Users/clo/dev/one/evidence/m10/tree-rust-candidates-01/groups-cells", + "expected_pages": 12, + "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/outline-edit/rust-tree/groups-cells/cold/scripts/cold.ps1 b/corpus/outline-edit/rust-tree/groups-cells/cold/scripts/cold.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..9b4c65b63ddad6e65d7acb85fb7d0accbb23d030 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/scripts/cold.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/cold.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/scripts/read.ps1 b/corpus/outline-edit/rust-tree/groups-cells/cold/scripts/read.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..113263f9c23ecc3cafa21407510de560fdc964a8 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/scripts/read.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/read.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/source.json b/corpus/outline-edit/rust-tree/groups-cells/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..3e65b796310a5ba0e8274b5410ceede230fb0a69 --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/source.json @@ -0,0 +1,8 @@ +[ + { + "path": "synthetic.one", + "bytes": 98760, + "sha256": "1d61348e067f63ac3e39443085fbc1e8285ef52873b0191943ec439168f48e41", + "mtime_ns": 1788896902499834185 + } +] diff --git a/corpus/outline-edit/rust-tree/groups-cells/cold/teardown.json b/corpus/outline-edit/rust-tree/groups-cells/cold/teardown.json new file mode 120000 index 0000000000000000000000000000000000000000..dbca0643416e3c7b16e1f46f95489ea4415f20fd --- /dev/null +++ b/corpus/outline-edit/rust-tree/groups-cells/cold/teardown.json @@ -0,0 +1 @@ +../../../../paragraph-edit/joined/teardown.json \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/ordinary/candidate/synthetic.one b/corpus/outline-edit/rust-tree/ordinary/candidate/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..47d51d27fe692fdea4c2026c4183612fc8067e2d GIT binary patch literal 111728 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 Dm67uf literal 0 HcmV?d00001 diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/environment.json b/corpus/outline-edit/rust-tree/ordinary/cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..c72b5ea584b5c4ebc30d8d970e90d874b83a83ab --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-91CEBD14", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/hierarchy.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..49a89381a4e252d93b8d90b2ff795683cb4a7953 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-000.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..7687a4c4e040e131d68d58b64d5293e15cb63ac4 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-000.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-001.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc862d0fb532801ee3be19eb3d80e0b04e60bb14 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-001.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-002.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..ad2bb8b3fa7cea17212f8c2035864c69ee925ef3 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-002.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-003.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..a1444695ac1defa80b42b81b2c30fef545d3ffe5 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-003.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-004.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..32479a5d91e6118de92dd81d097e5c99d0413d17 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-004.xml @@ -0,0 +1,4 @@ + +Target 🦀 é Link label]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-005.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..f098b46c264643471a1340ad3d4848582c6a0e7a --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-005.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-006.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..2adc7cbb4d9c962df21fc7da2f5c04e7a1b45090 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-006.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-007.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..2f02916167e0887177741a4ed40c306f7bf50b46 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-007.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-008.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..a3186b01c1c23c7ca1c98caf1e2a968e4f201322 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-008.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-009.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-009.xml new file mode 100644 index 0000000000000000000000000000000000000000..0c84ea1722fea42860f245cc11b3245139d1a6bf --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-009.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-010.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-010.xml new file mode 100644 index 0000000000000000000000000000000000000000..3866428f5a817ef1243afe16e991ae29f0e3494b --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-010.xml @@ -0,0 +1,20 @@ + +Bold 🦀 italic é color 東京
+End
]]>
Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-011.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-011.xml new file mode 100644 index 0000000000000000000000000000000000000000..3b9cdb947c29ab1b0dde96613b0164cdda0df301 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-011.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-012.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-012.xml new file mode 100644 index 0000000000000000000000000000000000000000..a8a05324f192996c4722be06d1db6855c52240f2 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-012.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-013.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-013.xml new file mode 100644 index 0000000000000000000000000000000000000000..329a9944172b3b8d2a7dbc224bc7fffb026083e5 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-013.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/page-014.xml b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-014.xml new file mode 100644 index 0000000000000000000000000000000000000000..258f36ba7104ea483588e1e148ad937cbff19aa5 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/page-014.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/read/payloads.json b/corpus/outline-edit/rust-tree/ordinary/cold/read/payloads.json new file mode 120000 index 0000000000000000000000000000000000000000..28e766c3d4f4499afc8ee14682bf4467ff3087cd --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/read/payloads.json @@ -0,0 +1 @@ +../../../../../paragraph-edit/before/read/payloads.json \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/run.json b/corpus/outline-edit/rust-tree/ordinary/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..1f1379224ca10dd65ab2b1faf8d3de8bd81ed764 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/Users/clo/dev/one/evidence/m10/tree-rust-candidates-01/ordinary", + "expected_pages": 15, + "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/outline-edit/rust-tree/ordinary/cold/scripts/cold.ps1 b/corpus/outline-edit/rust-tree/ordinary/cold/scripts/cold.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..9b4c65b63ddad6e65d7acb85fb7d0accbb23d030 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/scripts/cold.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/cold.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/scripts/read.ps1 b/corpus/outline-edit/rust-tree/ordinary/cold/scripts/read.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..113263f9c23ecc3cafa21407510de560fdc964a8 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/scripts/read.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/read.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/source.json b/corpus/outline-edit/rust-tree/ordinary/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..37f8ce43c5ae661fe1edb020bb08d815df0366c1 --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/source.json @@ -0,0 +1,8 @@ +[ + { + "path": "synthetic.one", + "bytes": 111728, + "sha256": "73cdf92d1e016cb7576a8e9165bf0d94fb94e5a1e233a834dd39347011661240", + "mtime_ns": 1788896901734213394 + } +] diff --git a/corpus/outline-edit/rust-tree/ordinary/cold/teardown.json b/corpus/outline-edit/rust-tree/ordinary/cold/teardown.json new file mode 120000 index 0000000000000000000000000000000000000000..dbca0643416e3c7b16e1f46f95489ea4415f20fd --- /dev/null +++ b/corpus/outline-edit/rust-tree/ordinary/cold/teardown.json @@ -0,0 +1 @@ +../../../../paragraph-edit/joined/teardown.json \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/unequal-groups/candidate/groups.one b/corpus/outline-edit/rust-tree/unequal-groups/candidate/groups.one new file mode 100644 index 0000000000000000000000000000000000000000..7bb4ee7a42dcff4c1b872caa717bfd0c2f79bf79 GIT binary patch literal 8784 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 L1dxj}Sh)iL9K}MX literal 0 HcmV?d00001 diff --git a/corpus/outline-edit/rust-tree/unequal-groups/cold/notebook/groups.one b/corpus/outline-edit/rust-tree/unequal-groups/cold/notebook/groups.one new file mode 100644 index 0000000000000000000000000000000000000000..37b1231df119c5495e2d0d234aa8ec194cd6d437 GIT binary patch literal 8784 zcmeHM4Nw)=6}}G?L_txX2=b5zybv`eL;NL@mcJ(osL*ICBr#%;4*oRK2pO@Bu85R| zm|#sT8dS6~L5T_(G#DNHfect{$0#;bCJ`OSXgf@B6l$soJ>TxVk7e2S1ln|_Gn|?G z?(RMJoIUs4bG~zT-;L*9`t@6lAI&{fF@Md4@!KvfEM9%ZZOS*Z4mHGX3VZ(UWTv6; zuXcF)ZfT3%cVYCFmij}D6Ns;FVtNM=t_O+%-ivWD++=JCVy%Z~?#dZ`{M)v_)VcXs zuT>3ExariH-IePF)P?Zzd}YY?n;2-jlrM}&7ys*E<>=Y(9QgXkpJQ(ycjfGktuK3~ zA2CGlPCoJ8!%=mA?<&>(u5$dIVWM^C_K);?;Q!wP2F4_$m@^A8F;kj}d7Dkl;z=V9 zIu^OqHl3ZBb18G<%FN~)Y-||Lv8yy=AB0*%Q^=(oHw3z4yu~2G*skt&2*9C496gmF@s53d!Ya{ww_57H#hFu{G03Z@?5K*@vlu?EqKa| z&R=D`ypRQ_oYm87yD1OR-Goc+m2TcVE!41|ETSJsuoF?qDV?*Wr3c&@7NU z?S$c`G@$0jla<!nE`!htZde=zABLi!*FzelqrY$f^m74pq8y2tJ@TV9Wp@07g07QABB zC$GB49=;|0^tESBUd7Y@le{ivHh9w)|n= zO(}Ui-0uxcj^L9+h>z`@i^=d;Q+s@Ma$9A~ixsE$?n;YX2Ov^syB3Gw7Z7kri;vI; z0uEg-$XXx#Sn!+;7wd}dtPR^Pc*=~}D38 zuXT>y%)$z^o{~#EjqCf1BxJld&_&NMU-lAm2Fq1QgZUT~{sMe};IEUH5?FoJ^n{(R zMKN33-(JyIe_2QuthgCdToQRS%=*dW>o$IWxbCtzCnb8wk7R(%F2KiKp%*u(Q_wkv zj6zfD6zUS{6zY|zRj5C5t&-PIKawx~=+`Mk+;Z z+&@0N=;_k#AD#0s|4vn4>63Vt9u8xiF<|Z*6mX(OYI<2@4|BbJmc&E8l<#mk$?HXx zJ^Ada``aO3&lg`s@_JskC;$7ZDZ_Dw?2ylqrx093W?GoQZ*RIsav<)}V}H@ZUx0h~ z!0Y@|=_rO6H}T$&df*o3K`rJTe7d@3EcJk5e`>%jGw(4EW0<$TJol*p>Y*=?_d(P{ z&O!}xkMSOIPf&wgTUmyD=BLYk>i0mu2m0UvbuEGTk5fkgmka2<4R3+!D$-d;fF~f- zkv+?gzy88=QzP;YHXJzlcK$`=y1I^Jyt4GYO{aH66{arw=)Bvhwnx>G5`YGgZXIb< zG5b(QfPWy^y)yre5v7BRqfbm*-g5Sf!P~TUYwMh_TYEl5{lgK-3_v6@Q?tFP;OyJu z^At{oy*p1~|4{>Qo-)u)&(2hMj^p=eQFl=%K=4;->#grMEH=+6+H-yG>?qUJzWNJ( z#MbGZFJPHAUc~vLguhfI(Hzt)+Y6?HbCy^Kq6W{z`QgKgiNPM{YZBgHurI~cuXr!k zp?9XhxN?5*aRdd45>K zlh#ZN6XyrSkAcpIPSk#k_`!Jsg!lQ@}?grhVweUf63oBZRc#9l|Wec(&?*?{LXqa zve+|He@ zurg|vUrfP*h)J;tXPkR#uQs2Bz4a=4k%K+4SJ;KT1tuVG4d zGpa|#-$;%N#8Yco9qx_X08c+Ul4Ukq8N|FqxNDe}XFBL5A)^y+p# zvS(MXa1#%6fb+s%z)gfaaFY$sGY-!kIPZXE{+#S{>*BFSJuJnzf}2JBm|R8odZ=O# zx0YQEOapEv@?&Eo$?IjeJ$doHMdFdvO!9isZcko(Z;|+p0=M;| + diff --git a/corpus/outline-edit/rust-tree/unequal-groups/cold/read/page-000.xml b/corpus/outline-edit/rust-tree/unequal-groups/cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..3ffaeb7aa3efc4353d34446e2982ed2e4248e1c8 --- /dev/null +++ b/corpus/outline-edit/rust-tree/unequal-groups/cold/read/page-000.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/outline-edit/rust-tree/unequal-groups/cold/read/payloads.json b/corpus/outline-edit/rust-tree/unequal-groups/cold/read/payloads.json new file mode 120000 index 0000000000000000000000000000000000000000..28e766c3d4f4499afc8ee14682bf4467ff3087cd --- /dev/null +++ b/corpus/outline-edit/rust-tree/unequal-groups/cold/read/payloads.json @@ -0,0 +1 @@ +../../../../../paragraph-edit/before/read/payloads.json \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/unequal-groups/cold/run.json b/corpus/outline-edit/rust-tree/unequal-groups/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..73aa039679d11bf9e8c68452fb08d8f395e37c7d --- /dev/null +++ b/corpus/outline-edit/rust-tree/unequal-groups/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/Users/clo/dev/one/evidence/m10/tree-rust-candidates-01/unequal-groups", + "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/outline-edit/rust-tree/unequal-groups/cold/scripts/cold.ps1 b/corpus/outline-edit/rust-tree/unequal-groups/cold/scripts/cold.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..9b4c65b63ddad6e65d7acb85fb7d0accbb23d030 --- /dev/null +++ b/corpus/outline-edit/rust-tree/unequal-groups/cold/scripts/cold.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/cold.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/unequal-groups/cold/scripts/read.ps1 b/corpus/outline-edit/rust-tree/unequal-groups/cold/scripts/read.ps1 new file mode 120000 index 0000000000000000000000000000000000000000..113263f9c23ecc3cafa21407510de560fdc964a8 --- /dev/null +++ b/corpus/outline-edit/rust-tree/unequal-groups/cold/scripts/read.ps1 @@ -0,0 +1 @@ +../../../../../paragraph-edit/reconciliation/keyboard/scripts/read.ps1 \ No newline at end of file diff --git a/corpus/outline-edit/rust-tree/unequal-groups/cold/source.json b/corpus/outline-edit/rust-tree/unequal-groups/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..81f3fa759ad854c31146ae958abe5d2a4bb104e8 --- /dev/null +++ b/corpus/outline-edit/rust-tree/unequal-groups/cold/source.json @@ -0,0 +1,8 @@ +[ + { + "path": "groups.one", + "bytes": 8784, + "sha256": "771ef9f745a621829ff616a6009c62f623b0132c31d78f8fc04258646916d49a", + "mtime_ns": 1788897850908407538 + } +] diff --git a/corpus/outline-edit/rust-tree/unequal-groups/cold/teardown.json b/corpus/outline-edit/rust-tree/unequal-groups/cold/teardown.json new file mode 120000 index 0000000000000000000000000000000000000000..dbca0643416e3c7b16e1f46f95489ea4415f20fd --- /dev/null +++ b/corpus/outline-edit/rust-tree/unequal-groups/cold/teardown.json @@ -0,0 +1 @@ +../../../../paragraph-edit/joined/teardown.json \ No newline at end of file diff --git a/crates/onestore/README.md b/crates/onestore/README.md index b8d720610fe1d6c48c165834b4b4449b087b7faf..d4ae6e6a8e6a3a2eb81c0c2ec42fac14f1a6ec21 100644 --- a/crates/onestore/README.md +++ b/crates/onestore/README.md @@ -53,6 +53,7 @@ harness also accepts `--client-profile release`. | `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 | +| `TreeEdit`, `PreparedEdit::tree` | Move or delete a subtree on one page, normalize surviving containers, and replace an emptied table cell's paragraph atomically | | `PreparedEdit::commit`, `PreparedEdit::commit_file` | Publish the exact prepared image under caller-held exclusion or the conservative filesystem adapter | | `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 | @@ -85,6 +86,17 @@ 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 +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 +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. +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 diff --git a/crates/onestore/src/commit.rs b/crates/onestore/src/commit.rs index 7bea20551acafc40915e9a770c2525df04da4073..4e3d2507a46971d073f8375baddd705eae20a8ed 100644 --- a/crates/onestore/src/commit.rs +++ b/crates/onestore/src/commit.rs @@ -183,6 +183,18 @@ pub struct PreparedEdit<'a> { } impl<'a> PreparedEdit<'a> { + /// 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], diff --git a/crates/onestore/src/edit.rs b/crates/onestore/src/edit.rs index 9cf5af2efde70468e991104a7f5e2c34b3d4d18f..89c4427f84f9e6578bdc92e82ad1af5fb82eaab9 100644 --- a/crates/onestore/src/edit.rs +++ b/crates/onestore/src/edit.rs @@ -1,6 +1,10 @@ -use crate::document::{Document, Kind}; -use crate::{Error, ExGuid, ObjectData, PropertySets, RevisionIndex, Store}; -use std::ops::Range; +use crate::{ + Error, ExGuid, Object, ObjectData, PropertySets, RevisionIndex, Store, + create::string, + document::{Document, Element, Kind}, + write::PropertyObject, +}; +use std::{collections::BTreeMap, ops::Range, sync::Arc}; fn title_line(text: &str) -> &str { text.trim_start().split('\r').next().unwrap() @@ -458,3 +462,49 @@ pub(crate) fn page_title( } Ok(Some((*page, automatic, title_text.to_owned()))) } + +pub(crate) fn update_title( + store: &Store<'_>, + raw: &crate::ResolvedRevision<'_>, + view: crate::document::Revision<'_>, + pages: &[ExGuid], + changed: &mut BTreeMap, +) -> Result<(), Error> { + let invalid = |message| Error { offset: 0, message }; + // Shorten the moved view's lifetime to the changed property buffers. + let mut view = view; + for (id, object) in changed.iter() { + view.nodes.insert( + *id, + Element::parse( + &Object { + jcid: object.jcid, + reference_count: 0, + data: ObjectData::Properties(&object.bytes), + global_ids: Arc::clone(&object.global_ids), + }, + store, + )?, + ); + } + let title = page_title(&view, pages, None)?; + drop(view); + if let Some((_, automatic, title)) = title { + let metadata = raw + .roots + .get(&2) + .ok_or_else(|| invalid("Page title metadata is unavailable"))?; + if raw.objects[metadata].jcid != 0x20030 { + return Err(invalid("Page title metadata is unavailable")); + } + let title = string(&title); + let mut object = PropertyObject::from_object(&raw.objects[metadata])?; + object.set(&[(0x1c001cf3, &title)])?; + changed.insert(*metadata, object); + changed + .get_mut(&pages[0]) + .unwrap() + .set(&[(0x1c001d3c, if automatic { &title } else { &[0, 0] })])?; + } + Ok(()) +} diff --git a/crates/onestore/src/lib.rs b/crates/onestore/src/lib.rs index a9c07773f79e5aeacf87c8175a1612e93878101d..c5e2f825aa45f9d0bdddb9e643ff4ab2451b2691 100644 --- a/crates/onestore/src/lib.rs +++ b/crates/onestore/src/lib.rs @@ -19,6 +19,7 @@ pub mod protected; mod revisions; mod snapshot; mod store; +mod tree; mod write; pub use commit::{ @@ -39,4 +40,5 @@ pub use properties::{IdStream, Property, PropertySets, Value}; pub use revisions::{ExGuid, ObjectSpace, Revision, RevisionIndex}; pub use snapshot::{read_snapshot, read_storage_snapshot}; pub use store::{Chunk, Error, FileType, Header, Node, NodeList, Reference, Store}; +pub use tree::TreeEdit; pub use write::replace_property_bytes; diff --git a/crates/onestore/src/paragraph.rs b/crates/onestore/src/paragraph.rs index 88b96ec981b077a4aef5eef3358577b812d17805..cdc8749091018e43582d70c34420beeb3f32eac6 100644 --- a/crates/onestore/src/paragraph.rs +++ b/crates/onestore/src/paragraph.rs @@ -2,7 +2,7 @@ use crate::{ Error, ExGuid, Object, ObjectData, PropertySets, RevisionIndex, Store, create::{current_timestamps, properties, string}, document::{Document, Element, Kind}, - edit::{editable_parents, page_title}, + edit::{editable_parents, update_title}, write::{PropertyObject, fresh_guid, write_revision}, }; use serde::{Deserialize, Serialize}; @@ -373,51 +373,6 @@ fn ordinary_text<'a>( Ok((text, runs)) } -fn update_title( - store: &Store<'_>, - raw: &crate::ResolvedRevision<'_>, - view: crate::document::Revision<'_>, - pages: &[ExGuid], - changed: &mut BTreeMap, -) -> Result<(), Error> { - // Shorten the moved view's lifetime to the changed property buffers. - let mut view = view; - for (id, object) in changed.iter() { - view.nodes.insert( - *id, - Element::parse( - &Object { - jcid: object.jcid, - reference_count: 0, - data: ObjectData::Properties(&object.bytes), - global_ids: Arc::clone(&object.global_ids), - }, - store, - )?, - ); - } - let title = page_title(&view, pages, None)?; - drop(view); - if let Some((_, automatic, title)) = title { - let metadata = raw - .roots - .get(&2) - .ok_or_else(|| invalid("Page title metadata is unavailable"))?; - if raw.objects[metadata].jcid != 0x20030 { - return Err(invalid("Page title metadata is unavailable")); - } - let title = string(&title); - let mut object = PropertyObject::from_object(&raw.objects[metadata])?; - object.set(&[(0x1c001cf3, &title)])?; - changed.insert(*metadata, object); - changed - .get_mut(&pages[0]) - .unwrap() - .set(&[(0x1c001d3c, if automatic { &title } else { &[0, 0] })])?; - } - Ok(()) -} - /// Joins adjacent ordinary text in one outline or table cell. /// The left paragraph survives. Empty left text adopts the right text identity; /// otherwise the left text survives. The left paragraph's tags are retained. diff --git a/crates/onestore/src/tree.rs b/crates/onestore/src/tree.rs new file mode 100644 index 0000000000000000000000000000000000000000..64a24dcf2624a172bba3fe8dcf631f39e35f420c --- /dev/null +++ b/crates/onestore/src/tree.rs @@ -0,0 +1,597 @@ +use crate::{ + Error, ExGuid, RevisionIndex, Store, + create::{current_timestamps, properties, string}, + document::{Document, Kind, Revision}, + edit::{editable_parents, update_title}, + write::{PropertyObject, fresh_guid, write_revision}, +}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeMap, BTreeSet}, + sync::Arc, +}; + +fn invalid(message: &'static str) -> Error { + Error { offset: 0, message } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +enum Placement { + Delete, + Move { + parent: ExGuid, + before: Option, + }, +} + +/// 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 { + guid: [u8; 16], + object: ExGuid, + placement: Placement, + author: String, + created: u32, +} + +impl TreeEdit { + /// 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 { + 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( + object: ExGuid, + parent: ExGuid, + before: Option, + author: &str, + ) -> Result { + Self::new(object, Placement::Move { parent, before }, author) + } + + fn new(object: ExGuid, placement: Placement, author: &str) -> Result { + let intent = Self { + guid: fresh_guid()?, + object, + placement, + author: author.to_owned(), + created: current_timestamps()?.0, + }; + intent.validate()?; + Ok(intent) + } + + fn validate(&self) -> Result<(), Error> { + if self.guid == [0; 16] || self.object.guid == [0; 16] || self.author.contains('\0') { + return Err(invalid( + "Choose page content and an author name without NUL", + )); + } + if let Placement::Move { parent, before } = self.placement + && (parent.guid == [0; 16] || before.is_some_and(|id| id.guid == [0; 16])) + { + return Err(invalid("Choose a destination and sibling on the same page")); + } + Ok(()) + } + + pub(crate) fn apply(&self, source: &[u8], space: ExGuid) -> Result, Error> { + self.validate()?; + let store = Store::parse(source)?; + let index = RevisionIndex::parse(&store)?; + index.validate_current()?; + let mut document = Document::parse(&index)?; + let pages: Vec<_> = document + .pages()? + .into_iter() + .filter_map(|(sid, page)| (sid == space).then_some(page)) + .collect(); + let [page] = pages.as_slice() else { + return Err(invalid("Tree editing requires a single active page")); + }; + let semantic = document.spaces.remove(&space).unwrap(); + let rid = semantic.contexts[&ExGuid::default()]; + let mut view = semantic + .revisions + .into_iter() + .find_map(|(id, view)| (id == rid).then_some(view)) + .unwrap(); + let parents = editable_parents(&view, &pages, self.object)?; + let path = checked_path(&view, &parents, *page, self.object)?; + let source_parent = *path + .get(1) + .ok_or_else(|| invalid("Select a paragraph or ordinary page outline"))?; + let compatible = |parent: ExGuid| match view.nodes[&self.object].kind { + Kind::Outline { .. } => parent == *page, + Kind::Paragraph { .. } => matches!( + view.nodes[&parent].kind, + Kind::Outline { .. } + | Kind::OutlineGroup + | Kind::Paragraph { .. } + | Kind::Cell { .. } + ), + _ => false, + }; + if !compatible(source_parent) || !view.nodes[&source_parent].children.contains(&self.object) + { + return Err(invalid("Select a paragraph or ordinary page outline")); + } + let mut changed_ids = BTreeSet::from([source_parent]); + let mut affected = BTreeSet::from([self.object]); + if let Placement::Move { parent, before } = self.placement { + if checked_path(&view, &parents, *page, parent)?.contains(&self.object) { + return Err(invalid("A subtree cannot move inside itself")); + } + let destination = &view.nodes[&parent]; + if !compatible(parent) || before.is_some_and(|id| !destination.children.contains(&id)) { + return Err(invalid( + "Choose a compatible destination and one of its direct children", + )); + } + let previous = destination.children.clone(); + if parent == source_parent && before == Some(self.object) { + return Ok(source.to_vec()); + } + view.nodes + .get_mut(&source_parent) + .unwrap() + .children + .retain(|id| *id != self.object); + let children = &mut view.nodes.get_mut(&parent).unwrap().children; + let position = before + .map(|id| children.iter().position(|child| *child == id).unwrap()) + .unwrap_or(children.len()); + children.insert(position, self.object); + if parent == source_parent && *children == previous { + return Ok(source.to_vec()); + } + changed_ids.insert(parent); + } else { + view.nodes + .get_mut(&source_parent) + .unwrap() + .children + .retain(|id| *id != self.object); + } + + let mut empty_cell = None; + let mut at = source_parent; + loop { + let node = &view.nodes[&at]; + if matches!(node.kind, Kind::Page { .. } | Kind::Paragraph { .. }) + && node.children.is_empty() + { + break; + } + if node.children.is_empty() { + if matches!(node.kind, Kind::Cell { .. }) { + empty_cell = Some(at); + break; + } + if !matches!(node.kind, Kind::Outline { .. } | Kind::OutlineGroup) { + return Err(invalid("This container cannot be emptied by a tree edit")); + } + if node.extra[0].iter().any(|field| field.id == 0x08001d0c) { + return Err(invalid("The emptied container cannot be deleted")); + } + let path = checked_path(&view, &parents, *page, at)?; + let parent = path[1]; + view.nodes + .get_mut(&parent) + .unwrap() + .children + .retain(|id| *id != at); + changed_ids.remove(&at); + changed_ids.insert(parent); + at = parent; + continue; + } + if matches!( + node.kind, + Kind::Outline { .. } | Kind::OutlineGroup | Kind::Paragraph { .. } + ) && node + .children + .iter() + .all(|id| matches!(view.nodes[id].kind, Kind::OutlineGroup)) + { + let groups = node.children.clone(); + let minimum = groups + .iter() + .map(|id| view.nodes[id].child_level.unwrap_or(1)) + .min() + .unwrap(); + let level = node + .child_level + .unwrap_or(1) + .checked_add(minimum) + .filter(|level| *level <= 31) + .ok_or_else(|| { + invalid("Removing this group would exceed 31 child indentation levels") + })?; + let mut children = Vec::new(); + for group in groups { + affected.insert(group); + let group_node = view.nodes.get_mut(&group).unwrap(); + let relative = group_node.child_level.unwrap_or(1) - minimum; + if relative == 0 { + if group_node.extra[0] + .iter() + .any(|field| field.id == 0x08001d0c) + { + return Err(invalid("The redundant outline group cannot be deleted")); + } + children.extend_from_slice(&group_node.children); + changed_ids.remove(&group); + } else { + group_node.child_level = Some(relative); + children.push(group); + changed_ids.insert(group); + } + } + let node = view.nodes.get_mut(&at).unwrap(); + node.children = children; + node.child_level = Some(level); + changed_ids.insert(at); + continue; + } + break; + } + + let mut pending: Vec<_> = affected.into_iter().collect(); + let mut checked = BTreeSet::new(); + while let Some(id) = pending.pop() { + if !checked.insert(id) { + continue; + } + checked_path(&view, &parents, *page, id)?; + let node = &view.nodes[&id]; + if matches!(self.placement, Placement::Delete) + && node.extra[0].iter().any(|field| field.id == 0x08001d0c) + { + return Err(invalid( + "The selected subtree contains content that cannot be deleted", + )); + } + pending.extend( + node.children + .iter() + .chain(&node.content) + .chain(&node.structure) + .copied(), + ); + } + let raw = index.resolve(space, rid)?; + let active_parents = editable_parents(&view, &pages, *page)?; + let modified = current_timestamps()?.0.to_le_bytes(); + let mut changed = BTreeMap::new(); + let author_id = ExGuid { + 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 { + changed.insert( + author_id, + PropertyObject { + jcid: 0x120001, + bytes: properties(&[(0x1c001d75, string(&self.author))])?, + global_ids: Arc::new(BTreeMap::from([(0, self.guid)])), + }, + ); + } + if let Some(author) = changed.get(&author_id) + && raw.objects.get(&author_id).is_some_and(|old| { + old.jcid == author.jcid && old.data == crate::ObjectData::Properties(&author.bytes) + }) + { + changed.remove(&author_id); + } + if changed.keys().any(|id| raw.objects.contains_key(id)) { + return Err(invalid( + "A tree-edit identity already exists; reconcile the existing edit", + )); + } + let mut ancestors = BTreeSet::new(); + for id in &changed_ids { + ancestors.extend(checked_path(&view, &active_parents, *page, *id)?); + } + for id in ancestors { + let mut object = PropertyObject::from_object(&raw.objects[&id])?; + if changed_ids.contains(&id) { + let node = &view.nodes[&id]; + let mut references = Vec::new(); + for child in &node.children { + references.extend_from_slice(&object.reference(*child)?); + } + object.set(&[(0x24001c20, &references)])?; + if let Some(level) = node.child_level { + object.set(&[(0x0c001c03, &[level])])?; + } + } + object.set(&[(0x14001d7a, &modified)])?; + changed.insert(id, object); + } + if moved_paragraph { + let mut object = PropertyObject::from_object(&raw.objects[&self.object])?; + let author = object.reference(author_id)?; + object.set(&[(0x20001d79, &author), (0x14001d7a, &modified)])?; + changed.insert(self.object, object); + } + update_title(&store, &raw, view, &pages, &mut changed)?; + write_revision(source, space, |_| Ok(changed)) + } +} + +fn checked_path( + view: &Revision<'_>, + parents: &BTreeMap>, + page: ExGuid, + object: ExGuid, +) -> Result, Error> { + let mut path = Vec::new(); + let mut seen = BTreeSet::new(); + let mut id = object; + loop { + if !seen.insert(id) { + return Err(invalid("Tree ancestry contains a cycle")); + } + let node = view + .nodes + .get(&id) + .ok_or_else(|| invalid("The selected page content is unavailable"))?; + if matches!(node.kind, Kind::Title) + || node.extra[0] + .iter() + .any(|field| matches!(field.id, 0x88001cb4 | 0x88001cf9 | 0x88001cb2 | 0x88001cde)) + { + return Err(invalid( + "Title or protected content cannot be moved or deleted here", + )); + } + path.push(id); + if id == page { + break; + } + let [parent] = parents.get(&id).map(Vec::as_slice).unwrap_or_default() else { + return Err(invalid("Tree content must have one active parent")); + }; + id = *parent; + } + Ok(path) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Insertion, PreparedEdit}; + + #[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 space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + 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 space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + assert_eq!(view.nodes[&outline].children, [second.object(), first]); + assert_eq!( + view.nodes[&first].latest_author, + Some(ExGuid { + guid: retained.guid, + n: 3 + }) + ); + } + + #[test] + fn group_normalization_preserves_unequal_indentation_and_overlapping_moves() { + let source = crate::create_section("groups.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 space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + 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 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 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)] { + let mut group = PropertyObject { + jcid: 0x60019, + bytes: properties(&[(0x0c001c03, vec![level])])?, + global_ids: Arc::new(BTreeMap::from([(0, guid)])), + }; + let reference = group.reference(child)?; + group.set(&[(0x24001c20, &reference)])?; + changed.insert(id, group); + } + let mut object = PropertyObject::from_object(&raw.objects[&outline])?; + let mut references = Vec::new(); + for id in [a, b, third.object()] { + references.extend_from_slice(&object.reference(id)?); + } + object.set(&[(0x24001c20, &references)])?; + changed.insert(outline, object); + 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 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()]]; + 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(); + } + } + } + + #[test] + fn protected_descendants_and_ambiguous_parents_reject_before_publication() { + let source = crate::create_section("protected.one", "Text", "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 space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + let outline = *view.nodes[&page] + .children + .iter() + .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) + .unwrap(); + let paragraph = view.nodes[&outline].children[0]; + let text = view.nodes[¶graph].content[0]; + let deletion = TreeEdit::delete(paragraph, "Author").unwrap(); + for target in [page, outline, paragraph, text] { + for property in [0x08001cde, 0x08001cb4, 0x08001cf9, 0x08001cb2] { + for enabled in [false, true] { + let bytes = write_revision(&source, sid, |raw| { + let mut object = PropertyObject::from_object(&raw.objects[&target])?; + object.set(&[(property | (u32::from(enabled) << 31), &[])])?; + Ok(BTreeMap::from([(target, object)])) + }) + .unwrap(); + assert_eq!( + PreparedEdit::tree(&bytes, sid, &deletion).is_err(), + enabled, + "{target}: {property:#x}" + ); + } + } + } + for protected in [outline, paragraph, text] { + let bytes = write_revision(&source, sid, |raw| { + let mut object = PropertyObject::from_object(&raw.objects[&protected])?; + object.set(&[(0x08001d0c, &[])])?; + Ok(BTreeMap::from([(protected, object)])) + }) + .unwrap(); + assert!(PreparedEdit::tree(&bytes, sid, &deletion).is_err()); + } + let bytes = write_revision(&source, sid, |raw| { + let mut object = PropertyObject::from_object(&raw.objects[&outline])?; + let reference = object.reference(paragraph)?; + object.set(&[(0x24001c20, &reference.repeat(2))])?; + 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()); + } +} diff --git a/crates/onestore/tests/support/tree_model.rs b/crates/onestore/tests/support/tree_model.rs new file mode 100644 index 0000000000000000000000000000000000000000..117a41157772e63c51134b0323e30c187d2444bb --- /dev/null +++ b/crates/onestore/tests/support/tree_model.rs @@ -0,0 +1,364 @@ +use onestore::{ + CommitState, ExGuid, Insertion, PreparedEdit, RevisionIndex, Store, TreeEdit, + document::{Document, Kind, Revision}, +}; +use std::{ + collections::{BTreeMap, BTreeSet}, + sync::LazyLock, +}; + +use crate::{current, disk}; + +static SOURCE: LazyLock> = LazyLock::new(|| { + let mut 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(); + let (sid, page) = document.pages().unwrap()[0]; + let space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + let outline = *view.nodes[&page] + .children + .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(); + } + } + source +}); + +fn forest(view: &Revision<'_>, root: ExGuid) -> BTreeMap { + let mut positions = BTreeMap::new(); + let mut pending = vec![(root, root, 0)]; + while let Some((id, mut scope, mut level)) = pending.pop() { + let node = &view.nodes[&id]; + if matches!(node.kind, Kind::Outline { .. } | Kind::Cell { .. }) { + scope = id; + level = 0; + } + assert!( + positions + .insert(id, (scope, level, positions.len())) + .is_none() + ); + pending.extend( + node.children + .iter() + .rev() + .map(|id| (*id, scope, level + u32::from(node.child_level.unwrap_or(1)))), + ); + pending.extend(node.content.iter().rev().map(|id| (*id, scope, level))); + } + positions +} + +pub fn run(input: &[u8]) { + let Some((&selector, input)) = input.split_first() else { + return; + }; + let source = if selector & 1 == 0 { + SOURCE.as_slice() + } else { + include_bytes!("../../../../corpus/outline-edit/tree/before/notebook/synthetic.one") + }; + let mut persisted = source.to_vec(); + let mut clients = vec![persisted.clone(); 12]; + for step in input.chunks_exact(10).take(36) { + let client = usize::from(step[0]) % clients.len(); + if step[1] & 1 != 0 { + clients[client] = persisted.clone(); + } + let source = &clients[client]; + let store = Store::parse(source).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let pages = document.pages().unwrap(); + let (sid, page) = pages[usize::from(step[2]) % pages.len()]; + let space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + let before = forest(view, page); + let mut targets: Vec<_> = before + .keys() + .copied() + .filter(|id| { + matches!( + view.nodes[id].kind, + Kind::Paragraph { .. } | Kind::Outline { .. } + ) + }) + .collect(); + targets.sort_by_key(|id| before[id].2); + if targets.is_empty() { + continue; + } + let target = targets[usize::from(step[3]) % targets.len()]; + let subtree = forest(view, target); + let parents: BTreeMap<_, _> = before + .keys() + .flat_map(|id| view.nodes[id].children.iter().map(|child| (*child, *id))) + .collect(); + let parent = parents[&target]; + 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) + } else { + let mut destinations: Vec<_> = before + .keys() + .copied() + .filter(|id| { + if matches!(view.nodes[&target].kind, Kind::Outline { .. }) { + *id == page + } else { + matches!( + view.nodes[id].kind, + Kind::Outline { .. } + | Kind::OutlineGroup + | 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 + .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 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 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 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); + assert_eq!( + serde_json::to_value(&node.tags).unwrap(), + serde_json::to_value(&view.nodes[id].tags).unwrap() + ); + } + if matches!( + node.kind, + Kind::Outline { .. } | Kind::OutlineGroup | Kind::Cell { .. } + ) { + assert!( + node.children + .iter() + .any(|id| matches!(after.nodes[id].kind, Kind::Paragraph { .. })) + ); + } + } + 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 { + visible: persisted.clone(), + durable: persisted.clone(), + operation: 0, + fail_at: (step[6] != 0).then_some(usize::from(u16::from_le_bytes([step[6], step[7]]))), + write_limit: if step[8] & 1 == 0 { 17 } else { 4096 }, + random: u64::from(step[9]) + 1, + }; + let result = edit.commit(&mut disk); + let actual = current::current(&disk.durable); + match result { + Ok(()) => assert_eq!(actual, after), + Err(error) => { + assert!(actual == before || actual == after); + match error.state { + CommitState::NotCommitted => assert_eq!(actual, before), + CommitState::Committed => assert_eq!(actual, after), + CommitState::Unknown => {} + } + } + } + persisted = disk.durable; + } +} diff --git a/crates/onestore/tests/tree.rs b/crates/onestore/tests/tree.rs new file mode 100644 index 0000000000000000000000000000000000000000..13d30afbc15613b51832d82b13f66bddeca68376 --- /dev/null +++ b/crates/onestore/tests/tree.rs @@ -0,0 +1,520 @@ +use onestore::{ + ExGuid, PreparedEdit, RevisionIndex, Store, TreeEdit, + document::{Document, Kind, Revision}, +}; +use std::collections::{BTreeMap, BTreeSet}; + +#[path = "support/current.rs"] +mod current; +#[path = "support/disk.rs"] +mod disk; +#[path = "support/tree_model.rs"] +mod tree_model; + +const FIXTURES: [(&[u8], &[u8]); 2] = [ + ( + include_bytes!("../../../corpus/outline-edit/before/notebook/synthetic.one"), + include_bytes!("../../../corpus/outline-edit/after/notebook/synthetic.one"), + ), + ( + include_bytes!("../../../corpus/outline-edit/tree/before/notebook/synthetic.one"), + include_bytes!("../../../corpus/outline-edit/tree/after/notebook/synthetic.one"), + ), +]; + +#[test] +fn twelve_client_tree_schedules_preserve_content_and_indentation() { + let mut random = 1932_u64; + for _ in 0..80 { + let input: Vec<_> = (0..241) + .map(|_| { + random ^= random << 13; + random ^= random >> 7; + random ^= random << 17; + random as u8 + }) + .collect(); + tree_model::run(&input); + } +} + +fn active(view: &Revision<'_>, page: ExGuid) -> BTreeSet { + let mut pending = vec![page]; + let mut ids = BTreeSet::new(); + while let Some(id) = pending.pop() { + assert!(ids.insert(id), "Repeated active identity: {id}"); + let node = &view.nodes[&id]; + pending.extend( + node.children + .iter() + .chain(&node.content) + .chain(&node.structure) + .copied(), + ); + } + ids +} + +fn operation(view: &Revision<'_>, page: ExGuid) -> Option<(String, ExGuid, TreeEdit)> { + let Kind::Metadata { + title: Some(name), .. + } = &view.nodes[&view.roots[&2]].kind + else { + panic!() + }; + let ids = active(view, page); + let paragraphs: BTreeMap<_, _> = ids + .iter() + .filter_map(|id| { + let node = &view.nodes[id]; + let text = node.content.first()?; + match &view.nodes[text].kind { + Kind::RichText { text, .. } if matches!(node.kind, Kind::Paragraph { .. }) => { + Some((text.as_str(), *id)) + } + _ => None, + } + }) + .collect(); + let target = *paragraphs + .iter() + .find(|(text, _)| text.starts_with("Target "))? + .1; + let outline = *view.nodes[&page] + .children + .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 edit = match name.as_str() { + "Delete outline" => TreeEdit::delete(outline, "Tree author"), + name if name.starts_with("Delete") => TreeEdit::delete(target, "Tree author"), + "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") + } + "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" { + outline + } else { + target + }, + edit, + )) +} + +#[test] +fn native_subtree_controls_match_with_preserved_fields_and_history() { + let mut count = 0; + for (source, native) in FIXTURES { + let store = Store::parse(source).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let native_store = Store::parse(native).unwrap(); + let native_index = RevisionIndex::parse(&native_store).unwrap(); + let native_document = Document::parse(&native_index).unwrap(); + let mut candidate = source.to_vec(); + for (sid, page) in document.pages().unwrap() { + let space = &document.spaces[&sid]; + let before = &space.revisions[&space.contexts[&ExGuid::default()]]; + 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 after_store = Store::parse(prepared.as_bytes()).unwrap(); + assert!(after_store.checksum_mismatches.is_empty()); + let after_index = RevisionIndex::parse(&after_store).unwrap(); + after_index.validate_current().unwrap(); + let after_document = Document::parse(&after_index).unwrap(); + let space = &after_document.spaces[&sid]; + let after = &space.revisions[&space.contexts[&ExGuid::default()]]; + let space = &native_document.spaces[&sid]; + let reference = &space.revisions[&space.contexts[&ExGuid::default()]]; + let old_ids = active(before, page); + let actual = active(after, page); + let native_ids = active(reference, page); + assert_eq!( + actual.intersection(&old_ids).collect::>(), + native_ids.intersection(&old_ids).collect::>(), + "{name}" + ); + assert_eq!(actual.len(), native_ids.len(), "{name}"); + let mut mutable = BTreeSet::new(); + for view in [before, after] { + let ids = active(view, page); + if !ids.contains(&object) { + continue; + } + let mut pending = vec![object]; + while let Some(id) = pending.pop() { + mutable.insert(id); + pending.extend( + ids.iter() + .filter(|parent| { + view.nodes[parent] + .children + .iter() + .chain(&view.nodes[parent].content) + .chain(&view.nodes[parent].structure) + .any(|child| *child == id) + }) + .copied(), + ); + } + } + let mut remap = BTreeMap::new(); + if name == "Delete sole cell paragraph" { + for kind in ["Paragraph", "RichText"] { + let selected = |view: &Revision<'_>, ids: &BTreeSet| { + *ids.difference(&old_ids) + .find(|id| { + serde_json::to_value(&view.nodes[id].kind).unwrap()["type"] == kind + }) + .unwrap() + }; + remap.insert(selected(reference, &native_ids), selected(after, &actual)); + } + } + for id in &actual { + let reference_id = remap + .iter() + .find_map(|(a, b)| (b == id).then_some(*a)) + .unwrap_or(*id); + let wanted = &reference.nodes[&reference_id]; + let node = &after.nodes[id]; + for (a, b) in [ + (&node.children, &wanted.children), + (&node.content, &wanted.content), + (&node.structure, &wanted.structure), + ] { + assert_eq!( + *a, + b.iter() + .map(|id| remap.get(id).copied().unwrap_or(*id)) + .collect::>(), + "{name}: {id}" + ); + } + if !node.children.is_empty() { + assert_eq!(node.child_level, wanted.child_level, "{name}: {id}"); + } + if old_ids.contains(id) { + let old = &before.nodes[id]; + let mut expected = serde_json::to_value(old).unwrap(); + let mut actual = serde_json::to_value(node).unwrap(); + expected["children"] = actual["children"].clone(); + if !node.children.is_empty() { + expected["child_level"] = actual["child_level"].clone(); + } + if mutable.contains(id) + || node.child_level != old.child_level + || node.children != old.children + { + assert!(node.modified >= old.modified); + expected["modified"] = actual["modified"].clone(); + } + if *id == object + && !name.starts_with("Delete") + && matches!(node.kind, Kind::Paragraph { .. }) + { + expected["latest_author"] = actual["latest_author"].clone(); + assert!( + matches!(&after.nodes[&node.latest_author.unwrap()].kind,Kind::Author{name:Some(name)} if name=="Tree author") + ); + } + for value in [&mut expected, &mut actual] { + value["extra"][0] + .as_array_mut() + .unwrap() + .sort_by_key(|field| field["id"].as_u64().unwrap()); + } + assert_eq!(actual, expected, "{name}: preserved {id}"); + if matches!(old.kind, Kind::RichText { .. }) { + assert_eq!( + serde_json::to_value(node).unwrap(), + serde_json::to_value(old).unwrap(), + "{name}: text {id}" + ); + } + assert_eq!( + serde_json::to_value(&node.kind).unwrap(), + serde_json::to_value(&old.kind).unwrap(), + "{name}: kind {id}" + ); + assert_eq!( + serde_json::to_value(&node.tags).unwrap(), + 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() + ); + } + } + for (sid, space) in &index.spaces { + for rid in space.revisions.keys() { + let old = index.resolve(*sid, *rid).unwrap(); + let retained = after_index.resolve(*sid, *rid).unwrap(); + assert_eq!(old.roots, retained.roots); + for (id, object) in old.objects { + assert_eq!(object.data, retained.objects[&id].data); + } + } + } + let another = PreparedEdit::tree(source, sid, &edit).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(); + let space = &second_doc.spaces[&sid]; + assert_eq!( + actual, + active(&space.revisions[&space.contexts[&ExGuid::default()]], page) + ); + candidate = PreparedEdit::tree(&candidate, sid, &edit) + .unwrap() + .as_bytes() + .to_vec(); + count += 1; + } + if let Some(output) = std::env::var_os("ONESTORE_TREE_OUTPUT") { + let output = std::path::PathBuf::from(output).join(if source == FIXTURES[0].0 { + "ordinary" + } else { + "groups-cells" + }); + assert!(output.is_absolute()); + std::fs::create_dir_all(&output).unwrap(); + std::fs::write(output.join("synthetic.one"), candidate).unwrap(); + } + } + assert_eq!(count, 18); +} + +#[test] +fn moves_reject_cycles_wrong_parents_and_stale_siblings() { + let source = onestore::create_section("tree.one", "Text", "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 space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + let outline = *view.nodes[&page] + .children + .iter() + .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) + .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"), + ] { + assert!(PreparedEdit::tree(&source, sid, &intent.unwrap()).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 + ); + } +} + +#[test] +fn publication_interruptions_expose_complete_old_or_new_trees() { + let source = FIXTURES[1].0; + let store = Store::parse(source).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let before = current::current(source); + let mut count = 0; + for (sid, page) in document.pages().unwrap() { + let space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + let Some((name, _, intent)) = operation(view, page) else { + continue; + }; + if !matches!( + name.as_str(), + "Delete sole cell paragraph" + | "Delete unindented sibling after group" + | "Move cell subtree down" + ) { + continue; + } + let edit = PreparedEdit::tree(source, sid, &intent).unwrap(); + let after = current::current(edit.as_bytes()); + for write_limit in [17, 4096] { + let disk = |fail_at| disk::Disk { + visible: source.to_vec(), + durable: source.to_vec(), + operation: 0, + fail_at, + write_limit, + random: 1931, + }; + let mut success = disk(None); + edit.commit(&mut success).unwrap(); + assert_eq!(success.durable, edit.as_bytes()); + for at in 1..=success.operation { + let mut interrupted = disk(Some(at)); + let failure = edit.commit(&mut interrupted).unwrap_err(); + let actual = current::current(&interrupted.durable); + assert!(actual == before || actual == after, "{name}: {at}"); + match failure.state { + onestore::CommitState::NotCommitted => assert_eq!(actual, before), + onestore::CommitState::Committed => assert_eq!(actual, after), + onestore::CommitState::Unknown => {} + } + } + } + count += 1; + } + assert_eq!(count, 3); +} + +#[test] +fn cross_container_moves_keep_tables_and_replace_emptied_cells() { + let source = FIXTURES[1].0; + let store = Store::parse(source).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let mut candidate = source.to_vec(); + let mut count = 0; + for (sid, page) in document.pages().unwrap() { + let space = &document.spaces[&sid]; + let view = &space.revisions[&space.contexts[&ExGuid::default()]]; + let Kind::Metadata { + title: Some(name), .. + } = &view.nodes[&view.roots[&2]].kind + else { + panic!() + }; + if !matches!( + name.as_str(), + "Outdent first group" + | "Delete sole cell paragraph" + | "Move cell subtree down" + | "Delete cell subtree" + ) { + continue; + } + let ids = active(view, page); + let parents: BTreeMap<_, _> = ids + .iter() + .flat_map(|id| view.nodes[id].children.iter().map(|child| (*child, *id))) + .collect(); + let target=*ids.iter().find(|id| view.nodes[id].content.first().is_some_and(|text| matches!(&view.nodes[text].kind,Kind::RichText{text,..} if text.starts_with("Target ")))).unwrap(); + let outlines: Vec<_> = view.nodes[&page] + .children + .iter() + .copied() + .filter(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) + .collect(); + let cells: Vec<_> = ids + .iter() + .copied() + .filter(|id| matches!(view.nodes[id].kind, Kind::Cell { .. })) + .collect(); + let (object, destination) = match name.as_str() { + "Outdent first group" => (*view.nodes[&outlines[0]].children.last().unwrap(), target), + "Delete sole cell paragraph" => ( + target, + *cells.iter().find(|id| **id != parents[&target]).unwrap(), + ), + "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 store = Store::parse(edit.as_bytes()).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let space = &document.spaces[&sid]; + let after = &space.revisions[&space.contexts[&ExGuid::default()]]; + assert_eq!(after.nodes[&destination].children.last(), Some(&object)); + let actual = active(after, page); + for id in ids.intersection(&actual) { + let old = &view.nodes[id]; + let node = &after.nodes[id]; + assert_eq!(node.content, old.content, "{name}: {id}"); + assert_eq!( + serde_json::to_value(&node.kind).unwrap(), + serde_json::to_value(&old.kind).unwrap() + ); + if matches!( + node.kind, + Kind::RichText { .. } | Kind::Table { .. } | Kind::Row | Kind::Cell { .. } + ) { + assert_eq!( + serde_json::to_value(&node.format).unwrap(), + serde_json::to_value(&old.format).unwrap() + ); + assert_eq!( + serde_json::to_value(&node.layout).unwrap(), + serde_json::to_value(&old.layout).unwrap() + ); + } + } + match name.as_str() { + "Outdent first group" => { + assert_eq!(after.nodes[&outlines[0]].children, [target]); + assert_eq!(after.nodes[&outlines[0]].child_level, Some(2)); + assert!(!actual.contains(&parents[&target])); + } + "Delete sole cell paragraph" => { + let old_cell = parents[&target]; + let replacement = after.nodes[&old_cell].children[0]; + assert!(!ids.contains(&replacement)); + assert_eq!(after.nodes[&old_cell].children.len(), 1); + let text = after.nodes[&replacement].content[0]; + assert!( + matches!(&after.nodes[&text].kind,Kind::RichText{text,..} if text.is_empty()) + ); + } + "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(); + count += 1; + } + assert_eq!(count, 4); + if let Some(output) = std::env::var_os("ONESTORE_TREE_OUTPUT") { + let output = std::path::PathBuf::from(output).join("cross-container"); + assert!(output.is_absolute()); + std::fs::create_dir_all(output.parent().unwrap()).unwrap(); + std::fs::create_dir(&output).unwrap(); + std::fs::write(output.join("synthetic.one"), candidate).unwrap(); + } +} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 8030aaced5f9bd27b8999e7e3a26402a8d929126..ba1cb1402bc1864d3c6c46ed5cd72ef809231bdd 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -100,3 +100,10 @@ path = "fuzz_targets/offline_paragraph.rs" test = false doc = false bench = false + +[[bin]] +name = "tree" +path = "fuzz_targets/tree.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/tree.rs b/fuzz/fuzz_targets/tree.rs new file mode 100644 index 0000000000000000000000000000000000000000..0b566cdd27345282e9f25b454c0e6ba0e46b2398 --- /dev/null +++ b/fuzz/fuzz_targets/tree.rs @@ -0,0 +1,11 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +#[path = "../../crates/onestore/tests/support/current.rs"] +mod current; +#[path = "../../crates/onestore/tests/support/disk.rs"] +mod disk; +#[path = "../../crates/onestore/tests/support/tree_model.rs"] +mod tree_model; + +fuzz_target!(|input: &[u8]| tree_model::run(input)); diff --git a/tools/test_outline_edit.py b/tools/test_outline_edit.py index 6cfb508c200184ed6d9f84cd1542ae3f009ab5ed..fe7a48d6c203e31b2e2be004b74e7285e8eb9fd7 100644 --- a/tools/test_outline_edit.py +++ b/tools/test_outline_edit.py @@ -19,6 +19,36 @@ compare = runpy.run_path(str(ROOT / 'tools/verify-document.py'))['compare'] class OutlineEditTest(unittest.TestCase): + def test_rust_subtree_moves_and_deletions_survive_cold_native_reopen(self): + for name, pages in [('ordinary', 15), ('groups-cells', 12), ('cross-container', 12), ('unequal-groups', 1)]: + fixture = FIXTURE / 'rust-tree' / name + with self.subTest(case=name), TemporaryDirectory() as temporary: + models = {} + for phase, notebook in [('candidate', fixture / 'candidate'), ('cold', fixture / 'cold/notebook')]: + folder = Path(temporary) / phase + shutil.copytree(fixture / 'cold/read', folder / 'read') + compare(notebook, folder / 'read') + section, = notebook.glob('*.one') + subprocess.run([EXPORTER, section, folder / 'model'], check=True) + model = json.loads((folder / 'model/document.json').read_text()) + models[phase] = {sid: (r, page) for sid, _, r, page in ordered_pages(model)} + self.assertEqual(len(models[phase]), pages) + self.assertEqual(models['candidate'].keys(), models['cold'].keys()) + for sid, (old, page) in models['candidate'].items(): + saved, saved_page = models['cold'][sid] + self.assertEqual(page, saved_page) + self.assertEqual(dict(walk(old, page)), dict(walk(saved, page))) + self.assertEqual(old['nodes'][old['roots']['2']]['kind'], saved['nodes'][saved['roots']['2']]['kind']) + for _, node in walk(old, page): + for oid in node['kind'].get('lists', []): + self.assertEqual(old['nodes'][oid], saved['nodes'][oid]) + if name == 'unequal-groups': + capture, = (fixture / 'cold/read').glob('page-*.xml') + root = ET.parse(capture).getroot() + self.assertEqual([(group.get('indent'), ''.join(Text(group.find('one:OE/one:T', ns).text or '').parts)) + for group in root.findall('one:Outline/one:OEChildren', ns)], + [('3', 'First'), ('2', 'Second')]) + def test_native_group_list_and_cell_tree_controls(self): fixture = FIXTURE / 'tree' models = {} -- 2.54.0