1use crate::patch::types::{Patch, PatchId};
10use std::cell::RefCell;
11use std::collections::{HashMap, HashSet, VecDeque};
12use suture_common::BranchName;
13use thiserror::Error;
14
15#[derive(Error, Debug)]
17pub enum DagError {
18 #[error("patch already exists: {0}")]
19 DuplicatePatch(String),
20
21 #[error("patch not found: {0}")]
22 PatchNotFound(String),
23
24 #[error("parent patch not found: {0}")]
25 ParentNotFound(String),
26
27 #[error("would create a cycle: {from} -> {to}")]
28 CycleDetected { from: String, to: String },
29
30 #[error("branch not found: {0}")]
31 BranchNotFound(String),
32
33 #[error("branch already exists: {0}")]
34 BranchAlreadyExists(String),
35
36 #[error("empty branch name")]
37 EmptyBranchName,
38
39 #[error("invalid branch name: {0}")]
40 InvalidBranchName(String),
41
42 #[error("cannot create root patch with parents")]
43 RootWithParents,
44
45 #[error("{0}")]
46 Custom(String),
47}
48
49#[derive(Clone, Debug)]
51pub struct DagNode {
52 pub(crate) patch: Patch,
54 pub(crate) parent_ids: Vec<PatchId>,
56 pub(crate) child_ids: Vec<PatchId>,
58 pub(crate) generation: u64,
61}
62
63pub struct PatchDag {
71 pub(crate) nodes: HashMap<PatchId, DagNode>,
73 pub(crate) branches: HashMap<String, PatchId>,
75 ancestor_cache: RefCell<HashMap<PatchId, HashSet<PatchId>>>,
79}
80
81impl Default for PatchDag {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87impl PatchDag {
88 pub fn new() -> Self {
90 Self {
91 nodes: HashMap::new(),
92 branches: HashMap::new(),
93 ancestor_cache: RefCell::new(HashMap::new()),
94 }
95 }
96
97 pub fn add_patch(
110 &mut self,
111 patch: Patch,
112 parent_ids: Vec<PatchId>,
113 ) -> Result<PatchId, DagError> {
114 let id = patch.id;
115
116 if self.nodes.contains_key(&id) {
118 return Err(DagError::DuplicatePatch(id.to_hex()));
119 }
120
121 if !parent_ids.is_empty() {
123 for parent_id in &parent_ids {
124 if !self.nodes.contains_key(parent_id) {
125 return Err(DagError::ParentNotFound(parent_id.to_hex()));
126 }
127 }
128
129 }
134
135 let generation = if parent_ids.is_empty() {
137 0
138 } else {
139 parent_ids
140 .iter()
141 .map(|pid| self.nodes.get(pid).map(|n| n.generation).unwrap_or(0))
142 .max()
143 .unwrap_or(0)
144 + 1
145 };
146
147 let node = DagNode {
149 patch,
150 parent_ids: parent_ids.clone(),
151 child_ids: Vec::new(),
152 generation,
153 };
154
155 for parent_id in &parent_ids {
157 if let Some(parent_node) = self.nodes.get_mut(parent_id) {
158 parent_node.child_ids.push(id);
159 }
160 }
161
162 self.nodes.insert(id, node);
163 Ok(id)
164 }
165
166 pub fn get_patch(&self, id: &PatchId) -> Option<&Patch> {
168 self.nodes.get(id).map(|node| &node.patch)
169 }
170
171 pub fn get_node(&self, id: &PatchId) -> Option<&DagNode> {
173 self.nodes.get(id)
174 }
175
176 pub fn has_patch(&self, id: &PatchId) -> bool {
178 self.nodes.contains_key(id)
179 }
180
181 pub fn ancestors(&self, id: &PatchId) -> HashSet<PatchId> {
189 if let Some(cached) = self.ancestor_cache.borrow().get(id) {
191 return cached.clone();
192 }
193
194 let mut ancestors = HashSet::new();
196 let mut queue: VecDeque<PatchId> = VecDeque::new();
197
198 if let Some(node) = self.nodes.get(id) {
199 for parent_id in &node.parent_ids {
200 if !ancestors.contains(parent_id) {
201 ancestors.insert(*parent_id);
202 queue.push_back(*parent_id);
203 }
204 }
205 }
206
207 while let Some(current) = queue.pop_front() {
208 if let Some(node) = self.nodes.get(¤t) {
209 for parent_id in &node.parent_ids {
210 if ancestors.insert(*parent_id) {
211 queue.push_back(*parent_id);
212 }
213 }
214 }
215 }
216
217 self.ancestor_cache
219 .borrow_mut()
220 .insert(*id, ancestors.clone());
221 ancestors
222 }
223
224 pub fn lca(&self, a: &PatchId, b: &PatchId) -> Option<PatchId> {
232 if a == b {
233 return Some(*a);
234 }
235
236 let ancestors_a = self.ancestors(a);
237 let ancestors_b = self.ancestors(b);
238
239 if ancestors_b.contains(a) {
241 return Some(*a);
242 }
243 if ancestors_a.contains(b) {
245 return Some(*b);
246 }
247
248 let common: Vec<PatchId> = ancestors_a.intersection(&ancestors_b).copied().collect();
250
251 if common.is_empty() {
252 return None;
253 }
254
255 let mut best: Option<PatchId> = None;
258 let mut best_gen: u64 = 0;
259
260 for candidate in &common {
261 let candidate_gen = self.nodes.get(candidate).map(|n| n.generation).unwrap_or(0);
262 if candidate_gen >= best_gen {
263 best_gen = candidate_gen;
264 best = Some(*candidate);
265 }
266 }
267
268 best
269 }
270
271 #[allow(dead_code)]
277 fn ancestor_depth(&self, id: &PatchId) -> usize {
278 self.nodes
279 .get(id)
280 .map(|n| n.generation as usize)
281 .unwrap_or(0)
282 }
283
284 pub fn create_branch(&mut self, name: BranchName, target: PatchId) -> Result<(), DagError> {
286 let name_str = name.as_str().to_string();
287
288 if name_str.is_empty() {
289 return Err(DagError::EmptyBranchName);
290 }
291
292 if self.branches.contains_key(&name_str) {
293 return Err(DagError::BranchAlreadyExists(name_str));
294 }
295
296 if !self.nodes.contains_key(&target) {
297 return Err(DagError::PatchNotFound(target.to_hex()));
298 }
299
300 self.branches.insert(name_str, target);
301 Ok(())
302 }
303
304 pub fn get_branch(&self, name: &BranchName) -> Option<PatchId> {
306 self.branches.get(name.as_str()).copied()
307 }
308
309 pub fn update_branch(&mut self, name: &BranchName, target: PatchId) -> Result<(), DagError> {
311 if !self.branches.contains_key(name.as_str()) {
312 return Err(DagError::BranchNotFound(name.as_str().to_string()));
313 }
314 if !self.nodes.contains_key(&target) {
315 return Err(DagError::PatchNotFound(target.to_hex()));
316 }
317 self.branches.insert(name.as_str().to_string(), target);
318 Ok(())
319 }
320
321 pub fn delete_branch(&mut self, name: &BranchName) -> Result<(), DagError> {
323 if self.branches.remove(name.as_str()).is_none() {
324 return Err(DagError::BranchNotFound(name.as_str().to_string()));
325 }
326 Ok(())
327 }
328
329 pub fn list_branches(&self) -> Vec<(String, PatchId)> {
331 let mut branches: Vec<_> = self
332 .branches
333 .iter()
334 .map(|(name, id)| (name.clone(), *id))
335 .collect();
336 branches.sort_by(|a, b| a.0.cmp(&b.0));
337 branches
338 }
339
340 pub fn patch_count(&self) -> usize {
342 self.nodes.len()
343 }
344
345 pub fn patch_ids(&self) -> Vec<PatchId> {
347 self.nodes.keys().copied().collect()
348 }
349
350 pub fn patch_chain(&self, id: &PatchId) -> Vec<PatchId> {
352 let mut chain = Vec::new();
353 let mut current = Some(*id);
354
355 while let Some(curr_id) = current {
356 if chain.contains(&curr_id) {
357 break; }
359 chain.push(curr_id);
360 current = self
361 .nodes
362 .get(&curr_id)
363 .and_then(|n| n.parent_ids.first().copied());
364 }
365
366 chain
367 }
368
369 pub fn reachable_patches(&self, id: &PatchId) -> Vec<Patch> {
371 let ancestors = self.ancestors(id);
372 let mut patches = Vec::with_capacity(ancestors.len() + 1);
373
374 if let Some(node) = self.nodes.get(id) {
375 patches.push(node.patch.clone());
376 }
377
378 for ancestor_id in ancestors {
379 if let Some(node) = self.nodes.get(&ancestor_id) {
380 patches.push(node.patch.clone());
381 }
382 }
383
384 patches
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use crate::patch::types::{OperationType, Patch, TouchSet};
392 use suture_common::Hash;
393
394 fn make_patch(addr: &str) -> Patch {
395 Patch::new(
396 OperationType::Modify,
397 TouchSet::single(addr),
398 Some(format!("file_{}", addr)),
399 vec![],
400 vec![],
401 "test".to_string(),
402 format!("edit {}", addr),
403 )
404 }
405
406 #[test]
407 fn test_add_root_patch() {
408 let mut dag = PatchDag::new();
409 let root = make_patch("root");
410 let id = dag.add_patch(root, vec![]).unwrap();
411 assert_eq!(dag.patch_count(), 1);
412 assert!(dag.has_patch(&id));
413 }
414
415 #[test]
416 fn test_add_patch_with_parent() {
417 let mut dag = PatchDag::new();
418 let root = make_patch("root");
419 let root_id = dag.add_patch(root, vec![]).unwrap();
420
421 let child = make_patch("child");
422 let child_id = dag.add_patch(child, vec![root_id]).unwrap();
423 assert_eq!(dag.patch_count(), 2);
424
425 let ancestors = dag.ancestors(&child_id);
426 assert_eq!(ancestors.len(), 1);
427 assert!(ancestors.contains(&root_id));
428 }
429
430 #[test]
431 fn test_duplicate_patch_rejected() {
432 let mut dag = PatchDag::new();
433 let p = make_patch("dup");
434 let _id = dag.add_patch(p.clone(), vec![]).unwrap();
435 let result = dag.add_patch(p, vec![]);
436 assert!(matches!(result, Err(DagError::DuplicatePatch(_))));
437 }
438
439 #[test]
440 fn test_parent_not_found() {
441 let mut dag = PatchDag::new();
442 let child = make_patch("child");
443 let fake_parent = Hash::from_hex(&"f".repeat(64)).unwrap();
444 let result = dag.add_patch(child, vec![fake_parent]);
445 assert!(matches!(result, Err(DagError::ParentNotFound(_))));
446 }
447
448 #[test]
449 fn test_ancestors_linear_chain() {
450 let mut dag = PatchDag::new();
451 let p0 = make_patch("p0");
452 let id0 = dag.add_patch(p0, vec![]).unwrap();
453
454 let p1 = make_patch("p1");
455 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
456
457 let p2 = make_patch("p2");
458 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
459
460 let ancestors = dag.ancestors(&id2);
461 assert_eq!(ancestors.len(), 2);
462 assert!(ancestors.contains(&id0));
463 assert!(ancestors.contains(&id1));
464 }
465
466 #[test]
467 fn test_ancestors_diamond() {
468 let mut dag = PatchDag::new();
469 let root = make_patch("root");
470 let root_id = dag.add_patch(root, vec![]).unwrap();
471
472 let left = make_patch("left");
473 let left_id = dag.add_patch(left, vec![root_id]).unwrap();
474
475 let right = make_patch("right");
476 let right_id = dag.add_patch(right, vec![root_id]).unwrap();
477
478 let merge = make_patch("merge");
479 let merge_id = dag.add_patch(merge, vec![left_id, right_id]).unwrap();
480
481 let ancestors = dag.ancestors(&merge_id);
482 assert_eq!(ancestors.len(), 3); }
484
485 #[test]
486 fn test_lca_linear() {
487 let mut dag = PatchDag::new();
488 let p0 = make_patch("p0");
489 let id0 = dag.add_patch(p0, vec![]).unwrap();
490
491 let p1 = make_patch("p1");
492 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
493
494 let p2 = make_patch("p2");
495 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
496
497 assert_eq!(dag.lca(&id1, &id2), Some(id1));
498 assert_eq!(dag.lca(&id0, &id2), Some(id0));
499 }
500
501 #[test]
502 fn test_hashset_contains() {
503 use std::collections::HashSet;
504 let h1 = suture_common::Hash::from_data(b"test1");
505 let h2 = suture_common::Hash::from_data(b"test2");
506 let mut set: HashSet<suture_common::Hash> = HashSet::new();
507 set.insert(h1);
508 set.insert(h2);
509 assert!(set.contains(&h1));
510 assert!(set.contains(&h2));
511 let h3 = suture_common::Hash::from_data(b"test1");
512 assert!(set.contains(&h3), "same-value hash should be in set");
513 }
514
515 #[test]
516 fn test_ancestors_with_hashset() {
517 let mut dag = PatchDag::new();
518 let root = make_patch("root");
519 let root_id = dag.add_patch(root, vec![]).unwrap();
520 let child = make_patch("child");
521 let child_id = dag.add_patch(child, vec![root_id]).unwrap();
522
523 let ancestors = dag.ancestors(&child_id);
524 assert_eq!(ancestors.len(), 1, "child should have 1 ancestor");
525 assert!(
526 ancestors.contains(&root_id),
527 "root should be ancestor of child"
528 );
529 }
530
531 #[test]
532 fn test_lca_diamond() {
533 let mut dag = PatchDag::new();
534 let root = make_patch("root");
535 let root_id = dag.add_patch(root, vec![]).unwrap();
536
537 let left = make_patch("left");
538 let left_id = dag.add_patch(left, vec![root_id]).unwrap();
539
540 let right = make_patch("right");
541 let right_id = dag.add_patch(right, vec![root_id]).unwrap();
542
543 let merge = make_patch("merge");
544 let merge_id = dag.add_patch(merge, vec![left_id, right_id]).unwrap();
545
546 let anc_left = dag.ancestors(&left_id);
548 let anc_right = dag.ancestors(&right_id);
549 let anc_merge = dag.ancestors(&merge_id);
550 assert!(
551 anc_left.contains(&root_id),
552 "root_id should be ancestor of left_id"
553 );
554 assert!(
555 anc_right.contains(&root_id),
556 "root_id should be ancestor of right_id"
557 );
558 assert!(
559 anc_merge.contains(&left_id),
560 "left_id should be ancestor of merge_id"
561 );
562 assert!(
563 anc_merge.contains(&root_id),
564 "root_id should be ancestor of merge_id"
565 );
566 assert_eq!(
567 anc_left.len(),
568 1,
569 "left_id should have exactly 1 ancestor (root_id)"
570 );
571 assert_eq!(
572 anc_merge.len(),
573 3,
574 "merge_id should have 3 ancestors (left, right, root)"
575 );
576
577 let lca_result = dag.lca(&merge_id, &left_id);
578 assert_eq!(
579 lca_result,
580 Some(left_id),
581 "LCA of merge and left should be left"
582 );
583 }
584
585 #[test]
586 fn test_branch_operations() {
587 let mut dag = PatchDag::new();
588 let root = make_patch("root");
589 let root_id = dag.add_patch(root, vec![]).unwrap();
590
591 let main = BranchName::new("main").unwrap();
592 dag.create_branch(main.clone(), root_id).unwrap();
593
594 assert_eq!(dag.get_branch(&main), Some(root_id));
595 assert_eq!(dag.list_branches().len(), 1);
596
597 let child = make_patch("child");
598 let child_id = dag.add_patch(child, vec![root_id]).unwrap();
599 dag.update_branch(&main, child_id).unwrap();
600 assert_eq!(dag.get_branch(&main), Some(child_id));
601
602 let feat = BranchName::new("feature").unwrap();
603 dag.create_branch(feat.clone(), root_id).unwrap();
604 assert_eq!(dag.list_branches().len(), 2);
605
606 dag.delete_branch(&feat).unwrap();
607 assert_eq!(dag.list_branches().len(), 1);
608 }
609
610 #[test]
611 fn test_branch_duplicate_rejected() {
612 let mut dag = PatchDag::new();
613 let root = make_patch("root");
614 let root_id = dag.add_patch(root, vec![]).unwrap();
615
616 let main = BranchName::new("main").unwrap();
617 dag.create_branch(main.clone(), root_id).unwrap();
618 let result = dag.create_branch(main, root_id);
619 assert!(matches!(result, Err(DagError::BranchAlreadyExists(_))));
620 }
621
622 #[test]
623 fn test_patch_chain() {
624 let mut dag = PatchDag::new();
625 let p0 = make_patch("p0");
626 let id0 = dag.add_patch(p0, vec![]).unwrap();
627
628 let p1 = make_patch("p1");
629 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
630
631 let p2 = make_patch("p2");
632 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
633
634 let chain = dag.patch_chain(&id2);
635 assert_eq!(chain.len(), 3);
636 assert_eq!(chain[0], id2); assert_eq!(chain[1], id1);
638 assert_eq!(chain[2], id0);
639 }
640
641 #[test]
642 fn test_generation_numbers_linear() {
643 let mut dag = PatchDag::new();
644 let p0 = make_patch("p0");
645 let id0 = dag.add_patch(p0, vec![]).unwrap();
646 assert_eq!(dag.get_node(&id0).unwrap().generation, 0);
647
648 let p1 = make_patch("p1");
649 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
650 assert_eq!(dag.get_node(&id1).unwrap().generation, 1);
651
652 let p2 = make_patch("p2");
653 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
654 assert_eq!(dag.get_node(&id2).unwrap().generation, 2);
655 }
656
657 #[test]
658 fn test_generation_numbers_diamond() {
659 let mut dag = PatchDag::new();
660 let root = make_patch("root");
661 let root_id = dag.add_patch(root, vec![]).unwrap();
662
663 let left = make_patch("left");
664 let left_id = dag.add_patch(left, vec![root_id]).unwrap();
665
666 let right = make_patch("right");
667 let right_id = dag.add_patch(right, vec![root_id]).unwrap();
668
669 let merge = make_patch("merge");
670 let merge_id = dag.add_patch(merge, vec![left_id, right_id]).unwrap();
671
672 assert_eq!(dag.get_node(&root_id).unwrap().generation, 0);
673 assert_eq!(dag.get_node(&left_id).unwrap().generation, 1);
674 assert_eq!(dag.get_node(&right_id).unwrap().generation, 1);
675 assert_eq!(dag.get_node(&merge_id).unwrap().generation, 2);
677 }
678
679 #[test]
680 fn test_generation_numbers_uneven_branches() {
681 let mut dag = PatchDag::new();
682 let root = make_patch("root");
683 let root_id = dag.add_patch(root, vec![]).unwrap();
684
685 let a = make_patch("a");
687 let a_id = dag.add_patch(a, vec![root_id]).unwrap();
688
689 let b = make_patch("b");
691 let b_id = dag.add_patch(b, vec![root_id]).unwrap();
692 let c = make_patch("c");
693 let c_id = dag.add_patch(c, vec![b_id]).unwrap();
694 let d = make_patch("d");
695 let d_id = dag.add_patch(d, vec![c_id]).unwrap();
696
697 let merge = make_patch("merge");
699 let merge_id = dag.add_patch(merge, vec![a_id, d_id]).unwrap();
700
701 assert_eq!(dag.get_node(&a_id).unwrap().generation, 1);
702 assert_eq!(dag.get_node(&d_id).unwrap().generation, 3);
703 assert_eq!(dag.get_node(&merge_id).unwrap().generation, 4);
705 }
706
707 #[test]
708 fn test_ancestor_cache() {
709 let mut dag = PatchDag::new();
710 let p0 = make_patch("p0");
711 let id0 = dag.add_patch(p0, vec![]).unwrap();
712 let p1 = make_patch("p1");
713 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
714 let p2 = make_patch("p2");
715 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
716
717 let anc1 = dag.ancestors(&id2);
719 assert_eq!(anc1.len(), 2);
720
721 let anc2 = dag.ancestors(&id2);
723 assert_eq!(anc2.len(), 2);
724 assert_eq!(anc1, anc2);
725
726 assert!(dag.ancestor_cache.borrow().contains_key(&id2));
728 }
729
730 #[test]
731 fn test_lca_uneven_branches() {
732 let mut dag = PatchDag::new();
733 let root = make_patch("root");
734 let root_id = dag.add_patch(root, vec![]).unwrap();
735
736 let a1 = make_patch("a1");
738 let a1_id = dag.add_patch(a1, vec![root_id]).unwrap();
739 let a2 = make_patch("a2");
740 let a2_id = dag.add_patch(a2, vec![a1_id]).unwrap();
741
742 let b1 = make_patch("b1");
744 let b1_id = dag.add_patch(b1, vec![root_id]).unwrap();
745
746 assert_eq!(dag.lca(&a2_id, &b1_id), Some(root_id));
748 assert_eq!(dag.lca(&a1_id, &b1_id), Some(root_id));
750 }
751
752 #[test]
753 fn test_lca_no_common_ancestor() {
754 let mut dag = PatchDag::new();
755 let r1 = make_patch("root1");
757 let r1_id = dag.add_patch(r1, vec![]).unwrap();
758 let r2 = make_patch("root2");
759 let r2_id = dag.add_patch(r2, vec![]).unwrap();
760
761 assert_eq!(dag.lca(&r1_id, &r2_id), None);
763 }
764
765 mod proptests {
766 use super::*;
767 use crate::patch::types::{OperationType, Patch, TouchSet};
768 use proptest::prelude::*;
769
770 fn make_unique_patch(idx: usize) -> Patch {
771 let addr = format!("proptest_addr_{}", idx);
772 Patch::new(
773 OperationType::Modify,
774 TouchSet::single(&addr),
775 Some(format!("file_{}", addr)),
776 addr.clone().into_bytes(),
777 vec![],
778 "proptest".to_string(),
779 format!("patch {}", idx),
780 )
781 }
782
783 proptest! {
784 #[test]
785 fn add_patch_increases_count(n in 0usize..20) {
786 let mut dag = PatchDag::new();
787 let mut last_id = None;
788 for i in 0..n {
789 let patch = make_unique_patch(i);
790 let parents = last_id.map(|id| vec![id]).unwrap_or_default();
791 let id = dag.add_patch(patch, parents).unwrap();
792 last_id = Some(id);
793 }
794 prop_assert_eq!(dag.patch_count(), n);
795 }
796
797 #[test]
798 fn patch_chain_ancestry(n in 0usize..20) {
799 prop_assume!(n > 0);
800 let mut dag = PatchDag::new();
801 let mut last_id = None;
802 for i in 0..n {
803 let patch = make_unique_patch(i);
804 let parents = last_id.map(|id| vec![id]).unwrap_or_default();
805 let id = dag.add_patch(patch, parents).unwrap();
806 last_id = Some(id);
807 }
808 let tip = last_id.unwrap();
809 let chain = dag.patch_chain(&tip);
810 prop_assert_eq!(chain.len(), n);
811 }
812
813 #[test]
814 fn lca_linear_chain(n in 2usize..15) {
815 let mut dag = PatchDag::new();
816 let mut ids = Vec::new();
817 for i in 0..n {
818 let patch = make_unique_patch(i);
819 let parents = ids.last().map(|id| vec![*id]).unwrap_or_default();
820 let id = dag.add_patch(patch, parents).unwrap();
821 ids.push(id);
822 }
823 prop_assert_eq!(dag.lca(&ids[0], &ids[n - 1]), Some(ids[0]));
825 if n >= 3 {
827 prop_assert_eq!(dag.lca(&ids[1], &ids[n - 1]), Some(ids[1]));
828 }
829 prop_assert_eq!(dag.lca(&ids[n - 1], &ids[n - 1]), Some(ids[n - 1]));
831 }
832
833 #[test]
834 fn ancestors_subset(n in 1usize..20) {
835 let mut dag = PatchDag::new();
836 let mut ids = Vec::new();
837 for i in 0..n {
838 let patch = make_unique_patch(i);
839 let parents = ids.last().map(|id| vec![*id]).unwrap_or_default();
840 let id = dag.add_patch(patch, parents).unwrap();
841 ids.push(id);
842 }
843 let tip = ids.last().unwrap();
844 let ancestors = dag.ancestors(tip);
845 for (i, id) in ids.iter().enumerate().take(n - 1) {
847 prop_assert!(ancestors.contains(id),
848 "predecessor {} should be ancestor of tip", i);
849 }
850 prop_assert!(!ancestors.contains(tip));
852 prop_assert_eq!(ancestors.len(), n - 1);
854 }
855 }
856 }
857}