| author | |
| committer | |
| log | 550d71c9d8ee053ba3bf16b0269e6bdac3352136 |
| tree | 00fe54abd2b48de1005ee20753f5d3325f078998 |
| parent | 3bc26d2f339e409b94776447c6d0cd661d5da6ff |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover) |
Use the same final edit FILETIME for equivalent page lowerings, keeping revision metadata equality exact. Check timestamp and author separately at writer checkpoints and outline/tree edits while preserving untouched fields, identities and historical bytes.
Assisted-by: gpt-6.1-sol4 files changed, 92 insertions(+), 20 deletions(-)
crates/onestore/tests/outline.rs+32-4| ... | @@ -9,6 +9,8 @@ use std::collections::BTreeSet; | ... | @@ -9,6 +9,8 @@ use std::collections::BTreeSet; |
| 9 | mod current; | 9 | mod current; |
| 10 | #[path = "support/disk.rs"] | 10 | #[path = "support/disk.rs"] |
| 11 | mod disk; | 11 | mod disk; |
| 12 | #[path = "support/metadata.rs"] | ||
| 13 | mod metadata; | ||
| 12 | #[path = "support/ops.rs"] | 14 | #[path = "support/ops.rs"] |
| 13 | mod ops; | 15 | mod ops; |
| 14 | #[path = "support/sweep.rs"] | 16 | #[path = "support/sweep.rs"] |
| ... | @@ -67,6 +69,7 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { | ... | @@ -67,6 +69,7 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { |
| 67 | for (name, sid, object, edit) in cases(&document) { | 69 | for (name, sid, object, edit) in cases(&document) { |
| 68 | let restored = serde_json::from_value(serde_json::to_value(edit).unwrap()).unwrap(); | 70 | let restored = serde_json::from_value(serde_json::to_value(edit).unwrap()).unwrap(); |
| 69 | assert_eq!(edit, restored); | 71 | assert_eq!(edit, restored); |
| 72 | let started = ops::now(); | ||
| 70 | let prepared = ops::page_op( | 73 | let prepared = ops::page_op( |
| 71 | SOURCE, | 74 | SOURCE, |
| 72 | sid, | 75 | sid, |
| ... | @@ -76,6 +79,7 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { | ... | @@ -76,6 +79,7 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { |
| 76 | }, | 79 | }, |
| 77 | ) | 80 | ) |
| 78 | .unwrap(); | 81 | .unwrap(); |
| 82 | let finished = ops::now(); | ||
| 79 | let after_store = Store::parse(prepared.as_bytes()).unwrap(); | 83 | let after_store = Store::parse(prepared.as_bytes()).unwrap(); |
| 80 | assert!(after_store.checksum_mismatches.is_empty()); | 84 | assert!(after_store.checksum_mismatches.is_empty()); |
| 81 | let after_index = RevisionIndex::parse(&after_store).unwrap(); | 85 | let after_index = RevisionIndex::parse(&after_store).unwrap(); |
| ... | @@ -100,15 +104,33 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { | ... | @@ -100,15 +104,33 @@ fn geometry_and_expansion_preserve_unrelated_properties_objects_and_history() { |
| 100 | .then_some(*parent) | 104 | .then_some(*parent) |
| 101 | })); | 105 | })); |
| 102 | } | 106 | } |
| 103 | assert_eq!( | 107 | let old_raw = index.resolve_active(sid).unwrap(); |
| 104 | before.nodes.keys().collect::<Vec<_>>(), | 108 | let new_raw = after_index.resolve_active(sid).unwrap(); |
| 105 | after.nodes.keys().collect::<Vec<_>>() | 109 | let at = metadata::check(&old_raw, &new_raw, "Author"); |
| 110 | assert!((started..=finished).contains(&at)); | ||
| 111 | let author = metadata::author(&new_raw.objects[&new_raw.roots[&4]]); | ||
| 112 | assert!( | ||
| 113 | after | ||
| 114 | .nodes | ||
| 115 | .keys() | ||
| 116 | .all(|id| before.nodes.contains_key(id) || *id == after.roots[&4] || *id == author) | ||
| 106 | ); | 117 | ); |
| 107 | for (id, node) in &before.nodes { | 118 | for (id, node) in &before.nodes { |
| 119 | if Some(id) == before.roots.get(&4) { | ||
| 120 | continue; | ||
| 121 | } | ||
| 108 | let mut expected = serde_json::to_value(node).unwrap(); | 122 | let mut expected = serde_json::to_value(node).unwrap(); |
| 109 | let actual = serde_json::to_value(&after.nodes[id]).unwrap(); | 123 | let actual = serde_json::to_value(&after.nodes[id]).unwrap(); |
| 110 | if changed.contains(id) { | 124 | if changed.contains(id) { |
| 111 | expected["modified"] = actual["modified"].clone(); | 125 | expected["modified"] = actual["modified"].clone(); |
| 126 | if matches!(node.kind, Kind::Paragraph { .. }) { | ||
| 127 | assert_eq!(after.nodes[id].latest_author, Some(author)); | ||
| 128 | assert_eq!( | ||
| 129 | after.nodes[id].modified.map(u64::from), | ||
| 130 | Some(at / 10_000_000 - 11_960_006_400) | ||
| 131 | ); | ||
| 132 | expected["latest_author"] = actual["latest_author"].clone(); | ||
| 133 | } | ||
| 112 | } | 134 | } |
| 113 | if *id == object { | 135 | if *id == object { |
| 114 | match edit { | 136 | match edit { |
| ... | @@ -344,6 +366,7 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content | ... | @@ -344,6 +366,7 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content |
| 344 | (144.0, 36.0, "First"), | 366 | (144.0, 36.0, "First"), |
| 345 | (0.0, 144.0, "First"), | 367 | (0.0, 144.0, "First"), |
| 346 | ] { | 368 | ] { |
| 369 | let started = ops::now(); | ||
| 347 | let edit = ops::page_op( | 370 | let edit = ops::page_op( |
| 348 | &source, | 371 | &source, |
| 349 | sid, | 372 | sid, |
| ... | @@ -353,12 +376,17 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content | ... | @@ -353,12 +376,17 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content |
| 353 | }, | 376 | }, |
| 354 | ) | 377 | ) |
| 355 | .unwrap(); | 378 | .unwrap(); |
| 379 | let finished = ops::now(); | ||
| 356 | let saved = Store::parse(edit.as_bytes()).unwrap(); | 380 | let saved = Store::parse(edit.as_bytes()).unwrap(); |
| 357 | let saved_index = RevisionIndex::parse(&saved).unwrap(); | 381 | let saved_index = RevisionIndex::parse(&saved).unwrap(); |
| 358 | saved_index.validate_current().unwrap(); | 382 | saved_index.validate_current().unwrap(); |
| 359 | let saved_document = Document::parse(&saved_index).unwrap(); | 383 | let saved_document = Document::parse(&saved_index).unwrap(); |
| 360 | let saved_space = &saved_document.spaces[&sid]; | 384 | let saved_space = &saved_document.spaces[&sid]; |
| 361 | let after = &saved_space.revisions[&saved_space.contexts[&ExGuid::default()]]; | 385 | let after = &saved_space.revisions[&saved_space.contexts[&ExGuid::default()]]; |
| 386 | let old_raw = index.resolve_active(sid).unwrap(); | ||
| 387 | let new_raw = saved_index.resolve_active(sid).unwrap(); | ||
| 388 | let at = metadata::check(&old_raw, &new_raw, "Author"); | ||
| 389 | assert!((started..=finished).contains(&at)); | ||
| 362 | let metadata = before.roots[&2]; | 390 | let metadata = before.roots[&2]; |
| 363 | assert!( | 391 | assert!( |
| 364 | matches!(&after.nodes[&metadata].kind, Kind::Metadata { title: Some(title), .. } if title == expected) | 392 | matches!(&after.nodes[&metadata].kind, Kind::Metadata { title: Some(title), .. } if title == expected) |
| ... | @@ -371,7 +399,7 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content | ... | @@ -371,7 +399,7 @@ fn outline_movement_updates_both_automatic_title_fields_without_changing_content |
| 371 | after.nodes.keys().collect::<Vec<_>>() | 399 | after.nodes.keys().collect::<Vec<_>>() |
| 372 | ); | 400 | ); |
| 373 | for (oid, node) in &before.nodes { | 401 | for (oid, node) in &before.nodes { |
| 374 | if ![page, metadata, insertion].contains(oid) { | 402 | if ![page, metadata, insertion, before.roots[&4]].contains(oid) { |
| 375 | assert_eq!( | 403 | assert_eq!( |
| 376 | serde_json::to_value(node).unwrap(), | 404 | serde_json::to_value(node).unwrap(), |
| 377 | serde_json::to_value(&after.nodes[oid]).unwrap() | 405 | serde_json::to_value(&after.nodes[oid]).unwrap() |
crates/onestore/tests/page_writer.rs+32-5| ... | @@ -1,6 +1,8 @@ | ... | @@ -1,6 +1,8 @@ |
| 1 | //! A page edit lowered whole (`op::lower_page`) must publish the same graph its ops applied | 1 | //! A page edit lowered whole (`op::lower_page`) must publish the same graph its ops applied |
| 2 | //! one by one publish, in one transaction, and must round-trip the model it was given. | 2 | //! one by one publish, in one transaction, and must round-trip the model it was given. |
| 3 | 3 | ||
| 4 | #[path = "support/metadata.rs"] | ||
| 5 | mod metadata; | ||
| 4 | #[path = "support/ops.rs"] | 6 | #[path = "support/ops.rs"] |
| 5 | mod ops; | 7 | mod ops; |
| 6 | 8 | ||
| ... | @@ -164,7 +166,7 @@ fn same_element(revision_a: &Revision<'_>, revision_b: &Revision<'_>, id: ExGuid | ... | @@ -164,7 +166,7 @@ fn same_element(revision_a: &Revision<'_>, revision_b: &Revision<'_>, id: ExGuid |
| 164 | } | 166 | } |
| 165 | 167 | ||
| 166 | /// Every active revision the ops produced one by one is reproduced by the page lowered whole, | 168 | /// Every active revision the ops produced one by one is reproduced by the page lowered whole, |
| 167 | /// ignoring writer-allocated immutable styles and author records. Modification times are | 169 | /// ignoring writer-allocated immutable styles and author records. Paragraph modification times are |
| 168 | /// not compared: a reorder can be attributed to either sibling, and the model cannot say which. | 170 | /// not compared: a reorder can be attributed to either sibling, and the model cannot say which. |
| 169 | fn equivalent(reference: &[u8], written: &[u8]) { | 171 | fn equivalent(reference: &[u8], written: &[u8]) { |
| 170 | let store_a = Store::parse(reference).unwrap(); | 172 | let store_a = Store::parse(reference).unwrap(); |
| ... | @@ -201,11 +203,29 @@ fn equivalent(reference: &[u8], written: &[u8]) { | ... | @@ -201,11 +203,29 @@ fn equivalent(reference: &[u8], written: &[u8]) { |
| 201 | } | 203 | } |
| 202 | } | 204 | } |
| 203 | 205 | ||
| 204 | /// Lowers the page the ops produced whole and checks both contracts. | 206 | /// Both lowerings use the last op's FILETIME so revision metadata compares exactly. |
| 205 | fn oracle(source: &[u8], space: ExGuid, reference: &[u8]) -> Vec<u8> { | 207 | fn oracle(source: &[u8], space: ExGuid, reference: &[u8]) -> Vec<u8> { |
| 206 | let after = page_in(reference, space); | 208 | let after = page_in(reference, space); |
| 207 | let prepared = ops::saved(source, space, &after).unwrap(); | 209 | let store = Store::parse(reference).unwrap(); |
| 208 | let written = prepared.as_slice().to_vec(); | 210 | let index = RevisionIndex::parse(&store).unwrap(); |
| 211 | let revision = index.resolve_active(space).unwrap(); | ||
| 212 | let source_store = Store::parse(source).unwrap(); | ||
| 213 | let source_index = RevisionIndex::parse(&source_store).unwrap(); | ||
| 214 | let original = source_index.resolve_active(space).unwrap(); | ||
| 215 | let at = metadata::check(&original, &revision, AUTHOR); | ||
| 216 | let arena = onestore::Arena::default(); | ||
| 217 | let mut section = onestore::Section::open(&arena, source.to_vec()).unwrap(); | ||
| 218 | let before = section.page(space).unwrap(); | ||
| 219 | let ops = onestore::op::lower_page(&before, &after) | ||
| 220 | .unwrap() | ||
| 221 | .into_iter() | ||
| 222 | .map(|op| onestore::op::Op::Page { space, op }) | ||
| 223 | .collect(); | ||
| 224 | section | ||
| 225 | .apply(AUTHOR, &onestore::op::Edit { at, ops }) | ||
| 226 | .unwrap(); | ||
| 227 | section.seal().unwrap().unwrap(); | ||
| 228 | let written = section.image(); | ||
| 209 | assert_eq!(page_in(&written, space), after, "model round trip"); | 229 | assert_eq!(page_in(&written, space), after, "model round trip"); |
| 210 | equivalent(reference, &written); | 230 | equivalent(reference, &written); |
| 211 | let transactions = |bytes: &[u8]| Store::parse(bytes).unwrap().header.transaction_count; | 231 | let transactions = |bytes: &[u8]| Store::parse(bytes).unwrap().header.transaction_count; |
| ... | @@ -583,7 +603,9 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { | ... | @@ -583,7 +603,9 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { |
| 583 | ); | 603 | ); |
| 584 | outline.paragraphs.push(paragraph); | 604 | outline.paragraphs.push(paragraph); |
| 585 | after.objects.push(PageObject::Outline(outline)); | 605 | after.objects.push(PageObject::Outline(outline)); |
| 606 | let started = ops::now(); | ||
| 586 | let written = ops::saved(TREES, space, &after).unwrap(); | 607 | let written = ops::saved(TREES, space, &after).unwrap(); |
| 608 | let finished = ops::now(); | ||
| 587 | let stored = page_in(written.as_slice(), space); | 609 | let stored = page_in(written.as_slice(), space); |
| 588 | let normalize = |page: &Page| -> Vec<(ExGuid, Vec<(ExGuid, String)>)> { | 610 | let normalize = |page: &Page| -> Vec<(ExGuid, Vec<(ExGuid, String)>)> { |
| 589 | body(page) | 611 | body(page) |
| ... | @@ -616,6 +638,8 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { | ... | @@ -616,6 +638,8 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { |
| 616 | let store = Store::parse(written.as_slice()).unwrap(); | 638 | let store = Store::parse(written.as_slice()).unwrap(); |
| 617 | let index = RevisionIndex::parse(&store).unwrap(); | 639 | let index = RevisionIndex::parse(&store).unwrap(); |
| 618 | let raw_after = index.resolve_active(space).unwrap(); | 640 | let raw_after = index.resolve_active(space).unwrap(); |
| 641 | let at = metadata::check(&raw_before, &raw_after, AUTHOR); | ||
| 642 | assert!((started..=finished).contains(&at)); | ||
| 619 | let touched: BTreeSet<ExGuid> = body(&after) | 643 | let touched: BTreeSet<ExGuid> = body(&after) |
| 620 | .iter() | 644 | .iter() |
| 621 | .flat_map(|o| { | 645 | .flat_map(|o| { |
| ... | @@ -629,7 +653,10 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { | ... | @@ -629,7 +653,10 @@ fn model_edits_round_trip_and_leave_other_objects_untouched() { |
| 629 | .collect(); | 653 | .collect(); |
| 630 | let mut untouched = 0; | 654 | let mut untouched = 0; |
| 631 | for (id, object) in &raw_before.objects { | 655 | for (id, object) in &raw_before.objects { |
| 632 | if touched.contains(id) || matches!(object.jcid, 0x12004d | 0x120001) { | 656 | if touched.contains(id) |
| 657 | || Some(id) == raw_before.roots.get(&4) | ||
| 658 | || matches!(object.jcid, 0x12004d | 0x120001) | ||
| 659 | { | ||
| 633 | continue; | 660 | continue; |
| 634 | } | 661 | } |
| 635 | let after = &raw_after.objects[id]; | 662 | let after = &raw_after.objects[id]; |
crates/onestore/tests/tree.rs+17-9| ... | @@ -9,6 +9,8 @@ use std::collections::{BTreeMap, BTreeSet}; | ... | @@ -9,6 +9,8 @@ use std::collections::{BTreeMap, BTreeSet}; |
| 9 | mod current; | 9 | mod current; |
| 10 | #[path = "support/disk.rs"] | 10 | #[path = "support/disk.rs"] |
| 11 | mod disk; | 11 | mod disk; |
| 12 | #[path = "support/metadata.rs"] | ||
| 13 | mod metadata; | ||
| 12 | #[path = "support/ops.rs"] | 14 | #[path = "support/ops.rs"] |
| 13 | mod ops; | 15 | mod ops; |
| 14 | #[path = "support/sweep.rs"] | 16 | #[path = "support/sweep.rs"] |
| ... | @@ -191,8 +193,10 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { | ... | @@ -191,8 +193,10 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { |
| 191 | .into_iter() | 193 | .into_iter() |
| 192 | .map(|op| Op::Page { space: sid, op }) | 194 | .map(|op| Op::Page { space: sid, op }) |
| 193 | .collect(); | 195 | .collect(); |
| 196 | let started = ops::now(); | ||
| 194 | let prepared = ops::apply(source, "Tree author", edit.clone()) | 197 | let prepared = ops::apply(source, "Tree author", edit.clone()) |
| 195 | .unwrap_or_else(|error| panic!("{name}: {error}")); | 198 | .unwrap_or_else(|error| panic!("{name}: {error}")); |
| 199 | let finished = ops::now(); | ||
| 196 | let after_store = Store::parse(prepared.as_bytes()).unwrap(); | 200 | let after_store = Store::parse(prepared.as_bytes()).unwrap(); |
| 197 | assert!(after_store.checksum_mismatches.is_empty()); | 201 | assert!(after_store.checksum_mismatches.is_empty()); |
| 198 | let after_index = RevisionIndex::parse(&after_store).unwrap(); | 202 | let after_index = RevisionIndex::parse(&after_store).unwrap(); |
| ... | @@ -200,6 +204,11 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { | ... | @@ -200,6 +204,11 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { |
| 200 | let after_document = Document::parse(&after_index).unwrap(); | 204 | let after_document = Document::parse(&after_index).unwrap(); |
| 201 | let space = &after_document.spaces[&sid]; | 205 | let space = &after_document.spaces[&sid]; |
| 202 | let after = &space.revisions[&space.contexts[&ExGuid::default()]]; | 206 | let after = &space.revisions[&space.contexts[&ExGuid::default()]]; |
| 207 | let old_raw = index.resolve_active(sid).unwrap(); | ||
| 208 | let new_raw = after_index.resolve_active(sid).unwrap(); | ||
| 209 | let at = metadata::check(&old_raw, &new_raw, "Tree author"); | ||
| 210 | assert!((started..=finished).contains(&at)); | ||
| 211 | let author = metadata::author(&new_raw.objects[&new_raw.roots[&4]]); | ||
| 203 | let space = &native_document.spaces[&sid]; | 212 | let space = &native_document.spaces[&sid]; |
| 204 | let reference = &space.revisions[&space.contexts[&ExGuid::default()]]; | 213 | let reference = &space.revisions[&space.contexts[&ExGuid::default()]]; |
| 205 | let old_ids = active(before, page); | 214 | let old_ids = active(before, page); |
| ... | @@ -284,15 +293,14 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { | ... | @@ -284,15 +293,14 @@ fn native_subtree_controls_match_with_preserved_fields_and_history() { |
| 284 | { | 293 | { |
| 285 | assert!(node.modified >= old.modified); | 294 | assert!(node.modified >= old.modified); |
| 286 | expected["modified"] = actual["modified"].clone(); | 295 | expected["modified"] = actual["modified"].clone(); |
| 287 | } | 296 | if matches!(node.kind, Kind::Paragraph { .. }) { |
| 288 | if *id == object | 297 | assert_eq!(node.latest_author, Some(author)); |
| 289 | && !name.starts_with("Delete") | 298 | assert_eq!( |
| 290 | && matches!(node.kind, Kind::Paragraph { .. }) | 299 | node.modified.map(u64::from), |
| 291 | { | 300 | Some(at / 10_000_000 - 11_960_006_400) |
| 292 | expected["latest_author"] = actual["latest_author"].clone(); | 301 | ); |
| 293 | assert!( | 302 | expected["latest_author"] = actual["latest_author"].clone(); |
| 294 | matches!(&after.nodes[&node.latest_author.unwrap()].kind,Kind::Author{name:Some(name)} if name=="Tree author") | 303 | } |
| 295 | ); | ||
| 296 | } | 304 | } |
| 297 | for value in [&mut expected, &mut actual] { | 305 | for value in [&mut expected, &mut actual] { |
| 298 | value["extra"][0] | 306 | value["extra"][0] |
crates/onestore/tests/writer.rs+11-2| ... | @@ -1,3 +1,5 @@ | ... | @@ -1,3 +1,5 @@ |
| 1 | #[path = "support/metadata.rs"] | ||
| 2 | mod metadata; | ||
| 1 | #[path = "support/ops.rs"] | 3 | #[path = "support/ops.rs"] |
| 2 | mod ops; | 4 | mod ops; |
| 3 | 5 | ||
| ... | @@ -390,6 +392,7 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { | ... | @@ -390,6 +392,7 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { |
| 390 | let store = Store::parse(&bytes).unwrap(); | 392 | let store = Store::parse(&bytes).unwrap(); |
| 391 | let before = RevisionIndex::parse(&store).unwrap(); | 393 | let before = RevisionIndex::parse(&store).unwrap(); |
| 392 | let rid = before.spaces[&sid].labels[&(ExGuid::default(), 1)]; | 394 | let rid = before.spaces[&sid].labels[&(ExGuid::default(), 1)]; |
| 395 | let at = 134_000_000_000_000_000 + u64::from(operation) * 10_000_000; | ||
| 393 | let written = if section { | 396 | let written = if section { |
| 394 | let typed = |range, with: &str| Op::Page { | 397 | let typed = |range, with: &str| Op::Page { |
| 395 | space: sid, | 398 | space: sid, |
| ... | @@ -400,7 +403,6 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { | ... | @@ -400,7 +403,6 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { |
| 400 | }, | 403 | }, |
| 401 | }; | 404 | }; |
| 402 | // Modification times count seconds: each edit is a second later. | 405 | // Modification times count seconds: each edit is a second later. |
| 403 | let at = 134_000_000_000_000_000 + u64::from(operation) * 10_000_000; | ||
| 404 | let ops = vec![typed(0..0, "x"), typed(0..1, "")]; | 406 | let ops = vec![typed(0..0, "x"), typed(0..1, "")]; |
| 405 | let arena = onestore::Arena::default(); | 407 | let arena = onestore::Arena::default(); |
| 406 | let mut section = onestore::Section::open(&arena, bytes.clone()).unwrap(); | 408 | let mut section = onestore::Section::open(&arena, bytes.clone()).unwrap(); |
| ... | @@ -428,6 +430,9 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { | ... | @@ -428,6 +430,9 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { |
| 428 | assert_preserved_committed_bytes(&bytes, &written); | 430 | assert_preserved_committed_bytes(&bytes, &written); |
| 429 | let old = before.resolve(sid, rid).unwrap(); | 431 | let old = before.resolve(sid, rid).unwrap(); |
| 430 | let current = after.resolve(sid, next).unwrap(); | 432 | let current = after.resolve(sid, next).unwrap(); |
| 433 | if section { | ||
| 434 | assert_eq!(metadata::check(&old, &current, "Author"), at); | ||
| 435 | } | ||
| 431 | assert_eq!(old.roots, current.roots); | 436 | assert_eq!(old.roots, current.roots); |
| 432 | assert_eq!(old.objects.len(), current.objects.len()); | 437 | assert_eq!(old.objects.len(), current.objects.len()); |
| 433 | for (id, object) in &old.objects { | 438 | for (id, object) in &old.objects { |
| ... | @@ -456,7 +461,11 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { | ... | @@ -456,7 +461,11 @@ fn checkpoints_bound_dependencies_and_preserve_native_objects_and_history() { |
| 456 | assert_eq!(a.len(), b.len()); | 461 | assert_eq!(a.len(), b.len()); |
| 457 | for (a, b) in a.iter().zip(b) { | 462 | for (a, b) in a.iter().zip(b) { |
| 458 | assert_eq!(a.id, b.id); | 463 | assert_eq!(a.id, b.id); |
| 459 | if a.id != property && !matches!(a.value, Value::References { .. }) | 464 | if a.id != property |
| 465 | && !(section | ||
| 466 | && Some(id) == old.roots.get(&4) | ||
| 467 | && a.id == 0x18001d77) | ||
| 468 | && !matches!(a.value, Value::References { .. }) | ||
| 460 | { | 469 | { |
| 461 | assert_eq!(a.value, b.value, "{id} {:#x}", a.id); | 470 | assert_eq!(a.value, b.value, "{id} {:#x}", a.id); |
| 462 | } | 471 | } |