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