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
63impl DagNode {
64 pub fn id(&self) -> &PatchId {
66 &self.patch.id
67 }
68}
69
70pub struct PatchDag {
78 pub(crate) nodes: HashMap<PatchId, DagNode>,
80 pub(crate) branches: HashMap<String, PatchId>,
82 ancestor_cache: RefCell<HashMap<PatchId, HashSet<PatchId>>>,
86}
87
88impl Default for PatchDag {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl PatchDag {
95 pub fn new() -> Self {
97 Self {
98 nodes: HashMap::new(),
99 branches: HashMap::new(),
100 ancestor_cache: RefCell::new(HashMap::new()),
101 }
102 }
103
104 pub fn add_patch(
117 &mut self,
118 patch: Patch,
119 parent_ids: Vec<PatchId>,
120 ) -> Result<PatchId, DagError> {
121 let id = patch.id;
122
123 if self.nodes.contains_key(&id) {
125 return Err(DagError::DuplicatePatch(id.to_hex()));
126 }
127
128 if !parent_ids.is_empty() {
130 for parent_id in &parent_ids {
131 if !self.nodes.contains_key(parent_id) {
132 return Err(DagError::ParentNotFound(parent_id.to_hex()));
133 }
134 }
135
136 }
141
142 let generation = if parent_ids.is_empty() {
144 0
145 } else {
146 parent_ids
147 .iter()
148 .map(|pid| self.nodes.get(pid).map(|n| n.generation).unwrap_or(0))
149 .max()
150 .unwrap_or(0)
151 + 1
152 };
153
154 let node = DagNode {
156 patch,
157 parent_ids: parent_ids.clone(),
158 child_ids: Vec::new(),
159 generation,
160 };
161
162 for parent_id in &parent_ids {
164 if let Some(parent_node) = self.nodes.get_mut(parent_id) {
165 parent_node.child_ids.push(id);
166 }
167 }
168
169 self.nodes.insert(id, node);
170 Ok(id)
171 }
172
173 pub fn get_patch(&self, id: &PatchId) -> Option<&Patch> {
175 self.nodes.get(id).map(|node| &node.patch)
176 }
177
178 pub fn get_node(&self, id: &PatchId) -> Option<&DagNode> {
180 self.nodes.get(id)
181 }
182
183 pub fn has_patch(&self, id: &PatchId) -> bool {
185 self.nodes.contains_key(id)
186 }
187
188 pub fn ancestors(&self, id: &PatchId) -> HashSet<PatchId> {
196 if let Some(cached) = self.ancestor_cache.borrow().get(id) {
198 return cached.clone();
199 }
200
201 let mut ancestors = HashSet::new();
203 let mut queue: VecDeque<PatchId> = VecDeque::new();
204
205 if let Some(node) = self.nodes.get(id) {
206 for parent_id in &node.parent_ids {
207 if !ancestors.contains(parent_id) {
208 ancestors.insert(*parent_id);
209 queue.push_back(*parent_id);
210 }
211 }
212 }
213
214 while let Some(current) = queue.pop_front() {
215 if let Some(node) = self.nodes.get(¤t) {
216 for parent_id in &node.parent_ids {
217 if ancestors.insert(*parent_id) {
218 queue.push_back(*parent_id);
219 }
220 }
221 }
222 }
223
224 self.ancestor_cache
226 .borrow_mut()
227 .insert(*id, ancestors.clone());
228 ancestors
229 }
230
231 pub fn lca(&self, a: &PatchId, b: &PatchId) -> Option<PatchId> {
239 if a == b {
240 return Some(*a);
241 }
242
243 let ancestors_a = self.ancestors(a);
244 let ancestors_b = self.ancestors(b);
245
246 if ancestors_b.contains(a) {
248 return Some(*a);
249 }
250 if ancestors_a.contains(b) {
252 return Some(*b);
253 }
254
255 let common: Vec<PatchId> = ancestors_a.intersection(&ancestors_b).copied().collect();
257
258 if common.is_empty() {
259 return None;
260 }
261
262 let mut best: Option<PatchId> = None;
265 let mut best_gen: u64 = 0;
266
267 for candidate in &common {
268 let candidate_gen = self.nodes.get(candidate).map(|n| n.generation).unwrap_or(0);
269 if candidate_gen >= best_gen {
270 best_gen = candidate_gen;
271 best = Some(*candidate);
272 }
273 }
274
275 best
276 }
277
278 #[allow(dead_code)]
284 fn ancestor_depth(&self, id: &PatchId) -> usize {
285 self.nodes
286 .get(id)
287 .map(|n| n.generation as usize)
288 .unwrap_or(0)
289 }
290
291 pub fn create_branch(&mut self, name: BranchName, target: PatchId) -> Result<(), DagError> {
293 let name_str = name.as_str().to_string();
294
295 if name_str.is_empty() {
296 return Err(DagError::EmptyBranchName);
297 }
298
299 if self.branches.contains_key(&name_str) {
300 return Err(DagError::BranchAlreadyExists(name_str));
301 }
302
303 if !self.nodes.contains_key(&target) {
304 return Err(DagError::PatchNotFound(target.to_hex()));
305 }
306
307 self.branches.insert(name_str, target);
308 Ok(())
309 }
310
311 pub fn get_branch(&self, name: &BranchName) -> Option<PatchId> {
313 self.branches.get(name.as_str()).copied()
314 }
315
316 pub fn update_branch(&mut self, name: &BranchName, target: PatchId) -> Result<(), DagError> {
318 if !self.branches.contains_key(name.as_str()) {
319 return Err(DagError::BranchNotFound(name.as_str().to_string()));
320 }
321 if !self.nodes.contains_key(&target) {
322 return Err(DagError::PatchNotFound(target.to_hex()));
323 }
324 self.branches.insert(name.as_str().to_string(), target);
325 Ok(())
326 }
327
328 pub fn delete_branch(&mut self, name: &BranchName) -> Result<(), DagError> {
330 if self.branches.remove(name.as_str()).is_none() {
331 return Err(DagError::BranchNotFound(name.as_str().to_string()));
332 }
333 Ok(())
334 }
335
336 pub fn list_branches(&self) -> Vec<(String, PatchId)> {
338 let mut branches: Vec<_> = self
339 .branches
340 .iter()
341 .map(|(name, id)| (name.clone(), *id))
342 .collect();
343 branches.sort_by(|a, b| a.0.cmp(&b.0));
344 branches
345 }
346
347 pub fn patch_count(&self) -> usize {
349 self.nodes.len()
350 }
351
352 pub fn patch_ids(&self) -> Vec<PatchId> {
354 self.nodes.keys().copied().collect()
355 }
356
357 pub fn patch_chain(&self, id: &PatchId) -> Vec<PatchId> {
359 let mut chain = Vec::new();
360 let mut current = Some(*id);
361
362 while let Some(curr_id) = current {
363 if chain.contains(&curr_id) {
364 break; }
366 chain.push(curr_id);
367 current = self
368 .nodes
369 .get(&curr_id)
370 .and_then(|n| n.parent_ids.first().copied());
371 }
372
373 chain
374 }
375
376 pub fn reachable_patches(&self, id: &PatchId) -> Vec<Patch> {
378 let ancestors = self.ancestors(id);
379 let mut patches = Vec::with_capacity(ancestors.len() + 1);
380
381 if let Some(node) = self.nodes.get(id) {
382 patches.push(node.patch.clone());
383 }
384
385 for ancestor_id in ancestors {
386 if let Some(node) = self.nodes.get(&ancestor_id) {
387 patches.push(node.patch.clone());
388 }
389 }
390
391 patches
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398 use crate::patch::types::{OperationType, Patch, TouchSet};
399 use suture_common::Hash;
400
401 fn make_patch(addr: &str) -> Patch {
402 Patch::new(
403 OperationType::Modify,
404 TouchSet::single(addr),
405 Some(format!("file_{}", addr)),
406 vec![],
407 vec![],
408 "test".to_string(),
409 format!("edit {}", addr),
410 )
411 }
412
413 #[test]
414 fn test_add_root_patch() {
415 let mut dag = PatchDag::new();
416 let root = make_patch("root");
417 let id = dag.add_patch(root, vec![]).unwrap();
418 assert_eq!(dag.patch_count(), 1);
419 assert!(dag.has_patch(&id));
420 }
421
422 #[test]
423 fn test_add_patch_with_parent() {
424 let mut dag = PatchDag::new();
425 let root = make_patch("root");
426 let root_id = dag.add_patch(root, vec![]).unwrap();
427
428 let child = make_patch("child");
429 let child_id = dag.add_patch(child, vec![root_id]).unwrap();
430 assert_eq!(dag.patch_count(), 2);
431
432 let ancestors = dag.ancestors(&child_id);
433 assert_eq!(ancestors.len(), 1);
434 assert!(ancestors.contains(&root_id));
435 }
436
437 #[test]
438 fn test_duplicate_patch_rejected() {
439 let mut dag = PatchDag::new();
440 let p = make_patch("dup");
441 let _id = dag.add_patch(p.clone(), vec![]).unwrap();
442 let result = dag.add_patch(p, vec![]);
443 assert!(matches!(result, Err(DagError::DuplicatePatch(_))));
444 }
445
446 #[test]
447 fn test_parent_not_found() {
448 let mut dag = PatchDag::new();
449 let child = make_patch("child");
450 let fake_parent = Hash::from_hex(&"f".repeat(64)).unwrap();
451 let result = dag.add_patch(child, vec![fake_parent]);
452 assert!(matches!(result, Err(DagError::ParentNotFound(_))));
453 }
454
455 #[test]
456 fn test_ancestors_linear_chain() {
457 let mut dag = PatchDag::new();
458 let p0 = make_patch("p0");
459 let id0 = dag.add_patch(p0, vec![]).unwrap();
460
461 let p1 = make_patch("p1");
462 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
463
464 let p2 = make_patch("p2");
465 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
466
467 let ancestors = dag.ancestors(&id2);
468 assert_eq!(ancestors.len(), 2);
469 assert!(ancestors.contains(&id0));
470 assert!(ancestors.contains(&id1));
471 }
472
473 #[test]
474 fn test_ancestors_diamond() {
475 let mut dag = PatchDag::new();
476 let root = make_patch("root");
477 let root_id = dag.add_patch(root, vec![]).unwrap();
478
479 let left = make_patch("left");
480 let left_id = dag.add_patch(left, vec![root_id]).unwrap();
481
482 let right = make_patch("right");
483 let right_id = dag.add_patch(right, vec![root_id]).unwrap();
484
485 let merge = make_patch("merge");
486 let merge_id = dag.add_patch(merge, vec![left_id, right_id]).unwrap();
487
488 let ancestors = dag.ancestors(&merge_id);
489 assert_eq!(ancestors.len(), 3); }
491
492 #[test]
493 fn test_lca_linear() {
494 let mut dag = PatchDag::new();
495 let p0 = make_patch("p0");
496 let id0 = dag.add_patch(p0, vec![]).unwrap();
497
498 let p1 = make_patch("p1");
499 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
500
501 let p2 = make_patch("p2");
502 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
503
504 assert_eq!(dag.lca(&id1, &id2), Some(id1));
505 assert_eq!(dag.lca(&id0, &id2), Some(id0));
506 }
507
508 #[test]
509 fn test_hashset_contains() {
510 use std::collections::HashSet;
511 let h1 = suture_common::Hash::from_data(b"test1");
512 let h2 = suture_common::Hash::from_data(b"test2");
513 let mut set: HashSet<suture_common::Hash> = HashSet::new();
514 set.insert(h1);
515 set.insert(h2);
516 assert!(set.contains(&h1));
517 assert!(set.contains(&h2));
518 let h3 = suture_common::Hash::from_data(b"test1");
519 assert!(set.contains(&h3), "same-value hash should be in set");
520 }
521
522 #[test]
523 fn test_ancestors_with_hashset() {
524 let mut dag = PatchDag::new();
525 let root = make_patch("root");
526 let root_id = dag.add_patch(root, vec![]).unwrap();
527 let child = make_patch("child");
528 let child_id = dag.add_patch(child, vec![root_id]).unwrap();
529
530 let ancestors = dag.ancestors(&child_id);
531 assert_eq!(ancestors.len(), 1, "child should have 1 ancestor");
532 assert!(
533 ancestors.contains(&root_id),
534 "root should be ancestor of child"
535 );
536 }
537
538 #[test]
539 fn test_lca_diamond() {
540 let mut dag = PatchDag::new();
541 let root = make_patch("root");
542 let root_id = dag.add_patch(root, vec![]).unwrap();
543
544 let left = make_patch("left");
545 let left_id = dag.add_patch(left, vec![root_id]).unwrap();
546
547 let right = make_patch("right");
548 let right_id = dag.add_patch(right, vec![root_id]).unwrap();
549
550 let merge = make_patch("merge");
551 let merge_id = dag.add_patch(merge, vec![left_id, right_id]).unwrap();
552
553 let anc_left = dag.ancestors(&left_id);
555 let anc_right = dag.ancestors(&right_id);
556 let anc_merge = dag.ancestors(&merge_id);
557 assert!(
558 anc_left.contains(&root_id),
559 "root_id should be ancestor of left_id"
560 );
561 assert!(
562 anc_right.contains(&root_id),
563 "root_id should be ancestor of right_id"
564 );
565 assert!(
566 anc_merge.contains(&left_id),
567 "left_id should be ancestor of merge_id"
568 );
569 assert!(
570 anc_merge.contains(&root_id),
571 "root_id should be ancestor of merge_id"
572 );
573 assert_eq!(
574 anc_left.len(),
575 1,
576 "left_id should have exactly 1 ancestor (root_id)"
577 );
578 assert_eq!(
579 anc_merge.len(),
580 3,
581 "merge_id should have 3 ancestors (left, right, root)"
582 );
583
584 let lca_result = dag.lca(&merge_id, &left_id);
585 assert_eq!(
586 lca_result,
587 Some(left_id),
588 "LCA of merge and left should be left"
589 );
590 }
591
592 #[test]
593 fn test_branch_operations() {
594 let mut dag = PatchDag::new();
595 let root = make_patch("root");
596 let root_id = dag.add_patch(root, vec![]).unwrap();
597
598 let main = BranchName::new("main").unwrap();
599 dag.create_branch(main.clone(), root_id).unwrap();
600
601 assert_eq!(dag.get_branch(&main), Some(root_id));
602 assert_eq!(dag.list_branches().len(), 1);
603
604 let child = make_patch("child");
605 let child_id = dag.add_patch(child, vec![root_id]).unwrap();
606 dag.update_branch(&main, child_id).unwrap();
607 assert_eq!(dag.get_branch(&main), Some(child_id));
608
609 let feat = BranchName::new("feature").unwrap();
610 dag.create_branch(feat.clone(), root_id).unwrap();
611 assert_eq!(dag.list_branches().len(), 2);
612
613 dag.delete_branch(&feat).unwrap();
614 assert_eq!(dag.list_branches().len(), 1);
615 }
616
617 #[test]
618 fn test_branch_duplicate_rejected() {
619 let mut dag = PatchDag::new();
620 let root = make_patch("root");
621 let root_id = dag.add_patch(root, vec![]).unwrap();
622
623 let main = BranchName::new("main").unwrap();
624 dag.create_branch(main.clone(), root_id).unwrap();
625 let result = dag.create_branch(main, root_id);
626 assert!(matches!(result, Err(DagError::BranchAlreadyExists(_))));
627 }
628
629 #[test]
630 fn test_patch_chain() {
631 let mut dag = PatchDag::new();
632 let p0 = make_patch("p0");
633 let id0 = dag.add_patch(p0, vec![]).unwrap();
634
635 let p1 = make_patch("p1");
636 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
637
638 let p2 = make_patch("p2");
639 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
640
641 let chain = dag.patch_chain(&id2);
642 assert_eq!(chain.len(), 3);
643 assert_eq!(chain[0], id2); assert_eq!(chain[1], id1);
645 assert_eq!(chain[2], id0);
646 }
647
648 #[test]
649 fn test_generation_numbers_linear() {
650 let mut dag = PatchDag::new();
651 let p0 = make_patch("p0");
652 let id0 = dag.add_patch(p0, vec![]).unwrap();
653 assert_eq!(dag.get_node(&id0).unwrap().generation, 0);
654
655 let p1 = make_patch("p1");
656 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
657 assert_eq!(dag.get_node(&id1).unwrap().generation, 1);
658
659 let p2 = make_patch("p2");
660 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
661 assert_eq!(dag.get_node(&id2).unwrap().generation, 2);
662 }
663
664 #[test]
665 fn test_generation_numbers_diamond() {
666 let mut dag = PatchDag::new();
667 let root = make_patch("root");
668 let root_id = dag.add_patch(root, vec![]).unwrap();
669
670 let left = make_patch("left");
671 let left_id = dag.add_patch(left, vec![root_id]).unwrap();
672
673 let right = make_patch("right");
674 let right_id = dag.add_patch(right, vec![root_id]).unwrap();
675
676 let merge = make_patch("merge");
677 let merge_id = dag.add_patch(merge, vec![left_id, right_id]).unwrap();
678
679 assert_eq!(dag.get_node(&root_id).unwrap().generation, 0);
680 assert_eq!(dag.get_node(&left_id).unwrap().generation, 1);
681 assert_eq!(dag.get_node(&right_id).unwrap().generation, 1);
682 assert_eq!(dag.get_node(&merge_id).unwrap().generation, 2);
684 }
685
686 #[test]
687 fn test_generation_numbers_uneven_branches() {
688 let mut dag = PatchDag::new();
689 let root = make_patch("root");
690 let root_id = dag.add_patch(root, vec![]).unwrap();
691
692 let a = make_patch("a");
694 let a_id = dag.add_patch(a, vec![root_id]).unwrap();
695
696 let b = make_patch("b");
698 let b_id = dag.add_patch(b, vec![root_id]).unwrap();
699 let c = make_patch("c");
700 let c_id = dag.add_patch(c, vec![b_id]).unwrap();
701 let d = make_patch("d");
702 let d_id = dag.add_patch(d, vec![c_id]).unwrap();
703
704 let merge = make_patch("merge");
706 let merge_id = dag.add_patch(merge, vec![a_id, d_id]).unwrap();
707
708 assert_eq!(dag.get_node(&a_id).unwrap().generation, 1);
709 assert_eq!(dag.get_node(&d_id).unwrap().generation, 3);
710 assert_eq!(dag.get_node(&merge_id).unwrap().generation, 4);
712 }
713
714 #[test]
715 fn test_ancestor_cache() {
716 let mut dag = PatchDag::new();
717 let p0 = make_patch("p0");
718 let id0 = dag.add_patch(p0, vec![]).unwrap();
719 let p1 = make_patch("p1");
720 let id1 = dag.add_patch(p1, vec![id0]).unwrap();
721 let p2 = make_patch("p2");
722 let id2 = dag.add_patch(p2, vec![id1]).unwrap();
723
724 let anc1 = dag.ancestors(&id2);
726 assert_eq!(anc1.len(), 2);
727
728 let anc2 = dag.ancestors(&id2);
730 assert_eq!(anc2.len(), 2);
731 assert_eq!(anc1, anc2);
732
733 assert!(dag.ancestor_cache.borrow().contains_key(&id2));
735 }
736
737 #[test]
738 fn test_lca_uneven_branches() {
739 let mut dag = PatchDag::new();
740 let root = make_patch("root");
741 let root_id = dag.add_patch(root, vec![]).unwrap();
742
743 let a1 = make_patch("a1");
745 let a1_id = dag.add_patch(a1, vec![root_id]).unwrap();
746 let a2 = make_patch("a2");
747 let a2_id = dag.add_patch(a2, vec![a1_id]).unwrap();
748
749 let b1 = make_patch("b1");
751 let b1_id = dag.add_patch(b1, vec![root_id]).unwrap();
752
753 assert_eq!(dag.lca(&a2_id, &b1_id), Some(root_id));
755 assert_eq!(dag.lca(&a1_id, &b1_id), Some(root_id));
757 }
758
759 #[test]
760 fn test_lca_no_common_ancestor() {
761 let mut dag = PatchDag::new();
762 let r1 = make_patch("root1");
764 let r1_id = dag.add_patch(r1, vec![]).unwrap();
765 let r2 = make_patch("root2");
766 let r2_id = dag.add_patch(r2, vec![]).unwrap();
767
768 assert_eq!(dag.lca(&r1_id, &r2_id), None);
770 }
771
772 mod proptests {
773 use super::*;
774 use crate::patch::types::{OperationType, Patch, TouchSet};
775 use proptest::prelude::*;
776
777 fn make_unique_patch(idx: usize) -> Patch {
778 let addr = format!("proptest_addr_{}", idx);
779 Patch::new(
780 OperationType::Modify,
781 TouchSet::single(&addr),
782 Some(format!("file_{}", addr)),
783 addr.clone().into_bytes(),
784 vec![],
785 "proptest".to_string(),
786 format!("patch {}", idx),
787 )
788 }
789
790 proptest! {
791 #[test]
792 fn add_patch_increases_count(n in 0usize..20) {
793 let mut dag = PatchDag::new();
794 let mut last_id = None;
795 for i in 0..n {
796 let patch = make_unique_patch(i);
797 let parents = last_id.map(|id| vec![id]).unwrap_or_default();
798 let id = dag.add_patch(patch, parents).unwrap();
799 last_id = Some(id);
800 }
801 prop_assert_eq!(dag.patch_count(), n);
802 }
803
804 #[test]
805 fn patch_chain_ancestry(n in 0usize..20) {
806 prop_assume!(n > 0);
807 let mut dag = PatchDag::new();
808 let mut last_id = None;
809 for i in 0..n {
810 let patch = make_unique_patch(i);
811 let parents = last_id.map(|id| vec![id]).unwrap_or_default();
812 let id = dag.add_patch(patch, parents).unwrap();
813 last_id = Some(id);
814 }
815 let tip = last_id.unwrap();
816 let chain = dag.patch_chain(&tip);
817 prop_assert_eq!(chain.len(), n);
818 }
819
820 #[test]
821 fn lca_linear_chain(n in 2usize..15) {
822 let mut dag = PatchDag::new();
823 let mut ids = Vec::new();
824 for i in 0..n {
825 let patch = make_unique_patch(i);
826 let parents = ids.last().map(|id| vec![*id]).unwrap_or_default();
827 let id = dag.add_patch(patch, parents).unwrap();
828 ids.push(id);
829 }
830 prop_assert_eq!(dag.lca(&ids[0], &ids[n - 1]), Some(ids[0]));
832 if n >= 3 {
834 prop_assert_eq!(dag.lca(&ids[1], &ids[n - 1]), Some(ids[1]));
835 }
836 prop_assert_eq!(dag.lca(&ids[n - 1], &ids[n - 1]), Some(ids[n - 1]));
838 }
839
840 #[test]
841 fn ancestors_subset(n in 1usize..20) {
842 let mut dag = PatchDag::new();
843 let mut ids = Vec::new();
844 for i in 0..n {
845 let patch = make_unique_patch(i);
846 let parents = ids.last().map(|id| vec![*id]).unwrap_or_default();
847 let id = dag.add_patch(patch, parents).unwrap();
848 ids.push(id);
849 }
850 let tip = ids.last().unwrap();
851 let ancestors = dag.ancestors(tip);
852 for (i, id) in ids.iter().enumerate().take(n - 1) {
854 prop_assert!(ancestors.contains(id),
855 "predecessor {} should be ancestor of tip", i);
856 }
857 prop_assert!(!ancestors.contains(tip));
859 prop_assert_eq!(ancestors.len(), n - 1);
861 }
862 }
863 }
864}