##// END OF EJS Templates
rust: stop putting NULL_REVISION in MissingAncestors.bases...
Georges Racinet -
r41865:97743297 default
parent child Browse files
Show More
@@ -1,767 +1,766
1 1 // ancestors.rs
2 2 //
3 3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Rust versions of generic DAG ancestors algorithms for Mercurial
9 9
10 10 use super::{Graph, GraphError, Revision, NULL_REVISION};
11 11 use std::cmp::max;
12 12 use std::collections::{BinaryHeap, HashSet};
13 13 use crate::dagops;
14 14
15 15 /// Iterator over the ancestors of a given list of revisions
16 16 /// This is a generic type, defined and implemented for any Graph, so that
17 17 /// it's easy to
18 18 ///
19 19 /// - unit test in pure Rust
20 20 /// - bind to main Mercurial code, potentially in several ways and have these
21 21 /// bindings evolve over time
22 22 pub struct AncestorsIterator<G: Graph> {
23 23 graph: G,
24 24 visit: BinaryHeap<Revision>,
25 25 seen: HashSet<Revision>,
26 26 stoprev: Revision,
27 27 }
28 28
29 29 /// Lazy ancestors set, backed by AncestorsIterator
30 30 pub struct LazyAncestors<G: Graph + Clone> {
31 31 graph: G,
32 32 containsiter: AncestorsIterator<G>,
33 33 initrevs: Vec<Revision>,
34 34 stoprev: Revision,
35 35 inclusive: bool,
36 36 }
37 37
38 38 pub struct MissingAncestors<G: Graph> {
39 39 graph: G,
40 40 bases: HashSet<Revision>,
41 41 }
42 42
43 43 impl<G: Graph> AncestorsIterator<G> {
44 44 /// Constructor.
45 45 ///
46 46 /// if `inclusive` is true, then the init revisions are emitted in
47 47 /// particular, otherwise iteration starts from their parents.
48 48 pub fn new(
49 49 graph: G,
50 50 initrevs: impl IntoIterator<Item = Revision>,
51 51 stoprev: Revision,
52 52 inclusive: bool,
53 53 ) -> Result<Self, GraphError> {
54 54 let filtered_initrevs = initrevs.into_iter().filter(|&r| r >= stoprev);
55 55 if inclusive {
56 56 let visit: BinaryHeap<Revision> = filtered_initrevs.collect();
57 57 let seen = visit.iter().map(|&x| x).collect();
58 58 return Ok(AncestorsIterator {
59 59 visit: visit,
60 60 seen: seen,
61 61 stoprev: stoprev,
62 62 graph: graph,
63 63 });
64 64 }
65 65 let mut this = AncestorsIterator {
66 66 visit: BinaryHeap::new(),
67 67 seen: HashSet::new(),
68 68 stoprev: stoprev,
69 69 graph: graph,
70 70 };
71 71 this.seen.insert(NULL_REVISION);
72 72 for rev in filtered_initrevs {
73 73 for parent in this.graph.parents(rev)?.iter().cloned() {
74 74 this.conditionally_push_rev(parent);
75 75 }
76 76 }
77 77 Ok(this)
78 78 }
79 79
80 80 #[inline]
81 81 fn conditionally_push_rev(&mut self, rev: Revision) {
82 82 if self.stoprev <= rev && self.seen.insert(rev) {
83 83 self.visit.push(rev);
84 84 }
85 85 }
86 86
87 87 /// Consumes partially the iterator to tell if the given target
88 88 /// revision
89 89 /// is in the ancestors it emits.
90 90 /// This is meant for iterators actually dedicated to that kind of
91 91 /// purpose
92 92 pub fn contains(&mut self, target: Revision) -> Result<bool, GraphError> {
93 93 if self.seen.contains(&target) && target != NULL_REVISION {
94 94 return Ok(true);
95 95 }
96 96 for item in self {
97 97 let rev = item?;
98 98 if rev == target {
99 99 return Ok(true);
100 100 }
101 101 if rev < target {
102 102 return Ok(false);
103 103 }
104 104 }
105 105 Ok(false)
106 106 }
107 107
108 108 pub fn peek(&self) -> Option<Revision> {
109 109 self.visit.peek().map(|&r| r)
110 110 }
111 111
112 112 /// Tell if the iterator is about an empty set
113 113 ///
114 114 /// The result does not depend whether the iterator has been consumed
115 115 /// or not.
116 116 /// This is mostly meant for iterators backing a lazy ancestors set
117 117 pub fn is_empty(&self) -> bool {
118 118 if self.visit.len() > 0 {
119 119 return false;
120 120 }
121 121 if self.seen.len() > 1 {
122 122 return false;
123 123 }
124 124 // at this point, the seen set is at most a singleton.
125 125 // If not `self.inclusive`, it's still possible that it has only
126 126 // the null revision
127 127 self.seen.is_empty() || self.seen.contains(&NULL_REVISION)
128 128 }
129 129 }
130 130
131 131 /// Main implementation for the iterator
132 132 ///
133 133 /// The algorithm is the same as in `_lazyancestorsiter()` from `ancestors.py`
134 134 /// with a few non crucial differences:
135 135 ///
136 136 /// - there's no filtering of invalid parent revisions. Actually, it should be
137 137 /// consistent and more efficient to filter them from the end caller.
138 138 /// - we don't have the optimization for adjacent revisions (i.e., the case
139 139 /// where `p1 == rev - 1`), because it amounts to update the first element of
140 140 /// the heap without sifting, which Rust's BinaryHeap doesn't let us do.
141 141 /// - we save a few pushes by comparing with `stoprev` before pushing
142 142 impl<G: Graph> Iterator for AncestorsIterator<G> {
143 143 type Item = Result<Revision, GraphError>;
144 144
145 145 fn next(&mut self) -> Option<Self::Item> {
146 146 let current = match self.visit.peek() {
147 147 None => {
148 148 return None;
149 149 }
150 150 Some(c) => *c,
151 151 };
152 152 let [p1, p2] = match self.graph.parents(current) {
153 153 Ok(ps) => ps,
154 154 Err(e) => return Some(Err(e)),
155 155 };
156 156 if p1 < self.stoprev || !self.seen.insert(p1) {
157 157 self.visit.pop();
158 158 } else {
159 159 *(self.visit.peek_mut().unwrap()) = p1;
160 160 };
161 161
162 162 self.conditionally_push_rev(p2);
163 163 Some(Ok(current))
164 164 }
165 165 }
166 166
167 167 impl<G: Graph + Clone> LazyAncestors<G> {
168 168 pub fn new(
169 169 graph: G,
170 170 initrevs: impl IntoIterator<Item = Revision>,
171 171 stoprev: Revision,
172 172 inclusive: bool,
173 173 ) -> Result<Self, GraphError> {
174 174 let v: Vec<Revision> = initrevs.into_iter().collect();
175 175 Ok(LazyAncestors {
176 176 graph: graph.clone(),
177 177 containsiter: AncestorsIterator::new(
178 178 graph,
179 179 v.iter().cloned(),
180 180 stoprev,
181 181 inclusive,
182 182 )?,
183 183 initrevs: v,
184 184 stoprev: stoprev,
185 185 inclusive: inclusive,
186 186 })
187 187 }
188 188
189 189 pub fn contains(&mut self, rev: Revision) -> Result<bool, GraphError> {
190 190 self.containsiter.contains(rev)
191 191 }
192 192
193 193 pub fn is_empty(&self) -> bool {
194 194 self.containsiter.is_empty()
195 195 }
196 196
197 197 pub fn iter(&self) -> AncestorsIterator<G> {
198 198 // the arguments being the same as for self.containsiter, we know
199 199 // for sure that AncestorsIterator constructor can't fail
200 200 AncestorsIterator::new(
201 201 self.graph.clone(),
202 202 self.initrevs.iter().cloned(),
203 203 self.stoprev,
204 204 self.inclusive,
205 205 )
206 206 .unwrap()
207 207 }
208 208 }
209 209
210 210 impl<G: Graph> MissingAncestors<G> {
211 211 pub fn new(graph: G, bases: impl IntoIterator<Item = Revision>) -> Self {
212 let mut bases: HashSet<Revision> = bases.into_iter().collect();
213 if bases.is_empty() {
214 bases.insert(NULL_REVISION);
215 }
216 MissingAncestors { graph, bases }
212 MissingAncestors { graph: graph, bases: bases.into_iter().collect() }
217 213 }
218 214
219 215 pub fn has_bases(&self) -> bool {
220 self.bases.iter().any(|&b| b != NULL_REVISION)
216 !self.bases.is_empty()
221 217 }
222 218
223 219 /// Return a reference to current bases.
224 220 ///
225 221 /// This is useful in unit tests, but also setdiscovery.py does
226 222 /// read the bases attribute of a ancestor.missingancestors instance.
227 223 pub fn get_bases<'a>(&'a self) -> &'a HashSet<Revision> {
228 224 &self.bases
229 225 }
230 226
231 227 /// Computes the relative heads of current bases.
232 228 ///
233 229 /// The object is still usable after this.
234 230 pub fn bases_heads(&self) -> Result<HashSet<Revision>, GraphError> {
235 231 dagops::heads(&self.graph, self.bases.iter())
236 232 }
237 233
238 234 /// Consumes the object and returns the relative heads of its bases.
239 235 pub fn into_bases_heads(mut self) -> Result<HashSet<Revision>, GraphError> {
240 236 dagops::retain_heads(&self.graph, &mut self.bases)?;
241 237 Ok(self.bases)
242 238 }
243 239
244 240 pub fn add_bases(
245 241 &mut self,
246 242 new_bases: impl IntoIterator<Item = Revision>,
247 243 ) {
248 self.bases.extend(new_bases);
244 self.bases
245 .extend(new_bases.into_iter().filter(|&rev| rev != NULL_REVISION));
249 246 }
250 247
251 248 /// Remove all ancestors of self.bases from the revs set (in place)
252 249 pub fn remove_ancestors_from(
253 250 &mut self,
254 251 revs: &mut HashSet<Revision>,
255 252 ) -> Result<(), GraphError> {
256 253 revs.retain(|r| !self.bases.contains(r));
257 // the null revision is always an ancestor
254 // the null revision is always an ancestor. Logically speaking
255 // it's debatable in case bases is empty, but the Python
256 // implementation always adds NULL_REVISION to bases, making it
257 // unconditionnally true.
258 258 revs.remove(&NULL_REVISION);
259 259 if revs.is_empty() {
260 260 return Ok(());
261 261 }
262 262 // anything in revs > start is definitely not an ancestor of bases
263 263 // revs <= start need to be investigated
264 264 // TODO optim: if a missingancestors is to be used several times,
265 265 // we shouldn't need to iterate each time on bases
266 266 let start = match self.bases.iter().cloned().max() {
267 267 Some(m) => m,
268 None => {
269 // bases is empty (shouldn't happen, but let's be safe)
268 None => { // self.bases is empty
270 269 return Ok(());
271 270 }
272 271 };
273 272 // whatever happens, we'll keep at least keepcount of them
274 273 // knowing this gives us a earlier stop condition than
275 274 // going all the way to the root
276 275 let keepcount = revs.iter().filter(|r| **r > start).count();
277 276
278 277 let mut curr = start;
279 278 while curr != NULL_REVISION && revs.len() > keepcount {
280 279 if self.bases.contains(&curr) {
281 280 revs.remove(&curr);
282 281 self.add_parents(curr)?;
283 282 }
284 283 curr -= 1;
285 284 }
286 285 Ok(())
287 286 }
288 287
289 288 /// Add rev's parents to self.bases
290 289 #[inline]
291 290 fn add_parents(&mut self, rev: Revision) -> Result<(), GraphError> {
292 291 // No need to bother the set with inserting NULL_REVISION over and
293 292 // over
294 293 for p in self.graph.parents(rev)?.iter().cloned() {
295 294 if p != NULL_REVISION {
296 295 self.bases.insert(p);
297 296 }
298 297 }
299 298 Ok(())
300 299 }
301 300
302 301 /// Return all the ancestors of revs that are not ancestors of self.bases
303 302 ///
304 303 /// This may include elements from revs.
305 304 ///
306 305 /// Equivalent to the revset (::revs - ::self.bases). Revs are returned in
307 306 /// revision number order, which is a topological order.
308 307 pub fn missing_ancestors(
309 308 &mut self,
310 309 revs: impl IntoIterator<Item = Revision>,
311 310 ) -> Result<Vec<Revision>, GraphError> {
312 311 // just for convenience and comparison with Python version
313 312 let bases_visit = &mut self.bases;
314 313 let mut revs: HashSet<Revision> = revs
315 314 .into_iter()
316 315 .filter(|r| !bases_visit.contains(r))
317 316 .collect();
318 317 let revs_visit = &mut revs;
319 318 let mut both_visit: HashSet<Revision> =
320 319 revs_visit.intersection(&bases_visit).cloned().collect();
321 320 if revs_visit.is_empty() {
322 321 return Ok(Vec::new());
323 322 }
324 323
325 324 let max_bases =
326 325 bases_visit.iter().cloned().max().unwrap_or(NULL_REVISION);
327 326 let max_revs =
328 327 revs_visit.iter().cloned().max().unwrap_or(NULL_REVISION);
329 328 let start = max(max_bases, max_revs);
330 329
331 330 // TODO heuristics for with_capacity()?
332 331 let mut missing: Vec<Revision> = Vec::new();
333 332 for curr in (0..=start).rev() {
334 333 if revs_visit.is_empty() {
335 334 break;
336 335 }
337 336 if both_visit.remove(&curr) {
338 337 // curr's parents might have made it into revs_visit through
339 338 // another path
340 339 for p in self.graph.parents(curr)?.iter().cloned() {
341 340 if p == NULL_REVISION {
342 341 continue;
343 342 }
344 343 revs_visit.remove(&p);
345 344 bases_visit.insert(p);
346 345 both_visit.insert(p);
347 346 }
348 347 } else if revs_visit.remove(&curr) {
349 348 missing.push(curr);
350 349 for p in self.graph.parents(curr)?.iter().cloned() {
351 350 if p == NULL_REVISION {
352 351 continue;
353 352 }
354 353 if bases_visit.contains(&p) {
355 354 // p is already known to be an ancestor of revs_visit
356 355 revs_visit.remove(&p);
357 356 both_visit.insert(p);
358 357 } else if both_visit.contains(&p) {
359 358 // p should have been in bases_visit
360 359 revs_visit.remove(&p);
361 360 bases_visit.insert(p);
362 361 } else {
363 362 // visit later
364 363 revs_visit.insert(p);
365 364 }
366 365 }
367 366 } else if bases_visit.contains(&curr) {
368 367 for p in self.graph.parents(curr)?.iter().cloned() {
369 368 if p == NULL_REVISION {
370 369 continue;
371 370 }
372 371 if revs_visit.remove(&p) || both_visit.contains(&p) {
373 372 // p is an ancestor of bases_visit, and is implicitly
374 373 // in revs_visit, which means p is ::revs & ::bases.
375 374 bases_visit.insert(p);
376 375 both_visit.insert(p);
377 376 } else {
378 377 bases_visit.insert(p);
379 378 }
380 379 }
381 380 }
382 381 }
383 382 missing.reverse();
384 383 Ok(missing)
385 384 }
386 385 }
387 386
388 387 #[cfg(test)]
389 388 mod tests {
390 389
391 390 use super::*;
392 391 use crate::testing::{SampleGraph, VecGraph};
393 392 use std::iter::FromIterator;
394 393
395 394 fn list_ancestors<G: Graph>(
396 395 graph: G,
397 396 initrevs: Vec<Revision>,
398 397 stoprev: Revision,
399 398 inclusive: bool,
400 399 ) -> Vec<Revision> {
401 400 AncestorsIterator::new(graph, initrevs, stoprev, inclusive)
402 401 .unwrap()
403 402 .map(|res| res.unwrap())
404 403 .collect()
405 404 }
406 405
407 406 #[test]
408 407 /// Same tests as test-ancestor.py, without membership
409 408 /// (see also test-ancestor.py.out)
410 409 fn test_list_ancestor() {
411 410 assert_eq!(list_ancestors(SampleGraph, vec![], 0, false), vec![]);
412 411 assert_eq!(
413 412 list_ancestors(SampleGraph, vec![11, 13], 0, false),
414 413 vec![8, 7, 4, 3, 2, 1, 0]
415 414 );
416 415 assert_eq!(
417 416 list_ancestors(SampleGraph, vec![1, 3], 0, false),
418 417 vec![1, 0]
419 418 );
420 419 assert_eq!(
421 420 list_ancestors(SampleGraph, vec![11, 13], 0, true),
422 421 vec![13, 11, 8, 7, 4, 3, 2, 1, 0]
423 422 );
424 423 assert_eq!(
425 424 list_ancestors(SampleGraph, vec![11, 13], 6, false),
426 425 vec![8, 7]
427 426 );
428 427 assert_eq!(
429 428 list_ancestors(SampleGraph, vec![11, 13], 6, true),
430 429 vec![13, 11, 8, 7]
431 430 );
432 431 assert_eq!(
433 432 list_ancestors(SampleGraph, vec![11, 13], 11, true),
434 433 vec![13, 11]
435 434 );
436 435 assert_eq!(
437 436 list_ancestors(SampleGraph, vec![11, 13], 12, true),
438 437 vec![13]
439 438 );
440 439 assert_eq!(
441 440 list_ancestors(SampleGraph, vec![10, 1], 0, true),
442 441 vec![10, 5, 4, 2, 1, 0]
443 442 );
444 443 }
445 444
446 445 #[test]
447 446 /// Corner case that's not directly in test-ancestors.py, but
448 447 /// that happens quite often, as demonstrated by running the whole
449 448 /// suite.
450 449 /// For instance, run tests/test-obsolete-checkheads.t
451 450 fn test_nullrev_input() {
452 451 let mut iter =
453 452 AncestorsIterator::new(SampleGraph, vec![-1], 0, false).unwrap();
454 453 assert_eq!(iter.next(), None)
455 454 }
456 455
457 456 #[test]
458 457 fn test_contains() {
459 458 let mut lazy =
460 459 AncestorsIterator::new(SampleGraph, vec![10, 1], 0, true).unwrap();
461 460 assert!(lazy.contains(1).unwrap());
462 461 assert!(!lazy.contains(3).unwrap());
463 462
464 463 let mut lazy =
465 464 AncestorsIterator::new(SampleGraph, vec![0], 0, false).unwrap();
466 465 assert!(!lazy.contains(NULL_REVISION).unwrap());
467 466 }
468 467
469 468 #[test]
470 469 fn test_peek() {
471 470 let mut iter =
472 471 AncestorsIterator::new(SampleGraph, vec![10], 0, true).unwrap();
473 472 // peek() gives us the next value
474 473 assert_eq!(iter.peek(), Some(10));
475 474 // but it's not been consumed
476 475 assert_eq!(iter.next(), Some(Ok(10)));
477 476 // and iteration resumes normally
478 477 assert_eq!(iter.next(), Some(Ok(5)));
479 478
480 479 // let's drain the iterator to test peek() at the end
481 480 while iter.next().is_some() {}
482 481 assert_eq!(iter.peek(), None);
483 482 }
484 483
485 484 #[test]
486 485 fn test_empty() {
487 486 let mut iter =
488 487 AncestorsIterator::new(SampleGraph, vec![10], 0, true).unwrap();
489 488 assert!(!iter.is_empty());
490 489 while iter.next().is_some() {}
491 490 assert!(!iter.is_empty());
492 491
493 492 let iter =
494 493 AncestorsIterator::new(SampleGraph, vec![], 0, true).unwrap();
495 494 assert!(iter.is_empty());
496 495
497 496 // case where iter.seen == {NULL_REVISION}
498 497 let iter =
499 498 AncestorsIterator::new(SampleGraph, vec![0], 0, false).unwrap();
500 499 assert!(iter.is_empty());
501 500 }
502 501
503 502 /// A corrupted Graph, supporting error handling tests
504 503 #[derive(Clone, Debug)]
505 504 struct Corrupted;
506 505
507 506 impl Graph for Corrupted {
508 507 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
509 508 match rev {
510 509 1 => Ok([0, -1]),
511 510 r => Err(GraphError::ParentOutOfRange(r)),
512 511 }
513 512 }
514 513 }
515 514
516 515 #[test]
517 516 fn test_initrev_out_of_range() {
518 517 // inclusive=false looks up initrev's parents right away
519 518 match AncestorsIterator::new(SampleGraph, vec![25], 0, false) {
520 519 Ok(_) => panic!("Should have been ParentOutOfRange"),
521 520 Err(e) => assert_eq!(e, GraphError::ParentOutOfRange(25)),
522 521 }
523 522 }
524 523
525 524 #[test]
526 525 fn test_next_out_of_range() {
527 526 // inclusive=false looks up initrev's parents right away
528 527 let mut iter =
529 528 AncestorsIterator::new(Corrupted, vec![1], 0, false).unwrap();
530 529 assert_eq!(iter.next(), Some(Err(GraphError::ParentOutOfRange(0))));
531 530 }
532 531
533 532 #[test]
534 533 fn test_lazy_iter_contains() {
535 534 let mut lazy =
536 535 LazyAncestors::new(SampleGraph, vec![11, 13], 0, false).unwrap();
537 536
538 537 let revs: Vec<Revision> = lazy.iter().map(|r| r.unwrap()).collect();
539 538 // compare with iterator tests on the same initial revisions
540 539 assert_eq!(revs, vec![8, 7, 4, 3, 2, 1, 0]);
541 540
542 541 // contains() results are correct, unaffected by the fact that
543 542 // we consumed entirely an iterator out of lazy
544 543 assert_eq!(lazy.contains(2), Ok(true));
545 544 assert_eq!(lazy.contains(9), Ok(false));
546 545 }
547 546
548 547 #[test]
549 548 fn test_lazy_contains_iter() {
550 549 let mut lazy =
551 550 LazyAncestors::new(SampleGraph, vec![11, 13], 0, false).unwrap(); // reminder: [8, 7, 4, 3, 2, 1, 0]
552 551
553 552 assert_eq!(lazy.contains(2), Ok(true));
554 553 assert_eq!(lazy.contains(6), Ok(false));
555 554
556 555 // after consumption of 2 by the inner iterator, results stay
557 556 // consistent
558 557 assert_eq!(lazy.contains(2), Ok(true));
559 558 assert_eq!(lazy.contains(5), Ok(false));
560 559
561 560 // iter() still gives us a fresh iterator
562 561 let revs: Vec<Revision> = lazy.iter().map(|r| r.unwrap()).collect();
563 562 assert_eq!(revs, vec![8, 7, 4, 3, 2, 1, 0]);
564 563 }
565 564
566 565 #[test]
567 566 /// Test constructor, add/get bases and heads
568 567 fn test_missing_bases() -> Result<(), GraphError> {
569 568 let mut missing_ancestors =
570 569 MissingAncestors::new(SampleGraph, [5, 3, 1, 3].iter().cloned());
571 570 let mut as_vec: Vec<Revision> =
572 571 missing_ancestors.get_bases().iter().cloned().collect();
573 572 as_vec.sort();
574 573 assert_eq!(as_vec, [1, 3, 5]);
575 574
576 575 missing_ancestors.add_bases([3, 7, 8].iter().cloned());
577 576 as_vec = missing_ancestors.get_bases().iter().cloned().collect();
578 577 as_vec.sort();
579 578 assert_eq!(as_vec, [1, 3, 5, 7, 8]);
580 579
581 580 as_vec = missing_ancestors.bases_heads()?.iter().cloned().collect();
582 581 as_vec.sort();
583 582 assert_eq!(as_vec, [3, 5, 7, 8]);
584 583 Ok(())
585 584 }
586 585
587 586 fn assert_missing_remove(
588 587 bases: &[Revision],
589 588 revs: &[Revision],
590 589 expected: &[Revision],
591 590 ) {
592 591 let mut missing_ancestors =
593 592 MissingAncestors::new(SampleGraph, bases.iter().cloned());
594 593 let mut revset: HashSet<Revision> = revs.iter().cloned().collect();
595 594 missing_ancestors
596 595 .remove_ancestors_from(&mut revset)
597 596 .unwrap();
598 597 let mut as_vec: Vec<Revision> = revset.into_iter().collect();
599 598 as_vec.sort();
600 599 assert_eq!(as_vec.as_slice(), expected);
601 600 }
602 601
603 602 #[test]
604 603 fn test_missing_remove() {
605 604 assert_missing_remove(
606 605 &[1, 2, 3, 4, 7],
607 606 Vec::from_iter(1..10).as_slice(),
608 607 &[5, 6, 8, 9],
609 608 );
610 609 assert_missing_remove(&[10], &[11, 12, 13, 14], &[11, 12, 13, 14]);
611 610 assert_missing_remove(&[7], &[1, 2, 3, 4, 5], &[3, 5]);
612 611 }
613 612
614 613 fn assert_missing_ancestors(
615 614 bases: &[Revision],
616 615 revs: &[Revision],
617 616 expected: &[Revision],
618 617 ) {
619 618 let mut missing_ancestors =
620 619 MissingAncestors::new(SampleGraph, bases.iter().cloned());
621 620 let missing = missing_ancestors
622 621 .missing_ancestors(revs.iter().cloned())
623 622 .unwrap();
624 623 assert_eq!(missing.as_slice(), expected);
625 624 }
626 625
627 626 #[test]
628 627 fn test_missing_ancestors() {
629 628 // examples taken from test-ancestors.py by having it run
630 629 // on the same graph (both naive and fast Python algs)
631 630 assert_missing_ancestors(&[10], &[11], &[3, 7, 11]);
632 631 assert_missing_ancestors(&[11], &[10], &[5, 10]);
633 632 assert_missing_ancestors(&[7], &[9, 11], &[3, 6, 9, 11]);
634 633 }
635 634
636 635 /// An interesting case found by a random generator similar to
637 636 /// the one in test-ancestor.py. An early version of Rust MissingAncestors
638 637 /// failed this, yet none of the integration tests of the whole suite
639 638 /// catched it.
640 639 #[test]
641 640 fn test_remove_ancestors_from_case1() {
642 641 let graph: VecGraph = vec![
643 642 [NULL_REVISION, NULL_REVISION],
644 643 [0, NULL_REVISION],
645 644 [1, 0],
646 645 [2, 1],
647 646 [3, NULL_REVISION],
648 647 [4, NULL_REVISION],
649 648 [5, 1],
650 649 [2, NULL_REVISION],
651 650 [7, NULL_REVISION],
652 651 [8, NULL_REVISION],
653 652 [9, NULL_REVISION],
654 653 [10, 1],
655 654 [3, NULL_REVISION],
656 655 [12, NULL_REVISION],
657 656 [13, NULL_REVISION],
658 657 [14, NULL_REVISION],
659 658 [4, NULL_REVISION],
660 659 [16, NULL_REVISION],
661 660 [17, NULL_REVISION],
662 661 [18, NULL_REVISION],
663 662 [19, 11],
664 663 [20, NULL_REVISION],
665 664 [21, NULL_REVISION],
666 665 [22, NULL_REVISION],
667 666 [23, NULL_REVISION],
668 667 [2, NULL_REVISION],
669 668 [3, NULL_REVISION],
670 669 [26, 24],
671 670 [27, NULL_REVISION],
672 671 [28, NULL_REVISION],
673 672 [12, NULL_REVISION],
674 673 [1, NULL_REVISION],
675 674 [1, 9],
676 675 [32, NULL_REVISION],
677 676 [33, NULL_REVISION],
678 677 [34, 31],
679 678 [35, NULL_REVISION],
680 679 [36, 26],
681 680 [37, NULL_REVISION],
682 681 [38, NULL_REVISION],
683 682 [39, NULL_REVISION],
684 683 [40, NULL_REVISION],
685 684 [41, NULL_REVISION],
686 685 [42, 26],
687 686 [0, NULL_REVISION],
688 687 [44, NULL_REVISION],
689 688 [45, 4],
690 689 [40, NULL_REVISION],
691 690 [47, NULL_REVISION],
692 691 [36, 0],
693 692 [49, NULL_REVISION],
694 693 [NULL_REVISION, NULL_REVISION],
695 694 [51, NULL_REVISION],
696 695 [52, NULL_REVISION],
697 696 [53, NULL_REVISION],
698 697 [14, NULL_REVISION],
699 698 [55, NULL_REVISION],
700 699 [15, NULL_REVISION],
701 700 [23, NULL_REVISION],
702 701 [58, NULL_REVISION],
703 702 [59, NULL_REVISION],
704 703 [2, NULL_REVISION],
705 704 [61, 59],
706 705 [62, NULL_REVISION],
707 706 [63, NULL_REVISION],
708 707 [NULL_REVISION, NULL_REVISION],
709 708 [65, NULL_REVISION],
710 709 [66, NULL_REVISION],
711 710 [67, NULL_REVISION],
712 711 [68, NULL_REVISION],
713 712 [37, 28],
714 713 [69, 25],
715 714 [71, NULL_REVISION],
716 715 [72, NULL_REVISION],
717 716 [50, 2],
718 717 [74, NULL_REVISION],
719 718 [12, NULL_REVISION],
720 719 [18, NULL_REVISION],
721 720 [77, NULL_REVISION],
722 721 [78, NULL_REVISION],
723 722 [79, NULL_REVISION],
724 723 [43, 33],
725 724 [81, NULL_REVISION],
726 725 [82, NULL_REVISION],
727 726 [83, NULL_REVISION],
728 727 [84, 45],
729 728 [85, NULL_REVISION],
730 729 [86, NULL_REVISION],
731 730 [NULL_REVISION, NULL_REVISION],
732 731 [88, NULL_REVISION],
733 732 [NULL_REVISION, NULL_REVISION],
734 733 [76, 83],
735 734 [44, NULL_REVISION],
736 735 [92, NULL_REVISION],
737 736 [93, NULL_REVISION],
738 737 [9, NULL_REVISION],
739 738 [95, 67],
740 739 [96, NULL_REVISION],
741 740 [97, NULL_REVISION],
742 741 [NULL_REVISION, NULL_REVISION],
743 742 ];
744 743 let problem_rev = 28 as Revision;
745 744 let problem_base = 70 as Revision;
746 745 // making the problem obvious: problem_rev is a parent of problem_base
747 746 assert_eq!(graph.parents(problem_base).unwrap()[1], problem_rev);
748 747
749 748 let mut missing_ancestors: MissingAncestors<VecGraph> =
750 749 MissingAncestors::new(
751 750 graph,
752 751 [60, 26, 70, 3, 96, 19, 98, 49, 97, 47, 1, 6]
753 752 .iter()
754 753 .cloned(),
755 754 );
756 755 assert!(missing_ancestors.bases.contains(&problem_base));
757 756
758 757 let mut revs: HashSet<Revision> =
759 758 [4, 12, 41, 28, 68, 38, 1, 30, 56, 44]
760 759 .iter()
761 760 .cloned()
762 761 .collect();
763 762 missing_ancestors.remove_ancestors_from(&mut revs).unwrap();
764 763 assert!(!revs.contains(&problem_rev));
765 764 }
766 765
767 766 }
General Comments 0
You need to be logged in to leave comments. Login now