dag: add a test about strip->reinsert->build_higher_level_segment bug

Summary: See the added test.

Differential Revision: D35548450

fbshipit-source-id: a1e1d889b88bfcf15030659a0dd4869b7f38ff80
This commit is contained in:
Jun Wu 2022-04-11 12:02:43 -07:00 committed by Facebook GitHub Bot
parent 8c7ea28d84
commit a452ebfe49
2 changed files with 130 additions and 0 deletions

View File

@ -352,6 +352,53 @@ impl TestDag {
format!("{}{}\n{}", all_str, iddag_state, idmap_state)
}
#[cfg(test)]
/// Dump Dag segments as ASCII string.
pub fn dump_segments_ascii(&self) -> String {
use crate::Id;
use crate::IdSet;
use crate::IdSpan;
use std::collections::HashSet;
let span_iter = |span: IdSpan| IdSet::from_spans(vec![span]).into_iter().rev();
let iddag = &self.dag.dag;
let all_ids = iddag.all_ids_in_groups(&Group::ALL).unwrap();
let max_level = iddag.max_level().unwrap();
let mut output = String::new();
for level in 0..=max_level {
output = format!("{}\n Lv{}:", output.trim_end(), level);
for span in all_ids.iter_span_asc() {
output += " |";
let segments = iddag.segments_in_span_ascending(*span, level).unwrap();
let segment_ids: HashSet<Id> = segments
.iter()
.flat_map(|s| span_iter(s.span().unwrap()))
.collect();
let segment_highs: HashSet<Id> =
segments.iter().map(|s| s.high().unwrap()).collect();
for id in span_iter(*span) {
let id_str = format!("{:?}", id);
if segment_ids.contains(&id) {
output += &id_str
} else {
let space = " ".repeat(id_str.len());
output += &space;
};
output.push(
if segment_highs.contains(&id)
|| (segment_ids.contains(&(id + 1)) && !segment_ids.contains(&id))
{
'|'
} else {
' '
},
);
}
}
}
output.trim_end().to_string()
}
async fn validate(&self) {
// All vertexes should be accessible, and round-trip through IdMap.
let mut iter = self.dag.all().await.unwrap().iter().await.unwrap();

View File

@ -105,3 +105,86 @@ P->C: 1->4, 4->N0
// Strip C+F. B is no longer lazy.
assert_eq!(strip("C F").await, "<spans [0:1]>\nLv0: RH0-1[]\n1->B");
}
#[tokio::test]
async fn test_reinsert_then_create_higher_level() {
// Test re-inserting a stripped "gap" and then creating higher level segments.
//
// Initial state:
//
// Lv0: |0|1|2|3|4|5|6|7|8|9|10|
// Lv1: |-|---|-|---|---|---|
// Lv2: |-----|-|-------|
//
// Strip 3:
//
// Lv0: |0|1|2| |4|5|6|7|8|9|10|
// Lv1: |-|---| |---|---|---|
// Lv2: |-----| |-------|
//
// Reinsert 3 (Note: 3 no longer has Lv1 and Lv2 segments):
//
// Lv0: |0|1|2|3|4|5|6|7|8|9|10|
// Lv1: |-|---| |---|---|---|
// Lv2: |-----| |-------|
//
// Trigger Lv3 segment creation:
//
// Lv0: |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|
// Lv1: |-|---| |---|---|---|-----|-----|
// Lv2: |-----| |-------|---------|
// Lv3: ? |-----------------|
let mut dag = TestDag::new_with_segment_size(2);
// Use "Z" as an extra parent to avoid merging flat segments.
dag.drawdag("Z", &[]);
for i in 1..=10 {
let ascii = match i {
1 | 3 | 4 => format!("A{i}", i = i),
_ => format!("Z-A{i} A{p}-A{i}", p = i - 1, i = i),
};
dag.drawdag(&ascii, &[]);
}
dag.flush("").await;
assert_eq!(
dag.dump_segments_ascii(),
r#"
Lv0: |N0|N1|N2|N3|N4|N5|N6|N7|N8|N9|N10|
Lv1: |N0|N1 N2|N3|N4 N5|N6 N7|N8 N9|
Lv2: |N0 N1 N2|N3|N4 N5 N6 N7|"#
);
// Strip 3.
dag.strip("A3").await;
assert_eq!(
dag.dump_segments_ascii(),
r#"
Lv0: |N0|N1|N2| |N4|N5|N6|N7|N8|N9|N10|
Lv1: |N0|N1 N2| |N4 N5|N6 N7|N8 N9|
Lv2: |N0 N1 N2| |N4 N5 N6 N7|"#
);
// Reinsert 3. Note A3 does not have Lv1 or Lv2 segments.
dag.drawdag("A2-A3 Z-A3", &[]);
assert_eq!(
dag.dump_segments_ascii(),
r#"
Lv0: |N0|N1|N2|N3|N4|N5|N6|N7|N8|N9|N10|
Lv1: |N0|N1 N2| |N4 N5|N6 N7|N8 N9|
Lv2: |N0 N1 N2| |N4 N5 N6 N7|"#
);
// Try to create Lv3 segments.
dag.drawdag("A10-A11 A3-A11", &[]);
for i in 12..=14 {
// FIXME: Crash when creating Lv3 segment.
// Bug("level 3 segments [RN0-N2[], RN4-N7[N0]] are not sorted or connected!")
if i == 14 {
break;
}
let ascii = format!("Z-A{i} A{p}-A{i}", p = i - 1, i = i);
dag.drawdag(&ascii, &[]);
}
}