Skip to main content

dag/
tests.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8use nonblocking::non_blocking_result as r;
9use tempfile::tempdir;
10pub use test_dag::TestDag;
11
12pub use self::drawdag::DrawDag;
13use crate::id::Group;
14use crate::id::Vertex;
15use crate::ops::DagAddHeads;
16use crate::ops::DagPersistent;
17use crate::ops::ImportAscii;
18#[cfg(feature = "render")]
19use crate::render::render_dag;
20use crate::set::SyncSetQuery;
21use crate::Dag;
22use crate::DagAlgorithm;
23use crate::IdMap;
24#[cfg(test)]
25use crate::IdSet;
26use crate::Result;
27use crate::Set;
28
29mod drawdag;
30mod test_dag;
31
32#[cfg(test)]
33mod test_add_heads;
34
35#[cfg(test)]
36mod test_bisect;
37
38#[cfg(test)]
39mod test_integrity;
40
41#[cfg(test)]
42mod test_sparse;
43
44#[cfg(test)]
45mod test_strip;
46
47#[cfg(test)]
48mod test_to_parents;
49
50#[cfg(test)]
51mod test_discontinuous;
52
53#[cfg(test)]
54mod test_server;
55
56#[cfg(test)]
57mod test_virtual;
58
59#[cfg(test)]
60pub mod dummy_dag;
61
62#[cfg(test)]
63pub(crate) use test_dag::ProtocolMonitor;
64
65use crate::dag::MemDag;
66#[cfg(test)]
67use crate::iddag::FirstAncestorConstraint;
68use crate::ops::IdConvert;
69#[cfg(test)]
70use crate::protocol::Process;
71#[cfg(test)]
72use crate::protocol::RequestLocationToName;
73#[cfg(test)]
74use crate::protocol::RequestNameToLocation;
75#[cfg(test)]
76use crate::render::render_segment_dag;
77#[cfg(test)]
78use crate::Id;
79#[cfg(test)]
80use crate::VertexListWithOptions;
81
82// Example from segmented-changelog.pdf
83// - DAG1: page 10
84// - DAG2: page 11
85// - DAG3: page 17
86// - DAG4: page 18
87// - DAG5: page 19
88
89static ASCII_DAG1: &str = r#"
90                C-D-\     /--I--J--\
91            A-B------E-F-G-H--------K--L"#;
92
93static ASCII_DAG2: &str = r#"
94                      T /---------------N--O---\           T
95                     / /                        \           \
96               /----E-F-\    /-------L--M--------P--\     S--U---\
97            A-B-C-D------G--H--I--J--K---------------Q--R---------V--W
98                                   \--N"#;
99
100static ASCII_DAG3: &str = r#"
101              B---D---F--\
102            A---C---E-----G"#;
103
104static ASCII_DAG4: &str = r#"
105             D  C  B
106              \  \  \
107            A--E--F--G"#;
108
109static ASCII_DAG5: &str = r#"
110        B---D---F
111         \   \   \
112      A---C---E---G"#;
113
114fn test_dag_sort_version<T: DagAlgorithm + IdConvert>(dag: &T) -> Result<()> {
115    // Test that sort() returns a set with dag and map hints assigned.
116    let sets = [
117        set(""),
118        set("A C B"),
119        r(from_ascii(MemDag::new(), ASCII_DAG3).sort(&set("C A B")))?,
120    ];
121    for set in sets {
122        let sorted = r(dag.sort(&set))?;
123        let hints = sorted.hints();
124        assert_eq!(hints.dag_version(), Some(dag.dag_version()));
125        assert_eq!(hints.id_map_version(), Some(dag.map_version()));
126    }
127
128    Ok(())
129}
130
131fn test_generic_dag1<T: DagAlgorithm + DagAddHeads + IdConvert>(dag: T) -> Result<T> {
132    let dag = from_ascii(dag, ASCII_DAG1);
133    assert_eq!(expand(r(dag.all())?), "A B C D E F G H I J K L");
134    assert_eq!(expand(r(dag.dirty())?), "A B C D E F G H I J K L");
135    assert_eq!(expand(r(dag.ancestors(set("H I")))?), "A B C D E F G H I");
136    assert_eq!(expand(r(dag.sort(&set("H E A")))?.skip(1).take(2)), "A E");
137    assert_eq!(expand(r(dag.first_ancestors(set("F")))?), "A B E F");
138    assert_eq!(expand(r(dag.parents(set("H I E")))?), "B D G");
139    assert_eq!(expand(r(dag.children(set("G D L")))?), "E H I");
140    assert_eq!(expand(r(dag.merges(r(dag.all())?))?), "E K");
141    assert_eq!(expand(r(dag.merges(set("E F J K")))?), "E K");
142    assert_eq!(expand(r(dag.merges(set("A B D F H J L")))?), "");
143    assert_eq!(expand(r(dag.roots(set("A B E F C D I J")))?), "A C I");
144    assert_eq!(expand(r(dag.heads(set("A B E F C D I J")))?), "F J");
145    assert_eq!(expand(r(dag.gca_all(set("J K H")))?), "G");
146
147    test_dag_sort_version(&dag)?;
148
149    Ok(dag)
150}
151
152fn test_generic_dag_beautify<D: DagAlgorithm + DagAddHeads>(
153    new_dag: &impl Fn() -> D,
154) -> Result<()> {
155    let ascii = r#"
156        A C
157        | |
158        B D
159        |/
160        E"#;
161    let order = ["B", "D", "A", "C"];
162    let dag = from_ascii_with_heads(new_dag(), ascii, Some(&order));
163    assert_eq!(expand(r(dag.all())?), "A B C D E");
164
165    let dag2 = r(dag.beautify(None))?;
166    assert_eq!(expand(r(dag2.all())?), "A B C D E");
167
168    let dag3 = r(dag.beautify(Some(set("A B E"))))?;
169    assert_eq!(expand(r(dag3.all())?), "A B C D E");
170
171    let dag4 = r(dag.beautify(Some(set("C D E"))))?;
172    assert_eq!(expand(r(dag4.all())?), "A B C D E");
173
174    let ascii = r#"
175        A G
176        |/
177        B F
178        |/
179        C E
180        |/
181        D"#;
182    let order = ["C", "E", "G", "F", "A"];
183    let dag = from_ascii_with_heads(new_dag(), ascii, Some(&order));
184    assert_eq!(expand(r(dag.all())?), "A B C D E F G");
185
186    let dag2 = r(dag.beautify(None))?;
187    assert_eq!(expand(r(dag2.all())?), "A B C D E F G");
188
189    let dag3 = r(dag.beautify(Some(r(dag.ancestors(set("A")))?)))?;
190    assert_eq!(expand(r(dag3.all())?), "A B C D E F G");
191
192    let ascii = r#"
193        A---B---C---D---E---F---G
194             \
195              H---I---J---K
196                   \
197                    L "#;
198    let order = ["D", "J", "L", "K", "G"];
199    let dag = from_ascii_with_heads(new_dag(), ascii, Some(&order));
200    assert_eq!(expand(r(dag.all())?), "A B C D E F G H I J K L");
201
202    let dag2 = r(dag.beautify(None))?;
203    assert_eq!(expand(r(dag2.all())?), "A B C D E F G H I J K L");
204
205    Ok(())
206}
207
208fn test_generic_dag_reachable_roots(dag: impl DagAlgorithm + DagAddHeads) -> Result<()> {
209    let ascii = r#"
210         Z
211         |\
212         D |
213         | F
214         C |
215         | E
216         B |
217         |/
218         A
219         "#;
220    let dag = from_ascii_with_heads(dag, ascii, Some(&["Z"][..]));
221
222    // B is not reachable without going through other roots (C).
223    // A is reachable through Z -> F -> E -> A.
224    assert_eq!(
225        expand(r(dag.reachable_roots(set("A B C"), set("Z")))?),
226        "A C"
227    );
228
229    // A, E are not reachable without going through other roots (C, F).
230    assert_eq!(
231        expand(r(dag.reachable_roots(set("A C E F"), set("Z")))?),
232        "C F"
233    );
234
235    // roots and heads overlap.
236    assert_eq!(
237        expand(r(dag.reachable_roots(set("A B C D E F Z"), set("D F")))?),
238        "D F"
239    );
240
241    // E, F are not reachable.
242    assert_eq!(
243        expand(r(dag.reachable_roots(set("A B E F"), set("D")))?),
244        "B"
245    );
246
247    // "Bogus" root "Z".
248    assert_eq!(expand(r(dag.reachable_roots(set("A Z"), set("C")))?), "A");
249
250    Ok(())
251}
252
253// This test is specific to the implementation in this crate, and requires
254// >2 parents support.
255fn test_specific_dag_import(dag: impl DagAlgorithm + DagAddHeads) -> Result<()> {
256    let ascii = r#"
257            J K
258           /|\|\
259          G H I H
260          |/|/|
261          E F |
262         /|/|\|
263        A B C D"#;
264    let dag1 = from_ascii_with_heads(dag, ascii, Some(&["J", "K"][..]));
265
266    let dir = tempdir().unwrap();
267    let mut dag2 = Dag::open(dir.path())?;
268    r(dag2.import_and_flush(&dag1, set("J")))?;
269    #[cfg(feature = "render")]
270    assert_eq!(
271        render(&dag2),
272        r#"
273            K
274            ├─╮
275            │ │ J
276            ╭─┬─┤
277            │ I │
278            │ ├───╮
279            H │ │ │
280            ├─────╮
281            │ │ │ F
282            │ ╭───┼─╮
283            │ D │ │ │
284            │   │ │ │
285            │   │ │ C
286            │   │ │
287            │   G │
288            ├───╯ │
289            E     │
290            ├─────╮
291            │     B
292293            A"#
294    );
295
296    // Check that dag2 is actually flushed to disk.
297    let dag3 = Dag::open(dir.path())?;
298    #[cfg(feature = "render")]
299    assert_eq!(
300        render(&dag3),
301        r#"
302            K
303            ├─╮
304            │ │ J
305            ╭─┬─┤
306            │ I │
307            │ ├───╮
308            H │ │ │
309            ├─────╮
310            │ │ │ F
311            │ ╭───┼─╮
312            │ D │ │ │
313            │   │ │ │
314            │   │ │ C
315            │   │ │
316            │   G │
317            ├───╯ │
318            E     │
319            ├─────╮
320            │     B
321322            A"#
323    );
324    Ok(())
325}
326
327fn test_generic_dag2<T: DagAlgorithm + DagAddHeads>(dag: T) -> Result<T> {
328    let ascii = r#"
329            J K
330           / \|\
331          G H I H
332          |/|/|
333          E F |
334         /|/| |
335        A B C D"#;
336    let dag = from_ascii_with_heads(dag, ascii, Some(&["J", "K"][..]));
337
338    let v = |name: &str| -> Vertex { Vertex::copy_from(name.as_bytes()) };
339
340    assert_eq!(expand(r(dag.all())?), "A B C D E F G H I J K");
341    assert_eq!(expand(r(dag.ancestors(set("H I")))?), "A B C D E F H I");
342    assert_eq!(expand(r(dag.first_ancestors(set("H I")))?), "A D E H I");
343    assert_eq!(expand(r(dag.first_ancestors(set("J G D")))?), "A D E G J");
344    assert_eq!(expand(r(dag.parents(set("H I E")))?), "A B D E F");
345    assert_eq!(r(dag.first_ancestor_nth(v("H"), 2))?.unwrap(), v("A"));
346    assert!(r(dag.first_ancestor_nth(v("H"), 3))?.is_none());
347    assert_eq!(expand(r(dag.heads(set("E H F K I D")))?), "K");
348    assert_eq!(expand(r(dag.children(set("E F I")))?), "G H I J K");
349    assert_eq!(expand(r(dag.merges(r(dag.all())?))?), "E F H I J K");
350    assert_eq!(expand(r(dag.merges(set("E H G D I")))?), "E H I");
351    assert_eq!(expand(r(dag.roots(set("E G H J I K D")))?), "D E");
352    assert_eq!(r(dag.gca_one(set("J K")))?, Some(v("I")));
353    assert_eq!(expand(r(dag.gca_all(set("J K")))?), "E I");
354    assert_eq!(expand(r(dag.common_ancestors(set("G H")))?), "A B E");
355    assert!(r(dag.is_ancestor(v("B"), v("K")))?);
356    assert!(!r(dag.is_ancestor(v("K"), v("B")))?);
357    assert_eq!(expand(r(dag.heads_ancestors(set("A E F D G")))?), "D F G");
358    assert_eq!(expand(r(dag.range(set("A"), set("K")))?), "A E H K");
359    assert_eq!(expand(r(dag.only(set("I"), set("G")))?), "C D F I");
360    let (reachable, unreachable) = r(dag.only_both(set("I"), set("G")))?;
361    assert_eq!(expand(reachable), "C D F I");
362    assert_eq!(expand(unreachable), expand(r(dag.ancestors(set("G")))?));
363    assert_eq!(expand(r(dag.descendants(set("F E")))?), "E F G H I J K");
364
365    assert!(r(dag.is_ancestor(v("B"), v("J")))?);
366    assert!(r(dag.is_ancestor(v("F"), v("F")))?);
367    assert!(!r(dag.is_ancestor(v("K"), v("I")))?);
368
369    Ok(dag)
370}
371
372#[test]
373fn test_import_ascii_with_vertex_fn() {
374    let ascii = r#"
375        B C
376        |/
377        A
378    "#;
379    let mut dag = MemDag::new();
380    let vertex_fn = |s: &str| -> Vertex {
381        let mut bytes = s.as_bytes().to_vec();
382        bytes.push(b'0');
383        Vertex::copy_from(&bytes)
384    };
385    dag.import_ascii_with_vertex_fn(ascii, vertex_fn).unwrap();
386    assert_eq!(expand(r(dag.all()).unwrap()), "A0 B0 C0");
387}
388
389#[test]
390fn test_mem_dag() {
391    let new_dag = MemDag::new;
392    test_generic_dag(&new_dag);
393    test_specific_dag_import(new_dag()).unwrap();
394}
395
396#[test]
397fn test_dag() {
398    use std::sync::atomic::AtomicUsize;
399    use std::sync::atomic::Ordering;
400
401    let dir = tempdir().unwrap();
402    let counter = AtomicUsize::new(0);
403    let new_dag = move || {
404        let count = counter.fetch_add(1, Ordering::AcqRel);
405        Dag::open(dir.path().join(count.to_string())).unwrap()
406    };
407    test_generic_dag(&new_dag);
408    test_specific_dag_import(new_dag()).unwrap();
409}
410
411#[test]
412fn test_protocols() {
413    let mut built = build_segments(ASCII_DAG1, "A C E L", 3);
414    assert_eq!(
415        built.ascii[3],
416        r#"
417                1-2-\     /--7--8--\
418            0-3------4-5-6-9--------10-11
419Lv0: RH0-0[] R1-2[] 3-3[0] H4-8[3, 2] 9-9[6] H10-11[9, 8]
420Lv1: R0-0[] R1-8[0]"#
421    );
422
423    // Replace "[66]" to "B", "[67]" to "C", etc.
424    let replace = |mut s: String| -> String {
425        for ch in "ABCDEFGHIJKL".chars() {
426            s = s.replace(&format!("[{}]", ch as u8), &format!("{}", ch));
427        }
428        s
429    };
430
431    // [Id] -> RequestLocationToName (useful for getting commit hashes from ids).
432    // 3 (D) and 9 (J) are p2 that cannot be resolved.
433    let ids: Vec<Id> = b"ABCEFGHI"
434        .iter()
435        .map(|&b| built.name_dag.map.find_id_by_name(&[b]).unwrap().unwrap())
436        .collect();
437    let ids = IdSet::from_spans(ids);
438    let request1: RequestLocationToName =
439        r((&built.name_dag.map, &built.name_dag.dag).process(ids)).unwrap();
440    assert_eq!(
441        replace(dbg(&request1)),
442        "RequestLocationToName { paths: [L~2, J~1(+5), D~1, B~1] }"
443    );
444
445    // [name] -> RequestNameToLocation (useful for getting ids from commit hashes).
446    let names = b"ABCEFGHI"
447        .iter()
448        .map(|&b| Vertex::copy_from(&[b]))
449        .collect();
450    let request2: RequestNameToLocation =
451        r((&built.name_dag.map, &built.name_dag.dag).process(names)).unwrap();
452    assert_eq!(
453        replace(dbg(&request2)),
454        "RequestNameToLocation { names: [A, B, C, E, F, G, H, I], heads: [L] }"
455    );
456
457    // RequestLocationToName -> ResponseIdNamePair
458    let response1 = r((&built.name_dag.map, &built.name_dag.dag).process(request1)).unwrap();
459    assert_eq!(
460        replace(dbg(&response1)),
461        "ResponseIdNamePair { path_names: [(L~2, [H]), (J~1(+5), [I, G, F, E, B]), (D~1, [C]), (B~1, [A])] }"
462    );
463
464    // RequestNameToLocation -> ResponseIdNamePair
465    // Only B, D, H, J, L are used since they are "universally known".
466    let response2 = r((&built.name_dag.map, &built.name_dag.dag).process(request2)).unwrap();
467    assert_eq!(
468        replace(dbg(&response2)),
469        "ResponseIdNamePair { path_names: [(B~1, [A]), (H~4, [B]), (D~1, [C]), (H~3, [E]), (H~2, [F]), (H~1, [G]), (L~2, [H]), (J~1, [I])] }"
470    );
471
472    // Applying responses to IdMap. Should not cause errors.
473    r((&mut built.name_dag.map, &built.name_dag.dag).process(response1)).unwrap();
474    r((&mut built.name_dag.map, &built.name_dag.dag).process(response2.clone())).unwrap();
475
476    // Try applying response2 to a sparse IdMap.
477    // Prepare the sparse IdMap.
478    let mut sparse_id_map = IdMap::open(built.dir.path().join("sparse-id")).unwrap();
479    r(built
480        .name_dag
481        .dag
482        .write_sparse_idmap(&built.name_dag.map, &mut sparse_id_map))
483    .unwrap();
484    assert_eq!(
485        dbg(&sparse_id_map),
486        "IdMap {\n  D: 2,\n  B: 3,\n  J: 8,\n  H: 9,\n  L: 11,\n}\n"
487    );
488    // Apply response2.
489    r((&mut sparse_id_map, &built.name_dag.dag).process(response2)).unwrap();
490    assert_eq!(
491        dbg(&sparse_id_map),
492        r#"IdMap {
493  A: 0,
494  C: 1,
495  D: 2,
496  B: 3,
497  E: 4,
498  F: 5,
499  G: 6,
500  I: 7,
501  J: 8,
502  H: 9,
503  L: 11,
504}
505"#
506    );
507}
508
509#[test]
510fn test_segment_non_master() {
511    let ascii = r#"
512a----b----c----d----e----f----g----------h----i
513     \                    \             /
514      h---i---j---k        l---m---n---o
515               \                \
516                -----------------p---q"#;
517    let built = build_segments(ascii, "i q", 3);
518    assert_eq!(
519        built.ascii[0],
520        r#"
521N0---N1---N2---N3---N4---N5---N6---------N11--N12
522     \                    \             /
523      N11-N12-j---k        N7--N8--N9--N10
524               \                \
525                -----------------p---q
526Lv0: RN0-N6[] N7-N10[N5] N11-N12[N0, N6, N10]"#
527    );
528    assert_eq!(
529        built.ascii[1],
530        r#"
531N0---N1---N2---N3---N4---N5---N6---------N11--N12
532     \                    \             /
533      N11-N12-N13-k        N7--N8--N9--N10
534               \                \
535                -----------------N14-N15
536Lv0: RN0-N6[] N7-N10[N5] N11-N12[N0, N6, N10] N13-N13[N12] N14-N15[N13, N8]
537Lv1: RN0-N12[]"#
538    );
539}
540
541#[test]
542fn test_segment_examples() {
543    assert_eq!(
544        build_segments(ASCII_DAG1, "L", 3).ascii[0],
545        r#"
546                2-3-\     /--8--9--\
547            0-1------4-5-6-7--------10-11
548Lv0: RH0-1[] R2-3[] H4-7[1, 3] 8-9[6] H10-11[7, 9]
549Lv1: R0-7[]"#
550    );
551
552    assert_eq!(
553        build_segments(ASCII_DAG2, "W", 3).ascii[0],
554        r#"
555                      19/---------------13-14--\           19
556                     / /                        \           \
557               /----4-5-\    /-------11-12-------15-\     18-20--\
558            0-1-2-3------6--7--8--9--10--------------16-17--------21-22
559                                   \--13
560Lv0: RH0-3[] 4-5[1] H6-10[3, 5] 11-12[7] 13-14[5, 9] 15-15[12, 14] H16-17[10, 15] R18-18[] 19-19[4] 20-20[18, 19] H21-22[17, 20]
561Lv1: R0-10[] 11-15[7, 5, 9] 16-17[10, 15] R18-20[4]
562Lv2: R0-17[]"#
563    );
564
565    assert_eq!(
566        build_segments(ASCII_DAG3, "G", 3).ascii[0],
567        r#"
568              3---4---5--\
569            0---1---2-----6
570Lv0: RH0-2[] R3-5[] H6-6[2, 5]"#
571    );
572
573    assert_eq!(
574        build_segments(ASCII_DAG4, "G", 3).ascii[0],
575        r#"
576             3  1  0
577              \  \  \
578            2--4--5--6
579Lv0: RH0-0[] R1-1[] R2-2[] R3-3[] 4-4[2, 3] 5-5[1, 4] H6-6[0, 5]
580Lv1: R0-0[] R1-1[] R2-4[]"#
581    );
582
583    assert_eq!(
584        build_segments(ASCII_DAG5, "G", 3).ascii[0],
585        r#"
586        1---3---5
587         \   \   \
588      0---2---4---6
589Lv0: RH0-0[] R1-1[] H2-2[0, 1] 3-3[1] H4-4[2, 3] 5-5[3] H6-6[4, 5]
590Lv1: R0-2[] 3-4[1, 2]"#
591    );
592
593    // Examples outside segmented-changelog.pdf
594
595    // For this graph, the numbers should look continuous in one direction.
596    let ascii_dag = r#"
597            Z---E--M--J--C
598                 \  \  \  \
599                  O--T--D--L
600                   \  \  \  \
601                    K--H--P--W
602                     \  \  \  \
603                      X--R--U--V
604                       \  \  \  \
605                        A--N--S--Y"#;
606    assert_eq!(
607        build_segments(ascii_dag, "Y", 3).ascii[0],
608        r#"
609            0---1--6--11-16
610                 \  \  \  \
611                  2--7--12-17
612                   \  \  \  \
613                    3--8--13-18
614                     \  \  \  \
615                      4--9--14-19
616                       \  \  \  \
617                        5--10-15-20
618Lv0: RH0-5[] 6-6[1] 7-7[6, 2] 8-8[3, 7] 9-9[8, 4] H10-10[5, 9] 11-11[6] 12-12[11, 7] 13-13[12, 8] 14-14[13, 9] H15-15[10, 14] 16-16[11] 17-17[16, 12] 18-18[17, 13] 19-19[14, 18] H20-20[15, 19]
619Lv1: R0-5[] 6-8[1, 2, 3] 9-10[8, 4, 5] 11-13[6, 7, 8] 14-15[13, 9, 10] 16-18[11, 12, 13]
620Lv2: R0-10[] 11-15[6, 7, 8, 9, 10]"#
621    );
622
623    // If a graph looks like this, it's hard to optimize anyway.
624    let ascii_dag = r#"
625            Z---E--J--C--O--T
626                 \     \     \
627                  D     L     K
628                   \     \     \
629                    H--P--W--X--R
630                     \     \     \
631                      U     V     A
632                       \     \     \
633                        N--S--Y--B--G"#;
634    assert_eq!(
635        build_segments(ascii_dag, "G", 3).ascii[0],
636        r#"
637            0---1--2--3--4--5
638                 \     \     \
639                  8     7     6
640                   \     \     \
641                    9--10-11-12-13
642                     \     \     \
643                      15    18    14
644                       \     \     \
645                        16-17-19-20-21
646Lv0: RH0-6[] 7-7[3] 8-10[1] 11-12[7, 10] H13-14[6, 12] 15-17[9] 18-18[11] 19-20[17, 18] H21-21[14, 20]
647Lv1: R0-6[] 7-12[3, 1] 13-14[6, 12] 15-20[9, 11]
648Lv2: R0-14[]"#
649    );
650}
651
652#[test]
653fn test_segment_groups() {
654    let dag = r#"
655A---B---C---D---E---F---G--------H---I
656     \               \          /
657      h--i--j--k      l--m--n--o
658                \            \
659                 -------------p---q"#;
660
661    // This test involves many things. Lower-case commits are non-master commits.
662    // - D after B: Test incremental build of a master commit with a master parent.
663    // - i after D: Test non-master with master parent.
664    // - k after i: Test non-master with non-master parent.
665    // - q after G: Test non-master with both master and non-master ancestors.
666    // - I after q: Test overwriting non-master Ids with master Ids (!).
667    let built = build_segments(dag, "B D i k G q I", 3);
668    assert_eq!(
669        built.ascii.join("\n"),
670        r#"
6710---1---C---D---E---F---G--------H---I
672     \               \          /
673      h--i--j--k      l--m--n--o
674                \            \
675                 -------------p---q
676Lv0: RH0-1[]
677
6780---1---2---3---E---F---G--------H---I
679     \               \          /
680      h--i--j--k      l--m--n--o
681                \            \
682                 -------------p---q
683Lv0: RH0-3[]
684
6850---1---2---3---E---F---G--------H---I
686     \               \          /
687      N0-N1-j--k      l--m--n--o
688                \            \
689                 -------------p---q
690Lv0: RH0-3[] N0-N1[1]
691
6920---1---2---3---E---F---G--------H---I
693     \               \          /
694      N0-N1-N2-N3     l--m--n--o
695                \            \
696                 -------------p---q
697Lv0: RH0-3[] N0-N1[1] N2-N3[N1]
698
6990---1---2---3---4---5---6--------H---I
700     \               \          /
701      N0-N1-N2-N3     l--m--n--o
702                \            \
703                 -------------p---q
704Lv0: RH0-6[] N0-N3[1]
705
7060---1---2---3---4---5---6--------H---I
707     \               \          /
708      N0-N1-N2-N3     N4-N5-N6-o
709                \            \
710                 -------------N7--N8
711Lv0: RH0-6[] N0-N3[1] N4-N6[5] N7-N8[N3, N6]
712
7130---1---2---3---4---5---6--------11--12
714     \               \          /
715      N0-N1-N2-N3     7--8--9--10
716                \            \
717                 -------------N4--N5
718Lv0: RH0-6[] 7-10[5] H11-12[6, 10] N0-N3[1] N4-N5[N3, 9]"#
719    );
720
721    // Notice that N4 to N6 were re-written in the last step.
722    // 'm' only has 1 id: 8 (master). The old id (N5) is now taken by 'q'.
723    assert_eq!(
724        built.name_dag.map.find_id_by_name(b"m").unwrap().unwrap(),
725        Id(8)
726    );
727    assert_eq!(
728        built.name_dag.map.find_name_by_id(Id(8)).unwrap().unwrap(),
729        b"m"
730    );
731    let id = Group::NON_MASTER.min_id() + 5;
732    assert_eq!(
733        built.name_dag.map.find_name_by_id(id).unwrap().unwrap(),
734        b"q"
735    );
736
737    // Parent-child indexes work fine.
738    assert_eq!(dbg(built.name_dag.dag.children_id(Id(5)).unwrap(),), "6 7");
739}
740
741#[test]
742fn test_dag_reassign_master() -> crate::Result<()> {
743    let dir = tempdir().unwrap();
744    let mut dag = Dag::open(dir.path())?;
745    dag = from_ascii(dag, "A-B-C");
746
747    // The in-memory DAG can answer parent_names questions.
748    assert_eq!(dbg(r(dag.parent_names("A".into()))?), "[]");
749    assert_eq!(dbg(r(dag.parent_names("C".into()))?), "[B]");
750
751    // First flush, A, B, C are non-master.
752    r(dag.flush(&Default::default())).unwrap();
753
754    assert_eq!(dbg(r(dag.vertex_id("A".into()))?), "N0");
755    assert_eq!(dbg(r(dag.vertex_id("C".into()))?), "N2");
756
757    // Second flush, making B master without adding new vertexes.
758    let heads =
759        VertexListWithOptions::from(vec![Vertex::from("B")]).with_desired_group(Group::MASTER);
760    r(dag.flush(&heads)).unwrap();
761    assert_eq!(dbg(r(dag.vertex_id("A".into()))?), "0");
762    assert_eq!(dbg(r(dag.vertex_id("B".into()))?), "1");
763    assert_eq!(dbg(r(dag.vertex_id("C".into()))?), "N0");
764
765    Ok(())
766}
767
768#[test]
769fn test_dag_reassign_non_master() {
770    let mut t = TestDag::new();
771
772    // A: master; B, Z: non-master.
773    t.drawdag("A--B--Z", &["A"]);
774    // C, D, E: non-master.
775    t.drawdag("B--C--D--E", &[]);
776    // Prompt C to master. Triggers non-master reassignment.
777    t.drawdag("", &["C"]);
778
779    // Z still exists.
780    #[cfg(feature = "render")]
781    assert_eq!(
782        t.render_graph(),
783        r#"
784            Z  N2
785786            │ E  N1
787            │ │
788            │ D  N0
789            │ │
790            │ C  2
791            ├─╯
792            B  1
793794            A  0"#
795    );
796
797    // Z can round-trip in IdMap.
798    let z_id = r(t.dag.vertex_id("Z".into())).unwrap();
799    let z_vertex = r(t.dag.vertex_name(z_id)).unwrap();
800    assert_eq!(dbg(z_vertex), "Z");
801}
802
803#[test]
804fn test_segment_ancestors_example1() {
805    // DAG from segmented-changelog.pdf
806    let ascii_dag = r#"
807            2-3-\     /--8--9--\
808        0-1------4-5-6-7--------10-11"#;
809    let result = build_segments(ascii_dag, "11", 3);
810    let dag = result.name_dag.dag;
811
812    for (id, count) in &[
813        (11, 12),
814        (10, 11),
815        (9, 9),
816        (8, 8),
817        (7, 8),
818        (6, 7),
819        (5, 6),
820        (4, 5),
821        (3, 2),
822        (2, 1),
823        (1, 2),
824        (0, 1),
825    ] {
826        assert_eq!(dag.ancestors((*id).into()).unwrap().count(), *count);
827    }
828
829    for (a, b, ancestor) in vec![
830        (10, 3, 3.into()),
831        (11, 0, 0.into()),
832        (11, 10, 10.into()),
833        (11, 9, 9.into()),
834        (3, 0, None),
835        (7, 1, 1.into()),
836        (9, 2, 2.into()),
837        (9, 7, 6.into()),
838    ] {
839        let ancestor = ancestor.map(Id);
840        let a = Id(a);
841        let b = Id(b);
842        assert_eq!(dag.gca_one((a, b).into()).unwrap(), ancestor);
843        assert_eq!(
844            dag.gca_all((a, b).into()).unwrap().iter_desc().nth(0),
845            ancestor
846        );
847        assert_eq!(dag.gca_all((a, b).into()).unwrap().iter_desc().nth(1), None);
848        assert_eq!(dag.is_ancestor(b, a).unwrap(), ancestor == Some(b));
849        assert_eq!(dag.is_ancestor(a, b).unwrap(), ancestor == Some(a));
850    }
851
852    for (spans, ancestors) in [
853        (vec![3..=8], vec![3]),
854        (vec![1..=1, 4..=9], vec![1]),
855        (vec![1..=4], vec![]),
856    ] {
857        assert_eq!(
858            dag.gca_all(IdSet::from_spans(spans))
859                .unwrap()
860                .iter_desc()
861                .collect::<Vec<Id>>(),
862            ancestors.into_iter().map(Id).collect::<Vec<Id>>(),
863        );
864    }
865}
866
867#[test]
868fn test_segment_multiple_gcas() {
869    let ascii_dag = r#"
870        B---C
871         \ /
872        A---D"#;
873    let result = build_segments(ascii_dag, "C D", 3);
874    assert_eq!(
875        result.ascii[1],
876        r#"
877        1---2
878         \ /
879        0---3
880Lv0: RH0-0[] R1-1[] H2-2[0, 1] 3-3[0, 1]
881Lv1: R0-2[]"#
882    );
883    let dag = result.name_dag.dag;
884    // This is kind of "undefined" whether it's 1 or 0.
885    assert_eq!(dag.gca_one((2, 3).into()).unwrap(), Some(Id(1)));
886    assert_eq!(
887        dag.gca_all((2, 3).into())
888            .unwrap()
889            .iter_desc()
890            .collect::<Vec<_>>(),
891        vec![1, 0]
892    );
893}
894
895#[test]
896fn test_parents() {
897    let result = build_segments(ASCII_DAG1, "L", 3);
898    assert_eq!(
899        result.ascii[0],
900        r#"
901                2-3-\     /--8--9--\
902            0-1------4-5-6-7--------10-11
903Lv0: RH0-1[] R2-3[] H4-7[1, 3] 8-9[6] H10-11[7, 9]
904Lv1: R0-7[]"#
905    );
906
907    let dag = result.name_dag.dag;
908
909    let parents = |spans| -> String { dbg(dag.parents(IdSet::from_spans(spans)).unwrap()) };
910    let parent_ids = |id| -> String { dbg(dag.parent_ids(Id(id)).unwrap()) };
911    let first_ancestor_nth = |id, n| -> String { dbg(dag.first_ancestor_nth(Id(id), n).unwrap()) };
912    let to_first_ancestor_nth = |id| -> String {
913        let c = FirstAncestorConstraint::KnownUniversally {
914            heads: Id(11).into(),
915        };
916        let res = dag.to_first_ancestor_nth(Id(id), c);
917        match res {
918            Ok(s) => dbg(s),
919            Err(e) => e.to_string(),
920        }
921    };
922
923    assert_eq!(parents(vec![]), "");
924
925    assert_eq!(parents(vec![0..=0]), "");
926    assert_eq!(parents(vec![0..=1]), "0");
927    assert_eq!(parents(vec![0..=2]), "0");
928    assert_eq!(parents(vec![0..=3]), "0 2");
929    assert_eq!(parents(vec![0..=4]), "0..=3");
930    assert_eq!(parents(vec![0..=5]), "0..=4");
931    assert_eq!(parents(vec![0..=6]), "0..=5");
932    assert_eq!(parents(vec![0..=7]), "0..=6");
933    assert_eq!(parents(vec![0..=8]), "0..=6");
934    assert_eq!(parents(vec![0..=9]), "0..=6 8");
935    assert_eq!(parents(vec![0..=10]), "0..=9");
936    assert_eq!(parents(vec![0..=11]), "0..=10");
937
938    assert_eq!(parents(vec![0..=0, 2..=2]), "");
939    assert_eq!(parents(vec![0..=0, 3..=3, 5..=5, 9..=10]), "2 4 7 8 9");
940    assert_eq!(parents(vec![1..=1, 4..=4, 6..=6, 8..=11]), "0 1 3 5..=10");
941
942    assert_eq!(parent_ids(0), "[]");
943    assert_eq!(parent_ids(1), "[0]");
944    assert_eq!(parent_ids(4), "[1, 3]");
945    assert_eq!(parent_ids(10), "[7, 9]");
946    assert_eq!(parent_ids(11), "[10]");
947
948    assert_eq!(first_ancestor_nth(0, 0), "0");
949    assert_eq!(first_ancestor_nth(4, 2), "0");
950    assert_eq!(first_ancestor_nth(10, 2), "6");
951    assert_eq!(first_ancestor_nth(10, 3), "5");
952    assert_eq!(first_ancestor_nth(11, 0), "11");
953    assert_eq!(first_ancestor_nth(11, 1), "10");
954    assert_eq!(first_ancestor_nth(11, 2), "7");
955    assert_eq!(first_ancestor_nth(11, 3), "6");
956    assert_eq!(first_ancestor_nth(11, 4), "5");
957    assert_eq!(first_ancestor_nth(11, 6), "1");
958    assert_eq!(first_ancestor_nth(11, 7), "0");
959    assert!(dag.first_ancestor_nth(Id::MIN, 1).is_err());
960    assert!(dag.first_ancestor_nth(Id(11), 8).is_err());
961
962    assert_eq!(to_first_ancestor_nth(0), "Some((1, 1))");
963    assert_eq!(to_first_ancestor_nth(1), "Some((9, 5))");
964    assert_eq!(to_first_ancestor_nth(2), "Some((3, 1))");
965    assert_eq!(
966        to_first_ancestor_nth(3),
967        "ProgrammingError: cannot convert 3 to x~n form (x must be in `H + parents(ancestors(H) & merge())` where H = 11) (trace: in seg R2-3[], 3 has child seg (H4-7[1, 3]), child seg cannot be followed (3 is not p1))"
968    );
969    assert_eq!(to_first_ancestor_nth(4), "Some((9, 4))");
970    assert_eq!(to_first_ancestor_nth(5), "Some((9, 3))");
971    assert_eq!(to_first_ancestor_nth(6), "Some((9, 2))");
972    assert_eq!(to_first_ancestor_nth(7), "Some((11, 2))");
973    assert_eq!(to_first_ancestor_nth(8), "Some((9, 1))");
974    assert_eq!(
975        to_first_ancestor_nth(9),
976        "ProgrammingError: cannot convert 9 to x~n form (x must be in `H + parents(ancestors(H) & merge())` where H = 11) (trace: in seg 8-9[6], 9 has child seg (H10-11[7, 9]), child seg cannot be followed (9 is not p1))"
977    );
978    assert_eq!(to_first_ancestor_nth(10), "Some((11, 1))");
979    assert_eq!(to_first_ancestor_nth(11), "Some((11, 0))");
980}
981
982#[test]
983fn test_children() {
984    let result = build_segments(ASCII_DAG1, "L", 3);
985    let dag = result.name_dag.dag;
986    let children = |spans| -> String { dbg(dag.children(IdSet::from_spans(spans)).unwrap()) };
987
988    // See test_parents above for the ASCII DAG.
989
990    assert_eq!(children(vec![]), "");
991    assert_eq!(children(vec![0..=0]), "1");
992
993    assert_eq!(children(vec![0..=1]), "1 4");
994    assert_eq!(children(vec![0..=2]), "1 3 4");
995    assert_eq!(children(vec![0..=3]), "1 3 4");
996    assert_eq!(children(vec![0..=4]), "1 3 4 5");
997    assert_eq!(children(vec![0..=5]), "1 3..=6");
998    assert_eq!(children(vec![0..=6]), "1 3..=8");
999    assert_eq!(children(vec![0..=7]), "1 3..=8 10");
1000    assert_eq!(children(vec![0..=8]), "1 3..=10");
1001    assert_eq!(children(vec![0..=9]), "1 3..=10");
1002    assert_eq!(children(vec![0..=10]), "1 3..=11");
1003    assert_eq!(children(vec![0..=11]), "1 3..=11");
1004
1005    assert_eq!(children(vec![1..=10]), "3..=11");
1006    assert_eq!(children(vec![2..=10]), "3..=11");
1007    assert_eq!(children(vec![3..=10]), "4..=11");
1008    assert_eq!(children(vec![4..=10]), "5..=11");
1009    assert_eq!(children(vec![5..=10]), "6..=11");
1010    assert_eq!(children(vec![6..=10]), "7..=11");
1011    assert_eq!(children(vec![7..=10]), "9 10 11");
1012    assert_eq!(children(vec![8..=10]), "9 10 11");
1013    assert_eq!(children(vec![9..=10]), "10 11");
1014    assert_eq!(children(vec![10..=10]), "11");
1015
1016    assert_eq!(children(vec![0..=0, 2..=2]), "1 3");
1017    assert_eq!(children(vec![0..=0, 3..=3, 5..=5, 9..=10]), "1 4 6 10 11");
1018    assert_eq!(children(vec![1..=1, 4..=4, 6..=6, 10..=10]), "4 5 7 8 11");
1019}
1020
1021#[test]
1022fn test_heads() {
1023    let ascii = r#"
1024    C G   K L
1025    | |\  |/
1026    B E F I J
1027    | |/  |/
1028    A D   H"#;
1029
1030    let result = build_segments(ascii, "C G K L J", 2);
1031    assert_eq!(
1032        result.ascii[4],
1033        r#"
1034    2 6   9 10
1035    | |\  |/
1036    1 5 4 8 11
1037    | |/  |/
1038    0 3   7
1039Lv0: RH0-2[] R3-4[] 5-5[3] 6-6[5, 4] R7-9[] 10-10[8] 11-11[7]
1040Lv1: R0-2[] R3-4[] 5-6[3, 4] R7-9[] 10-10[8]
1041Lv2: R0-2[] R3-6[] R7-9[]"#
1042    );
1043
1044    let dag = result.name_dag.dag;
1045    let heads = |spans| -> String { dbg(dag.heads(IdSet::from_spans(spans)).unwrap()) };
1046
1047    assert_eq!(heads(vec![]), "");
1048    assert_eq!(heads(vec![0..=11]), "2 6 9 10 11");
1049    assert_eq!(heads(vec![0..=1, 3..=5, 7..=10]), "1 4 5 9 10");
1050    assert_eq!(heads(vec![0..=0, 2..=2]), "0 2");
1051    assert_eq!(heads(vec![1..=2, 4..=6, 7..=7, 11..=11, 9..=9]), "2 6 9 11");
1052}
1053
1054#[test]
1055fn test_roots() {
1056    let ascii = r#"
1057    C G   J
1058    | |\  |\
1059    B E F I K
1060    | |/  |\
1061    A D   H L"#;
1062
1063    let result = build_segments(ascii, "C G J", 2);
1064    assert_eq!(
1065        result.ascii[2],
1066        r#"
1067    2 6   11
1068    | |\  |\
1069    1 5 4 107
1070    | |/  |\
1071    0 3   9 8
1072Lv0: RH0-2[] R3-4[] 5-5[3] 6-6[5, 4] R7-7[] R8-8[] R9-9[] 10-10[9, 8] 11-11[10, 7]
1073Lv1: R0-2[] R3-4[] 5-6[3, 4] R7-7[] R8-8[] R9-10[8]
1074Lv2: R0-2[] R3-6[] R7-7[]"#
1075    );
1076
1077    let dag = result.name_dag.dag;
1078    let roots = |spans| -> String { dbg(dag.roots(IdSet::from_spans(spans)).unwrap()) };
1079
1080    assert_eq!(roots(vec![]), "");
1081    assert_eq!(roots(vec![0..=11]), "0 3 7 8 9");
1082    assert_eq!(roots(vec![1..=2, 4..=6, 8..=10]), "1 4 5 8 9");
1083    assert_eq!(roots(vec![0..=0, 2..=3, 5..=6, 9..=11]), "0 2 3 9");
1084    assert_eq!(roots(vec![1..=1, 3..=3, 6..=8, 11..=11]), "1 3 6 7 8");
1085}
1086
1087#[test]
1088fn test_range() {
1089    let ascii = r#"
1090            J
1091           /|\
1092          G H I
1093          |/|/
1094          E F
1095         /|/|\
1096        A B C D"#;
1097
1098    let result = build_segments(ascii, "J", 2);
1099    assert_eq!(
1100        result.ascii[0],
1101        r#"
1102            9
1103           /|\
1104          3 7 8
1105          |/|/
1106          2 6
1107         /|/|\
1108        0 1 4 5
1109Lv0: RH0-0[] R1-1[] H2-3[0, 1] R4-4[] R5-5[] 6-6[1, 4, 5] 7-7[2, 6] 8-8[6] H9-9[3, 7, 8]
1110Lv1: R0-0[] R1-3[0] R4-4[] R5-6[1, 4] 7-7[2, 6]
1111Lv2: R0-3[] R4-6[1]"#
1112    );
1113
1114    let dag = result.name_dag.dag;
1115    let range = |roots, heads| -> String {
1116        dbg(dag
1117            .range(IdSet::from_spans(roots), IdSet::from_spans(heads))
1118            .unwrap())
1119    };
1120
1121    assert_eq!(range(vec![6], vec![3]), "");
1122    assert_eq!(range(vec![1], vec![3, 8]), "1 2 3 6 8");
1123    assert_eq!(range(vec![4], vec![3, 8]), "4 6 8");
1124    assert_eq!(range(vec![0, 5], vec![7]), "0 2 5 6 7");
1125    assert_eq!(range(vec![0, 5], vec![3, 8]), "0 2 3 5 6 8");
1126    assert_eq!(range(vec![0, 1, 4, 5], vec![3, 7, 8]), "0..=8");
1127
1128    assert_eq!(range(vec![0], vec![0]), "0");
1129    assert_eq!(range(vec![0], vec![1]), "");
1130    assert_eq!(range(vec![0], vec![2]), "0 2");
1131    assert_eq!(range(vec![0], vec![3]), "0 2 3");
1132    assert_eq!(range(vec![0], vec![4]), "");
1133    assert_eq!(range(vec![0], vec![5]), "");
1134    assert_eq!(range(vec![0], vec![6]), "");
1135    assert_eq!(range(vec![0], vec![7]), "0 2 7");
1136    assert_eq!(range(vec![0], vec![8]), "");
1137    assert_eq!(range(vec![0], vec![9]), "0 2 3 7 9");
1138    assert_eq!(range(vec![1], vec![1]), "1");
1139    assert_eq!(range(vec![1], vec![2]), "1 2");
1140    assert_eq!(range(vec![1], vec![3]), "1 2 3");
1141    assert_eq!(range(vec![1], vec![4]), "");
1142    assert_eq!(range(vec![1], vec![5]), "");
1143    assert_eq!(range(vec![1], vec![6]), "1 6");
1144    assert_eq!(range(vec![1], vec![7]), "1 2 6 7");
1145    assert_eq!(range(vec![1], vec![8]), "1 6 8");
1146    assert_eq!(range(vec![1], vec![9]), "1 2 3 6..=9");
1147    assert_eq!(range(vec![2], vec![2]), "2");
1148    assert_eq!(range(vec![2], vec![3]), "2 3");
1149    assert_eq!(range(vec![2], vec![4]), "");
1150    assert_eq!(range(vec![2], vec![5]), "");
1151    assert_eq!(range(vec![2], vec![6]), "");
1152    assert_eq!(range(vec![2], vec![7]), "2 7");
1153    assert_eq!(range(vec![2], vec![8]), "");
1154    assert_eq!(range(vec![2], vec![9]), "2 3 7 9");
1155    assert_eq!(range(vec![3], vec![3]), "3");
1156    assert_eq!(range(vec![3], vec![4]), "");
1157    assert_eq!(range(vec![3], vec![5]), "");
1158    assert_eq!(range(vec![3], vec![6]), "");
1159    assert_eq!(range(vec![3], vec![7]), "");
1160    assert_eq!(range(vec![3], vec![8]), "");
1161    assert_eq!(range(vec![3], vec![9]), "3 9");
1162    assert_eq!(range(vec![4], vec![4]), "4");
1163    assert_eq!(range(vec![4], vec![5]), "");
1164    assert_eq!(range(vec![4], vec![6]), "4 6");
1165    assert_eq!(range(vec![4], vec![7]), "4 6 7");
1166    assert_eq!(range(vec![4], vec![8]), "4 6 8");
1167    assert_eq!(range(vec![4], vec![9]), "4 6..=9");
1168    assert_eq!(range(vec![5], vec![5]), "5");
1169    assert_eq!(range(vec![5], vec![6]), "5 6");
1170    assert_eq!(range(vec![5], vec![7]), "5 6 7");
1171    assert_eq!(range(vec![5], vec![8]), "5 6 8");
1172    assert_eq!(range(vec![5], vec![9]), "5..=9");
1173    assert_eq!(range(vec![6], vec![6]), "6");
1174    assert_eq!(range(vec![6], vec![7]), "6 7");
1175    assert_eq!(range(vec![6], vec![8]), "6 8");
1176    assert_eq!(range(vec![6], vec![9]), "6..=9");
1177    assert_eq!(range(vec![7], vec![7]), "7");
1178    assert_eq!(range(vec![7], vec![8]), "");
1179    assert_eq!(range(vec![7], vec![9]), "7 9");
1180    assert_eq!(range(vec![8], vec![8]), "8");
1181    assert_eq!(range(vec![8], vec![9]), "8 9");
1182    assert_eq!(range(vec![9], vec![9]), "9");
1183
1184    // Test descendants() and ancestors() against range().
1185    for bits in 0..(1 << 10) {
1186        let mut set = IdSet::empty();
1187        for i in (0..=9).rev() {
1188            if bits & (1 << i) != 0 {
1189                set.push_span(i.into());
1190            }
1191        }
1192
1193        let all = dag.all().unwrap();
1194        assert_eq!(
1195            dag.range(set.clone(), all.clone()).unwrap().as_spans(),
1196            dag.descendants(set.clone()).unwrap().as_spans(),
1197        );
1198
1199        assert_eq!(
1200            dag.range(all.clone(), set.clone()).unwrap().as_spans(),
1201            dag.ancestors(set.clone()).unwrap().as_spans(),
1202        );
1203    }
1204}
1205
1206#[test]
1207fn test_render_segment_dag() {
1208    // For reference in below graphs.
1209    assert_eq!(
1210        build_segments(ASCII_DAG2, "W", 3).ascii[0],
1211        r#"
1212                      19/---------------13-14--\           19
1213                     / /                        \           \
1214               /----4-5-\    /-------11-12-------15-\     18-20--\
1215            0-1-2-3------6--7--8--9--10--------------16-17--------21-22
1216                                   \--13
1217Lv0: RH0-3[] 4-5[1] H6-10[3, 5] 11-12[7] 13-14[5, 9] 15-15[12, 14] H16-17[10, 15] R18-18[] 19-19[4] 20-20[18, 19] H21-22[17, 20]
1218Lv1: R0-10[] 11-15[7, 5, 9] 16-17[10, 15] R18-20[4]
1219Lv2: R0-17[]"#
1220    );
1221
1222    let built = build_segments(ASCII_DAG2, "W", 3);
1223    let mut buf = Vec::new();
1224    buf.push(b'\n');
1225    render_segment_dag(&mut buf, &built.name_dag, 0, Group::MASTER).unwrap();
1226
1227    assert_eq!(
1228        String::from_utf8(buf).unwrap(),
1229        r#"
1230o    V(21)-W(22)
1231├─╮
1232│ o    U(20)-U(20)
1233│ ├─╮
1234│ │ o  T(19)-T(19)
1235│ │ │
1236│ o │  S(18)-S(18)
1237│   │
1238o   │  Q(16)-R(17)
1239├─╮ │
1240│ o │    P(15)-P(15)
1241│ ├───╮
1242│ │ │ o  N(13)-O(14)
1243╭───┬─╯
1244│ o │  L(11)-M(12)
1245├─╯ │
1246o   │  G(6)-K(10)
1247├───╮
1248│   o  E(4)-F(5)
1249├───╯
1250o  A(0)-D(3)
1251
1252"#
1253    );
1254
1255    let mut buf = Vec::new();
1256    buf.push(b'\n');
1257    render_segment_dag(&mut buf, &built.name_dag, 1, Group::MASTER).unwrap();
1258
1259    assert_eq!(
1260        String::from_utf8(buf).unwrap(),
1261        r#"
1262o  S(18)-U(20)
12631264│ o  Q(16)-R(17)
1265╭─┤
1266│ o  L(11)-P(15)
1267╭─╯
1268o  A(0)-K(10)
1269
1270"#
1271    );
1272}
1273
1274#[test]
1275fn test_render_segment_dag_non_master() {
1276    let mut t = TestDag::new();
1277
1278    // A: master; B, Z: non-master.
1279    t.drawdag("A--B--Z", &["A"]);
1280
1281    let mut buf = Vec::new();
1282    buf.push(b'\n');
1283    render_segment_dag(&mut buf, &t.dag, 0, Group::NON_MASTER).unwrap();
1284
1285    assert_eq!(
1286        String::from_utf8(buf).unwrap(),
1287        r#"
1288o  B(N0)-Z(N1)
12891290~
1291"#
1292    );
1293}
1294
1295#[cfg_attr(test, tokio::test)]
1296async fn test_subdag() {
1297    let t = TestDag::draw("A..E");
1298    let s = t.dag.subdag(set("B D E")).await.unwrap();
1299    #[cfg(feature = "render")]
1300    assert_eq!(
1301        render(&s),
1302        r#"
1303            E
13041305            D
13061307            B"#
1308    );
1309
1310    // Test ordering: preserve the heads order (D before C).
1311    let t = TestDag::draw("A-X B-X X-C X-D");
1312    let s1 = t.dag.subdag(set("D C B A")).await.unwrap();
1313    let s2 = t.dag.subdag(set("A B C D")).await.unwrap();
1314    #[cfg(feature = "render")]
1315    assert_eq!(
1316        render(&s1),
1317        r#"
1318            D
1319            ├─╮
1320            │ │ C
1321            ╭─┬─╯
1322            │ A
13231324            B"#
1325    );
1326    #[cfg(feature = "render")]
1327    assert_eq!(render(&s1), render(&s2));
1328}
1329
1330// Test utilities
1331
1332fn expand(set: Set) -> String {
1333    let mut names = set
1334        .iter()
1335        .unwrap()
1336        .map(|n| String::from_utf8_lossy(n.unwrap().as_ref()).to_string())
1337        .collect::<Vec<String>>();
1338    names.sort();
1339    names.join(" ")
1340}
1341
1342fn set(names: &str) -> Set {
1343    let names: Vec<Vertex> = names
1344        .split_whitespace()
1345        .map(|n| Vertex::copy_from(n.as_bytes()))
1346        .collect();
1347    Set::from_static_names(names)
1348}
1349
1350impl IdMap {
1351    /// Replace names in an ASCII DAG using the ids assigned.
1352    fn replace(&self, text: &str) -> String {
1353        let mut result = text.to_string();
1354        for &group in Group::ALL.iter() {
1355            const MAX_ID_IN_ASCII_TEST: u64 = 30;
1356            for id in group.min_id().to(group.min_id() + MAX_ID_IN_ASCII_TEST) {
1357                if let Ok(Some(name)) = self.find_name_by_id(id) {
1358                    let name = String::from_utf8(name.to_vec()).unwrap();
1359                    let id_str = format!("{:01$}", id, name.len());
1360                    // Try to replace while maintaining width
1361                    if name.len() + 2 == id_str.len() {
1362                        result = result
1363                            .replace(&format!("{}--", name), &id_str)
1364                            .replace(&format!("{}  ", name), &id_str);
1365                    } else if name.len() + 1 == id_str.len() {
1366                        result = result
1367                            .replace(&format!("{}-", name), &id_str)
1368                            .replace(&format!("{} ", name), &id_str);
1369                    }
1370                    result = result.replace(&name.to_string(), &id_str);
1371                }
1372            }
1373        }
1374        result
1375    }
1376}
1377
1378fn get_parents_func_from_ascii(text: &str) -> impl Fn(Vertex) -> Result<Vec<Vertex>> {
1379    let parents = ::drawdag::parse(text);
1380    move |name: Vertex| -> Result<Vec<Vertex>> {
1381        Ok(parents[&String::from_utf8(name.as_ref().to_vec()).unwrap()]
1382            .iter()
1383            .map(|p| Vertex::copy_from(p.as_bytes()))
1384            .collect())
1385    }
1386}
1387
1388/// Result of `build_segments`.
1389pub(crate) struct BuildSegmentResult {
1390    pub(crate) ascii: Vec<String>,
1391    pub(crate) name_dag: Dag,
1392    pub(crate) dir: tempfile::TempDir,
1393}
1394
1395/// Take an ASCII DAG, assign segments from given heads.
1396/// Return the ASCII DAG and the built Dag.
1397pub(crate) fn build_segments(text: &str, heads: &str, segment_size: usize) -> BuildSegmentResult {
1398    let mut dag = TestDag::new_with_segment_size(segment_size);
1399
1400    let mut ascii = Vec::new();
1401    for head in heads.split(' ') {
1402        // Assign to non-master if the name starts with a lowercase character.
1403        let master = if head.chars().nth(0).unwrap().is_lowercase() {
1404            vec![]
1405        } else {
1406            vec![head]
1407        };
1408        dag.drawdag_with_limited_heads(text, &master[..], Some(&[head]));
1409        let annotated = dag.annotate_ascii(text);
1410        let segments = dag.render_segments();
1411        ascii.push(format!("{}\n{}", annotated, segments));
1412    }
1413
1414    BuildSegmentResult {
1415        ascii,
1416        name_dag: dag.dag,
1417        dir: dag.dir,
1418    }
1419}
1420
1421fn from_ascii<D: DagAddHeads>(dag: D, text: &str) -> D {
1422    from_ascii_with_heads(dag, text, None)
1423}
1424
1425fn from_ascii_with_heads<D: DagAddHeads>(mut dag: D, text: &str, heads: Option<&[&str]>) -> D {
1426    dag.import_ascii_with_heads(text, heads).unwrap();
1427    dag
1428}
1429
1430/// Test a general DAG interface against a few test cases.
1431pub fn test_generic_dag<D: DagAddHeads + DagAlgorithm + IdConvert + Send + Sync + 'static>(
1432    new_dag: &impl Fn() -> D,
1433) {
1434    test_generic_dag1(new_dag()).unwrap();
1435    test_generic_dag2(new_dag()).unwrap();
1436    test_generic_dag_reachable_roots(new_dag()).unwrap();
1437    test_generic_dag_beautify(new_dag).unwrap();
1438}
1439
1440#[cfg(feature = "render")]
1441fn render(dag: &(impl DagAlgorithm + ?Sized)) -> String {
1442    render_dag(dag, |_| None).unwrap()
1443}
1444
1445#[cfg(test)]
1446pub(crate) fn nid(i: u64) -> Id {
1447    Group::NON_MASTER.min_id() + i
1448}
1449
1450#[cfg(test)]
1451pub(crate) fn vid(i: u64) -> Id {
1452    Group::VIRTUAL.min_id() + i
1453}
1454
1455#[cfg(test)]
1456pub(crate) fn dbg_iter<'a, T: std::fmt::Debug>(
1457    iter: Box<dyn Iterator<Item = Result<T>> + 'a>,
1458) -> String {
1459    let v = iter.map(|s| s.unwrap()).collect::<Vec<_>>();
1460    dbg(v)
1461}
1462
1463#[cfg(test)]
1464pub(crate) fn dbg<T: std::fmt::Debug>(t: T) -> String {
1465    format!("{:?}", t)
1466}