about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/networkx/readwrite/tests/test_text.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/readwrite/tests/test_text.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/tests/test_text.py1742
1 files changed, 1742 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/tests/test_text.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/tests/test_text.py
new file mode 100644
index 00000000..b2b74482
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/tests/test_text.py
@@ -0,0 +1,1742 @@
+import random
+from itertools import product
+from textwrap import dedent
+
+import pytest
+
+import networkx as nx
+
+
+def test_generate_network_text_forest_directed():
+    # Create a directed forest with labels
+    graph = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
+    for node in graph.nodes:
+        graph.nodes[node]["label"] = "node_" + chr(ord("a") + node)
+
+    node_target = dedent(
+        """
+        ╙── 0
+            ├─╼ 1
+            │   ├─╼ 3
+            │   └─╼ 4
+            └─╼ 2
+                ├─╼ 5
+                └─╼ 6
+        """
+    ).strip()
+
+    label_target = dedent(
+        """
+        ╙── node_a
+            ├─╼ node_b
+            │   ├─╼ node_d
+            │   └─╼ node_e
+            └─╼ node_c
+                ├─╼ node_f
+                └─╼ node_g
+        """
+    ).strip()
+
+    # Basic node case
+    ret = nx.generate_network_text(graph, with_labels=False)
+    assert "\n".join(ret) == node_target
+
+    # Basic label case
+    ret = nx.generate_network_text(graph, with_labels=True)
+    assert "\n".join(ret) == label_target
+
+
+def test_write_network_text_empty_graph():
+    def _graph_str(g, **kw):
+        printbuf = []
+        nx.write_network_text(g, printbuf.append, end="", **kw)
+        return "\n".join(printbuf)
+
+    assert _graph_str(nx.DiGraph()) == "╙"
+    assert _graph_str(nx.Graph()) == "╙"
+    assert _graph_str(nx.DiGraph(), ascii_only=True) == "+"
+    assert _graph_str(nx.Graph(), ascii_only=True) == "+"
+
+
+def test_write_network_text_within_forest_glyph():
+    g = nx.DiGraph()
+    g.add_nodes_from([1, 2, 3, 4])
+    g.add_edge(2, 4)
+    lines = []
+    write = lines.append
+    nx.write_network_text(g, path=write, end="")
+    nx.write_network_text(g, path=write, ascii_only=True, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        ╟── 1
+        ╟── 2
+        ╎   └─╼ 4
+        ╙── 3
+        +-- 1
+        +-- 2
+        :   L-> 4
+        +-- 3
+        """
+    ).strip()
+    assert text == target
+
+
+def test_generate_network_text_directed_multi_tree():
+    tree1 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
+    tree2 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
+    forest = nx.disjoint_union_all([tree1, tree2])
+    ret = "\n".join(nx.generate_network_text(forest))
+
+    target = dedent(
+        """
+        ╟── 0
+        ╎   ├─╼ 1
+        ╎   │   ├─╼ 3
+        ╎   │   └─╼ 4
+        ╎   └─╼ 2
+        ╎       ├─╼ 5
+        ╎       └─╼ 6
+        ╙── 7
+            ├─╼ 8
+            │   ├─╼ 10
+            │   └─╼ 11
+            └─╼ 9
+                ├─╼ 12
+                └─╼ 13
+        """
+    ).strip()
+    assert ret == target
+
+    tree3 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
+    forest = nx.disjoint_union_all([tree1, tree2, tree3])
+    ret = "\n".join(nx.generate_network_text(forest, sources=[0, 14, 7]))
+
+    target = dedent(
+        """
+        ╟── 0
+        ╎   ├─╼ 1
+        ╎   │   ├─╼ 3
+        ╎   │   └─╼ 4
+        ╎   └─╼ 2
+        ╎       ├─╼ 5
+        ╎       └─╼ 6
+        ╟── 14
+        ╎   ├─╼ 15
+        ╎   │   ├─╼ 17
+        ╎   │   └─╼ 18
+        ╎   └─╼ 16
+        ╎       ├─╼ 19
+        ╎       └─╼ 20
+        ╙── 7
+            ├─╼ 8
+            │   ├─╼ 10
+            │   └─╼ 11
+            └─╼ 9
+                ├─╼ 12
+                └─╼ 13
+        """
+    ).strip()
+    assert ret == target
+
+    ret = "\n".join(
+        nx.generate_network_text(forest, sources=[0, 14, 7], ascii_only=True)
+    )
+
+    target = dedent(
+        """
+        +-- 0
+        :   |-> 1
+        :   |   |-> 3
+        :   |   L-> 4
+        :   L-> 2
+        :       |-> 5
+        :       L-> 6
+        +-- 14
+        :   |-> 15
+        :   |   |-> 17
+        :   |   L-> 18
+        :   L-> 16
+        :       |-> 19
+        :       L-> 20
+        +-- 7
+            |-> 8
+            |   |-> 10
+            |   L-> 11
+            L-> 9
+                |-> 12
+                L-> 13
+        """
+    ).strip()
+    assert ret == target
+
+
+def test_generate_network_text_undirected_multi_tree():
+    tree1 = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
+    tree2 = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
+    tree2 = nx.relabel_nodes(tree2, {n: n + len(tree1) for n in tree2.nodes})
+    forest = nx.union(tree1, tree2)
+    ret = "\n".join(nx.generate_network_text(forest, sources=[0, 7]))
+
+    target = dedent(
+        """
+        ╟── 0
+        ╎   ├── 1
+        ╎   │   ├── 3
+        ╎   │   └── 4
+        ╎   └── 2
+        ╎       ├── 5
+        ╎       └── 6
+        ╙── 7
+            ├── 8
+            │   ├── 10
+            │   └── 11
+            └── 9
+                ├── 12
+                └── 13
+        """
+    ).strip()
+    assert ret == target
+
+    ret = "\n".join(nx.generate_network_text(forest, sources=[0, 7], ascii_only=True))
+
+    target = dedent(
+        """
+        +-- 0
+        :   |-- 1
+        :   |   |-- 3
+        :   |   L-- 4
+        :   L-- 2
+        :       |-- 5
+        :       L-- 6
+        +-- 7
+            |-- 8
+            |   |-- 10
+            |   L-- 11
+            L-- 9
+                |-- 12
+                L-- 13
+        """
+    ).strip()
+    assert ret == target
+
+
+def test_generate_network_text_forest_undirected():
+    # Create a directed forest
+    graph = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
+
+    node_target0 = dedent(
+        """
+        ╙── 0
+            ├── 1
+            │   ├── 3
+            │   └── 4
+            └── 2
+                ├── 5
+                └── 6
+        """
+    ).strip()
+
+    # defined starting point
+    ret = "\n".join(nx.generate_network_text(graph, sources=[0]))
+    assert ret == node_target0
+
+    # defined starting point
+    node_target2 = dedent(
+        """
+        ╙── 2
+            ├── 0
+            │   └── 1
+            │       ├── 3
+            │       └── 4
+            ├── 5
+            └── 6
+        """
+    ).strip()
+    ret = "\n".join(nx.generate_network_text(graph, sources=[2]))
+    assert ret == node_target2
+
+
+def test_generate_network_text_overspecified_sources():
+    """
+    When sources are directly specified, we won't be able to determine when we
+    are in the last component, so there will always be a trailing, leftmost
+    pipe.
+    """
+    graph = nx.disjoint_union_all(
+        [
+            nx.balanced_tree(r=2, h=1, create_using=nx.DiGraph),
+            nx.balanced_tree(r=1, h=2, create_using=nx.DiGraph),
+            nx.balanced_tree(r=2, h=1, create_using=nx.DiGraph),
+        ]
+    )
+
+    # defined starting point
+    target1 = dedent(
+        """
+        ╟── 0
+        ╎   ├─╼ 1
+        ╎   └─╼ 2
+        ╟── 3
+        ╎   └─╼ 4
+        ╎       └─╼ 5
+        ╟── 6
+        ╎   ├─╼ 7
+        ╎   └─╼ 8
+        """
+    ).strip()
+
+    target2 = dedent(
+        """
+        ╟── 0
+        ╎   ├─╼ 1
+        ╎   └─╼ 2
+        ╟── 3
+        ╎   └─╼ 4
+        ╎       └─╼ 5
+        ╙── 6
+            ├─╼ 7
+            └─╼ 8
+        """
+    ).strip()
+
+    got1 = "\n".join(nx.generate_network_text(graph, sources=graph.nodes))
+    got2 = "\n".join(nx.generate_network_text(graph))
+    assert got1 == target1
+    assert got2 == target2
+
+
+def test_write_network_text_iterative_add_directed_edges():
+    """
+    Walk through the cases going from a disconnected to fully connected graph
+    """
+    graph = nx.DiGraph()
+    graph.add_nodes_from([1, 2, 3, 4])
+    lines = []
+    write = lines.append
+    write("--- initial state ---")
+    nx.write_network_text(graph, path=write, end="")
+    for i, j in product(graph.nodes, graph.nodes):
+        write(f"--- add_edge({i}, {j}) ---")
+        graph.add_edge(i, j)
+        nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    # defined starting point
+    target = dedent(
+        """
+        --- initial state ---
+        ╟── 1
+        ╟── 2
+        ╟── 3
+        ╙── 4
+        --- add_edge(1, 1) ---
+        ╟── 1 ╾ 1
+        ╎   └─╼  ...
+        ╟── 2
+        ╟── 3
+        ╙── 4
+        --- add_edge(1, 2) ---
+        ╟── 1 ╾ 1
+        ╎   ├─╼ 2
+        ╎   └─╼  ...
+        ╟── 3
+        ╙── 4
+        --- add_edge(1, 3) ---
+        ╟── 1 ╾ 1
+        ╎   ├─╼ 2
+        ╎   ├─╼ 3
+        ╎   └─╼  ...
+        ╙── 4
+        --- add_edge(1, 4) ---
+        ╙── 1 ╾ 1
+            ├─╼ 2
+            ├─╼ 3
+            ├─╼ 4
+            └─╼  ...
+        --- add_edge(2, 1) ---
+        ╙── 2 ╾ 1
+            └─╼ 1 ╾ 1
+                ├─╼ 3
+                ├─╼ 4
+                └─╼  ...
+        --- add_edge(2, 2) ---
+        ╙── 1 ╾ 1, 2
+            ├─╼ 2 ╾ 2
+            │   └─╼  ...
+            ├─╼ 3
+            ├─╼ 4
+            └─╼  ...
+        --- add_edge(2, 3) ---
+        ╙── 1 ╾ 1, 2
+            ├─╼ 2 ╾ 2
+            │   ├─╼ 3 ╾ 1
+            │   └─╼  ...
+            ├─╼ 4
+            └─╼  ...
+        --- add_edge(2, 4) ---
+        ╙── 1 ╾ 1, 2
+            ├─╼ 2 ╾ 2
+            │   ├─╼ 3 ╾ 1
+            │   ├─╼ 4 ╾ 1
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(3, 1) ---
+        ╙── 2 ╾ 1, 2
+            ├─╼ 1 ╾ 1, 3
+            │   ├─╼ 3 ╾ 2
+            │   │   └─╼  ...
+            │   ├─╼ 4 ╾ 2
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(3, 2) ---
+        ╙── 3 ╾ 1, 2
+            ├─╼ 1 ╾ 1, 2
+            │   ├─╼ 2 ╾ 2, 3
+            │   │   ├─╼ 4 ╾ 1
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(3, 3) ---
+        ╙── 1 ╾ 1, 2, 3
+            ├─╼ 2 ╾ 2, 3
+            │   ├─╼ 3 ╾ 1, 3
+            │   │   └─╼  ...
+            │   ├─╼ 4 ╾ 1
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(3, 4) ---
+        ╙── 1 ╾ 1, 2, 3
+            ├─╼ 2 ╾ 2, 3
+            │   ├─╼ 3 ╾ 1, 3
+            │   │   ├─╼ 4 ╾ 1, 2
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(4, 1) ---
+        ╙── 2 ╾ 1, 2, 3
+            ├─╼ 1 ╾ 1, 3, 4
+            │   ├─╼ 3 ╾ 2, 3
+            │   │   ├─╼ 4 ╾ 1, 2
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(4, 2) ---
+        ╙── 3 ╾ 1, 2, 3
+            ├─╼ 1 ╾ 1, 2, 4
+            │   ├─╼ 2 ╾ 2, 3, 4
+            │   │   ├─╼ 4 ╾ 1, 3
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(4, 3) ---
+        ╙── 4 ╾ 1, 2, 3
+            ├─╼ 1 ╾ 1, 2, 3
+            │   ├─╼ 2 ╾ 2, 3, 4
+            │   │   ├─╼ 3 ╾ 1, 3, 4
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- add_edge(4, 4) ---
+        ╙── 1 ╾ 1, 2, 3, 4
+            ├─╼ 2 ╾ 2, 3, 4
+            │   ├─╼ 3 ╾ 1, 3, 4
+            │   │   ├─╼ 4 ╾ 1, 2, 4
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_iterative_add_undirected_edges():
+    """
+    Walk through the cases going from a disconnected to fully connected graph
+    """
+    graph = nx.Graph()
+    graph.add_nodes_from([1, 2, 3, 4])
+    lines = []
+    write = lines.append
+    write("--- initial state ---")
+    nx.write_network_text(graph, path=write, end="")
+    for i, j in product(graph.nodes, graph.nodes):
+        if i == j:
+            continue
+        write(f"--- add_edge({i}, {j}) ---")
+        graph.add_edge(i, j)
+        nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- initial state ---
+        ╟── 1
+        ╟── 2
+        ╟── 3
+        ╙── 4
+        --- add_edge(1, 2) ---
+        ╟── 3
+        ╟── 4
+        ╙── 1
+            └── 2
+        --- add_edge(1, 3) ---
+        ╟── 4
+        ╙── 2
+            └── 1
+                └── 3
+        --- add_edge(1, 4) ---
+        ╙── 2
+            └── 1
+                ├── 3
+                └── 4
+        --- add_edge(2, 1) ---
+        ╙── 2
+            └── 1
+                ├── 3
+                └── 4
+        --- add_edge(2, 3) ---
+        ╙── 4
+            └── 1
+                ├── 2
+                │   └── 3 ─ 1
+                └──  ...
+        --- add_edge(2, 4) ---
+        ╙── 3
+            ├── 1
+            │   ├── 2 ─ 3
+            │   │   └── 4 ─ 1
+            │   └──  ...
+            └──  ...
+        --- add_edge(3, 1) ---
+        ╙── 3
+            ├── 1
+            │   ├── 2 ─ 3
+            │   │   └── 4 ─ 1
+            │   └──  ...
+            └──  ...
+        --- add_edge(3, 2) ---
+        ╙── 3
+            ├── 1
+            │   ├── 2 ─ 3
+            │   │   └── 4 ─ 1
+            │   └──  ...
+            └──  ...
+        --- add_edge(3, 4) ---
+        ╙── 1
+            ├── 2
+            │   ├── 3 ─ 1
+            │   │   └── 4 ─ 1, 2
+            │   └──  ...
+            └──  ...
+        --- add_edge(4, 1) ---
+        ╙── 1
+            ├── 2
+            │   ├── 3 ─ 1
+            │   │   └── 4 ─ 1, 2
+            │   └──  ...
+            └──  ...
+        --- add_edge(4, 2) ---
+        ╙── 1
+            ├── 2
+            │   ├── 3 ─ 1
+            │   │   └── 4 ─ 1, 2
+            │   └──  ...
+            └──  ...
+        --- add_edge(4, 3) ---
+        ╙── 1
+            ├── 2
+            │   ├── 3 ─ 1
+            │   │   └── 4 ─ 1, 2
+            │   └──  ...
+            └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_iterative_add_random_directed_edges():
+    """
+    Walk through the cases going from a disconnected to fully connected graph
+    """
+
+    rng = random.Random(724466096)
+    graph = nx.DiGraph()
+    graph.add_nodes_from([1, 2, 3, 4, 5])
+    possible_edges = list(product(graph.nodes, graph.nodes))
+    rng.shuffle(possible_edges)
+    graph.add_edges_from(possible_edges[0:8])
+    lines = []
+    write = lines.append
+    write("--- initial state ---")
+    nx.write_network_text(graph, path=write, end="")
+    for i, j in possible_edges[8:12]:
+        write(f"--- add_edge({i}, {j}) ---")
+        graph.add_edge(i, j)
+        nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- initial state ---
+        ╙── 3 ╾ 5
+            └─╼ 2 ╾ 2
+                ├─╼ 4 ╾ 4
+                │   ├─╼ 5
+                │   │   ├─╼ 1 ╾ 1
+                │   │   │   └─╼  ...
+                │   │   └─╼  ...
+                │   └─╼  ...
+                └─╼  ...
+        --- add_edge(4, 1) ---
+        ╙── 3 ╾ 5
+            └─╼ 2 ╾ 2
+                ├─╼ 4 ╾ 4
+                │   ├─╼ 5
+                │   │   ├─╼ 1 ╾ 1, 4
+                │   │   │   └─╼  ...
+                │   │   └─╼  ...
+                │   └─╼  ...
+                └─╼  ...
+        --- add_edge(2, 1) ---
+        ╙── 3 ╾ 5
+            └─╼ 2 ╾ 2
+                ├─╼ 4 ╾ 4
+                │   ├─╼ 5
+                │   │   ├─╼ 1 ╾ 1, 4, 2
+                │   │   │   └─╼  ...
+                │   │   └─╼  ...
+                │   └─╼  ...
+                └─╼  ...
+        --- add_edge(5, 2) ---
+        ╙── 3 ╾ 5
+            └─╼ 2 ╾ 2, 5
+                ├─╼ 4 ╾ 4
+                │   ├─╼ 5
+                │   │   ├─╼ 1 ╾ 1, 4, 2
+                │   │   │   └─╼  ...
+                │   │   └─╼  ...
+                │   └─╼  ...
+                └─╼  ...
+        --- add_edge(1, 5) ---
+        ╙── 3 ╾ 5
+            └─╼ 2 ╾ 2, 5
+                ├─╼ 4 ╾ 4
+                │   ├─╼ 5 ╾ 1
+                │   │   ├─╼ 1 ╾ 1, 4, 2
+                │   │   │   └─╼  ...
+                │   │   └─╼  ...
+                │   └─╼  ...
+                └─╼  ...
+
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_nearly_forest():
+    g = nx.DiGraph()
+    g.add_edge(1, 2)
+    g.add_edge(1, 5)
+    g.add_edge(2, 3)
+    g.add_edge(3, 4)
+    g.add_edge(5, 6)
+    g.add_edge(6, 7)
+    g.add_edge(6, 8)
+    orig = g.copy()
+    g.add_edge(1, 8)  # forward edge
+    g.add_edge(4, 2)  # back edge
+    g.add_edge(6, 3)  # cross edge
+    lines = []
+    write = lines.append
+    write("--- directed case ---")
+    nx.write_network_text(orig, path=write, end="")
+    write("--- add (1, 8), (4, 2), (6, 3) ---")
+    nx.write_network_text(g, path=write, end="")
+    write("--- undirected case ---")
+    nx.write_network_text(orig.to_undirected(), path=write, sources=[1], end="")
+    write("--- add (1, 8), (4, 2), (6, 3) ---")
+    nx.write_network_text(g.to_undirected(), path=write, sources=[1], end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- directed case ---
+        ╙── 1
+            ├─╼ 2
+            │   └─╼ 3
+            │       └─╼ 4
+            └─╼ 5
+                └─╼ 6
+                    ├─╼ 7
+                    └─╼ 8
+        --- add (1, 8), (4, 2), (6, 3) ---
+        ╙── 1
+            ├─╼ 2 ╾ 4
+            │   └─╼ 3 ╾ 6
+            │       └─╼ 4
+            │           └─╼  ...
+            ├─╼ 5
+            │   └─╼ 6
+            │       ├─╼ 7
+            │       ├─╼ 8 ╾ 1
+            │       └─╼  ...
+            └─╼  ...
+        --- undirected case ---
+        ╙── 1
+            ├── 2
+            │   └── 3
+            │       └── 4
+            └── 5
+                └── 6
+                    ├── 7
+                    └── 8
+        --- add (1, 8), (4, 2), (6, 3) ---
+        ╙── 1
+            ├── 2
+            │   ├── 3
+            │   │   ├── 4 ─ 2
+            │   │   └── 6
+            │   │       ├── 5 ─ 1
+            │   │       ├── 7
+            │   │       └── 8 ─ 1
+            │   └──  ...
+            └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_complete_graph_ascii_only():
+    graph = nx.generators.complete_graph(5, create_using=nx.DiGraph)
+    lines = []
+    write = lines.append
+    write("--- directed case ---")
+    nx.write_network_text(graph, path=write, ascii_only=True, end="")
+    write("--- undirected case ---")
+    nx.write_network_text(graph.to_undirected(), path=write, ascii_only=True, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- directed case ---
+        +-- 0 <- 1, 2, 3, 4
+            |-> 1 <- 2, 3, 4
+            |   |-> 2 <- 0, 3, 4
+            |   |   |-> 3 <- 0, 1, 4
+            |   |   |   |-> 4 <- 0, 1, 2
+            |   |   |   |   L->  ...
+            |   |   |   L->  ...
+            |   |   L->  ...
+            |   L->  ...
+            L->  ...
+        --- undirected case ---
+        +-- 0
+            |-- 1
+            |   |-- 2 - 0
+            |   |   |-- 3 - 0, 1
+            |   |   |   L-- 4 - 0, 1, 2
+            |   |   L--  ...
+            |   L--  ...
+            L--  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_with_labels():
+    graph = nx.generators.complete_graph(5, create_using=nx.DiGraph)
+    for n in graph.nodes:
+        graph.nodes[n]["label"] = f"Node(n={n})"
+    lines = []
+    write = lines.append
+    nx.write_network_text(graph, path=write, with_labels=True, ascii_only=False, end="")
+    text = "\n".join(lines)
+    # Non trees with labels can get somewhat out of hand with network text
+    # because we need to immediately show every non-tree edge to the right
+    target = dedent(
+        """
+        ╙── Node(n=0) ╾ Node(n=1), Node(n=2), Node(n=3), Node(n=4)
+            ├─╼ Node(n=1) ╾ Node(n=2), Node(n=3), Node(n=4)
+            │   ├─╼ Node(n=2) ╾ Node(n=0), Node(n=3), Node(n=4)
+            │   │   ├─╼ Node(n=3) ╾ Node(n=0), Node(n=1), Node(n=4)
+            │   │   │   ├─╼ Node(n=4) ╾ Node(n=0), Node(n=1), Node(n=2)
+            │   │   │   │   └─╼  ...
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_complete_graphs():
+    lines = []
+    write = lines.append
+    for k in [0, 1, 2, 3, 4, 5]:
+        g = nx.generators.complete_graph(k)
+        write(f"--- undirected k={k} ---")
+        nx.write_network_text(g, path=write, end="")
+
+    for k in [0, 1, 2, 3, 4, 5]:
+        g = nx.generators.complete_graph(k, nx.DiGraph)
+        write(f"--- directed k={k} ---")
+        nx.write_network_text(g, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- undirected k=0 ---
+        ╙
+        --- undirected k=1 ---
+        ╙── 0
+        --- undirected k=2 ---
+        ╙── 0
+            └── 1
+        --- undirected k=3 ---
+        ╙── 0
+            ├── 1
+            │   └── 2 ─ 0
+            └──  ...
+        --- undirected k=4 ---
+        ╙── 0
+            ├── 1
+            │   ├── 2 ─ 0
+            │   │   └── 3 ─ 0, 1
+            │   └──  ...
+            └──  ...
+        --- undirected k=5 ---
+        ╙── 0
+            ├── 1
+            │   ├── 2 ─ 0
+            │   │   ├── 3 ─ 0, 1
+            │   │   │   └── 4 ─ 0, 1, 2
+            │   │   └──  ...
+            │   └──  ...
+            └──  ...
+        --- directed k=0 ---
+        ╙
+        --- directed k=1 ---
+        ╙── 0
+        --- directed k=2 ---
+        ╙── 0 ╾ 1
+            └─╼ 1
+                └─╼  ...
+        --- directed k=3 ---
+        ╙── 0 ╾ 1, 2
+            ├─╼ 1 ╾ 2
+            │   ├─╼ 2 ╾ 0
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- directed k=4 ---
+        ╙── 0 ╾ 1, 2, 3
+            ├─╼ 1 ╾ 2, 3
+            │   ├─╼ 2 ╾ 0, 3
+            │   │   ├─╼ 3 ╾ 0, 1
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- directed k=5 ---
+        ╙── 0 ╾ 1, 2, 3, 4
+            ├─╼ 1 ╾ 2, 3, 4
+            │   ├─╼ 2 ╾ 0, 3, 4
+            │   │   ├─╼ 3 ╾ 0, 1, 4
+            │   │   │   ├─╼ 4 ╾ 0, 1, 2
+            │   │   │   │   └─╼  ...
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_multiple_sources():
+    g = nx.DiGraph()
+    g.add_edge(1, 2)
+    g.add_edge(1, 3)
+    g.add_edge(2, 4)
+    g.add_edge(3, 5)
+    g.add_edge(3, 6)
+    g.add_edge(5, 4)
+    g.add_edge(4, 1)
+    g.add_edge(1, 5)
+    lines = []
+    write = lines.append
+    # Use each node as the starting point to demonstrate how the representation
+    # changes.
+    nodes = sorted(g.nodes())
+    for n in nodes:
+        write(f"--- source node: {n} ---")
+        nx.write_network_text(g, path=write, sources=[n], end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- source node: 1 ---
+        ╙── 1 ╾ 4
+            ├─╼ 2
+            │   └─╼ 4 ╾ 5
+            │       └─╼  ...
+            ├─╼ 3
+            │   ├─╼ 5 ╾ 1
+            │   │   └─╼  ...
+            │   └─╼ 6
+            └─╼  ...
+        --- source node: 2 ---
+        ╙── 2 ╾ 1
+            └─╼ 4 ╾ 5
+                └─╼ 1
+                    ├─╼ 3
+                    │   ├─╼ 5 ╾ 1
+                    │   │   └─╼  ...
+                    │   └─╼ 6
+                    └─╼  ...
+        --- source node: 3 ---
+        ╙── 3 ╾ 1
+            ├─╼ 5 ╾ 1
+            │   └─╼ 4 ╾ 2
+            │       └─╼ 1
+            │           ├─╼ 2
+            │           │   └─╼  ...
+            │           └─╼  ...
+            └─╼ 6
+        --- source node: 4 ---
+        ╙── 4 ╾ 2, 5
+            └─╼ 1
+                ├─╼ 2
+                │   └─╼  ...
+                ├─╼ 3
+                │   ├─╼ 5 ╾ 1
+                │   │   └─╼  ...
+                │   └─╼ 6
+                └─╼  ...
+        --- source node: 5 ---
+        ╙── 5 ╾ 3, 1
+            └─╼ 4 ╾ 2
+                └─╼ 1
+                    ├─╼ 2
+                    │   └─╼  ...
+                    ├─╼ 3
+                    │   ├─╼ 6
+                    │   └─╼  ...
+                    └─╼  ...
+        --- source node: 6 ---
+        ╙── 6 ╾ 3
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_star_graph():
+    graph = nx.star_graph(5, create_using=nx.Graph)
+    lines = []
+    write = lines.append
+    nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        ╙── 1
+            └── 0
+                ├── 2
+                ├── 3
+                ├── 4
+                └── 5
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_path_graph():
+    graph = nx.path_graph(3, create_using=nx.Graph)
+    lines = []
+    write = lines.append
+    nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        ╙── 0
+            └── 1
+                └── 2
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_lollipop_graph():
+    graph = nx.lollipop_graph(4, 2, create_using=nx.Graph)
+    lines = []
+    write = lines.append
+    nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        ╙── 5
+            └── 4
+                └── 3
+                    ├── 0
+                    │   ├── 1 ─ 3
+                    │   │   └── 2 ─ 0, 3
+                    │   └──  ...
+                    └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_wheel_graph():
+    graph = nx.wheel_graph(7, create_using=nx.Graph)
+    lines = []
+    write = lines.append
+    nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        ╙── 1
+            ├── 0
+            │   ├── 2 ─ 1
+            │   │   └── 3 ─ 0
+            │   │       └── 4 ─ 0
+            │   │           └── 5 ─ 0
+            │   │               └── 6 ─ 0, 1
+            │   └──  ...
+            └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_circular_ladder_graph():
+    graph = nx.circular_ladder_graph(4, create_using=nx.Graph)
+    lines = []
+    write = lines.append
+    nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        ╙── 0
+            ├── 1
+            │   ├── 2
+            │   │   ├── 3 ─ 0
+            │   │   │   └── 7
+            │   │   │       ├── 6 ─ 2
+            │   │   │       │   └── 5 ─ 1
+            │   │   │       │       └── 4 ─ 0, 7
+            │   │   │       └──  ...
+            │   │   └──  ...
+            │   └──  ...
+            └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_dorogovtsev_goltsev_mendes_graph():
+    graph = nx.dorogovtsev_goltsev_mendes_graph(4, create_using=nx.Graph)
+    lines = []
+    write = lines.append
+    nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        ╙── 15
+            ├── 0
+            │   ├── 1 ─ 15
+            │   │   ├── 2 ─ 0
+            │   │   │   ├── 4 ─ 0
+            │   │   │   │   ├── 9 ─ 0
+            │   │   │   │   │   ├── 22 ─ 0
+            │   │   │   │   │   └── 38 ─ 4
+            │   │   │   │   ├── 13 ─ 2
+            │   │   │   │   │   ├── 34 ─ 2
+            │   │   │   │   │   └── 39 ─ 4
+            │   │   │   │   ├── 18 ─ 0
+            │   │   │   │   ├── 30 ─ 2
+            │   │   │   │   └──  ...
+            │   │   │   ├── 5 ─ 1
+            │   │   │   │   ├── 12 ─ 1
+            │   │   │   │   │   ├── 29 ─ 1
+            │   │   │   │   │   └── 40 ─ 5
+            │   │   │   │   ├── 14 ─ 2
+            │   │   │   │   │   ├── 35 ─ 2
+            │   │   │   │   │   └── 41 ─ 5
+            │   │   │   │   ├── 25 ─ 1
+            │   │   │   │   ├── 31 ─ 2
+            │   │   │   │   └──  ...
+            │   │   │   ├── 7 ─ 0
+            │   │   │   │   ├── 20 ─ 0
+            │   │   │   │   └── 32 ─ 2
+            │   │   │   ├── 10 ─ 1
+            │   │   │   │   ├── 27 ─ 1
+            │   │   │   │   └── 33 ─ 2
+            │   │   │   ├── 16 ─ 0
+            │   │   │   ├── 23 ─ 1
+            │   │   │   └──  ...
+            │   │   ├── 3 ─ 0
+            │   │   │   ├── 8 ─ 0
+            │   │   │   │   ├── 21 ─ 0
+            │   │   │   │   └── 36 ─ 3
+            │   │   │   ├── 11 ─ 1
+            │   │   │   │   ├── 28 ─ 1
+            │   │   │   │   └── 37 ─ 3
+            │   │   │   ├── 17 ─ 0
+            │   │   │   ├── 24 ─ 1
+            │   │   │   └──  ...
+            │   │   ├── 6 ─ 0
+            │   │   │   ├── 19 ─ 0
+            │   │   │   └── 26 ─ 1
+            │   │   └──  ...
+            │   └──  ...
+            └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_tree_max_depth():
+    orig = nx.balanced_tree(r=1, h=3, create_using=nx.DiGraph)
+    lines = []
+    write = lines.append
+    write("--- directed case, max_depth=0 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=0)
+    write("--- directed case, max_depth=1 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=1)
+    write("--- directed case, max_depth=2 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=2)
+    write("--- directed case, max_depth=3 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=3)
+    write("--- directed case, max_depth=4 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=4)
+    write("--- undirected case, max_depth=0 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
+    write("--- undirected case, max_depth=1 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
+    write("--- undirected case, max_depth=2 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
+    write("--- undirected case, max_depth=3 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
+    write("--- undirected case, max_depth=4 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=4)
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- directed case, max_depth=0 ---
+        ╙ ...
+        --- directed case, max_depth=1 ---
+        ╙── 0
+            └─╼  ...
+        --- directed case, max_depth=2 ---
+        ╙── 0
+            └─╼ 1
+                └─╼  ...
+        --- directed case, max_depth=3 ---
+        ╙── 0
+            └─╼ 1
+                └─╼ 2
+                    └─╼  ...
+        --- directed case, max_depth=4 ---
+        ╙── 0
+            └─╼ 1
+                └─╼ 2
+                    └─╼ 3
+        --- undirected case, max_depth=0 ---
+        ╙ ...
+        --- undirected case, max_depth=1 ---
+        ╙── 0 ─ 1
+            └──  ...
+        --- undirected case, max_depth=2 ---
+        ╙── 0
+            └── 1 ─ 2
+                └──  ...
+        --- undirected case, max_depth=3 ---
+        ╙── 0
+            └── 1
+                └── 2 ─ 3
+                    └──  ...
+        --- undirected case, max_depth=4 ---
+        ╙── 0
+            └── 1
+                └── 2
+                    └── 3
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_graph_max_depth():
+    orig = nx.erdos_renyi_graph(10, 0.15, directed=True, seed=40392)
+    lines = []
+    write = lines.append
+    write("--- directed case, max_depth=None ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=None)
+    write("--- directed case, max_depth=0 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=0)
+    write("--- directed case, max_depth=1 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=1)
+    write("--- directed case, max_depth=2 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=2)
+    write("--- directed case, max_depth=3 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=3)
+    write("--- undirected case, max_depth=None ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=None)
+    write("--- undirected case, max_depth=0 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
+    write("--- undirected case, max_depth=1 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
+    write("--- undirected case, max_depth=2 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
+    write("--- undirected case, max_depth=3 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- directed case, max_depth=None ---
+        ╟── 4
+        ╎   ├─╼ 0 ╾ 3
+        ╎   ├─╼ 5 ╾ 7
+        ╎   │   └─╼ 3
+        ╎   │       ├─╼ 1 ╾ 9
+        ╎   │       │   └─╼ 9 ╾ 6
+        ╎   │       │       ├─╼ 6
+        ╎   │       │       │   └─╼  ...
+        ╎   │       │       ├─╼ 7 ╾ 4
+        ╎   │       │       │   ├─╼ 2
+        ╎   │       │       │   └─╼  ...
+        ╎   │       │       └─╼  ...
+        ╎   │       └─╼  ...
+        ╎   └─╼  ...
+        ╙── 8
+        --- directed case, max_depth=0 ---
+        ╙ ...
+        --- directed case, max_depth=1 ---
+        ╟── 4
+        ╎   └─╼  ...
+        ╙── 8
+        --- directed case, max_depth=2 ---
+        ╟── 4
+        ╎   ├─╼ 0 ╾ 3
+        ╎   ├─╼ 5 ╾ 7
+        ╎   │   └─╼  ...
+        ╎   └─╼ 7 ╾ 9
+        ╎       └─╼  ...
+        ╙── 8
+        --- directed case, max_depth=3 ---
+        ╟── 4
+        ╎   ├─╼ 0 ╾ 3
+        ╎   ├─╼ 5 ╾ 7
+        ╎   │   └─╼ 3
+        ╎   │       └─╼  ...
+        ╎   └─╼ 7 ╾ 9
+        ╎       ├─╼ 2
+        ╎       └─╼  ...
+        ╙── 8
+        --- undirected case, max_depth=None ---
+        ╟── 8
+        ╙── 2
+            └── 7
+                ├── 4
+                │   ├── 0
+                │   │   └── 3
+                │   │       ├── 1
+                │   │       │   └── 9 ─ 7
+                │   │       │       └── 6
+                │   │       └── 5 ─ 4, 7
+                │   └──  ...
+                └──  ...
+        --- undirected case, max_depth=0 ---
+        ╙ ...
+        --- undirected case, max_depth=1 ---
+        ╟── 8
+        ╙── 2 ─ 7
+            └──  ...
+        --- undirected case, max_depth=2 ---
+        ╟── 8
+        ╙── 2
+            └── 7 ─ 4, 5, 9
+                └──  ...
+        --- undirected case, max_depth=3 ---
+        ╟── 8
+        ╙── 2
+            └── 7
+                ├── 4 ─ 0, 5
+                │   └──  ...
+                ├── 5 ─ 4, 3
+                │   └──  ...
+                └── 9 ─ 1, 6
+                    └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_clique_max_depth():
+    orig = nx.complete_graph(5, nx.DiGraph)
+    lines = []
+    write = lines.append
+    write("--- directed case, max_depth=None ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=None)
+    write("--- directed case, max_depth=0 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=0)
+    write("--- directed case, max_depth=1 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=1)
+    write("--- directed case, max_depth=2 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=2)
+    write("--- directed case, max_depth=3 ---")
+    nx.write_network_text(orig, path=write, end="", max_depth=3)
+    write("--- undirected case, max_depth=None ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=None)
+    write("--- undirected case, max_depth=0 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
+    write("--- undirected case, max_depth=1 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
+    write("--- undirected case, max_depth=2 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
+    write("--- undirected case, max_depth=3 ---")
+    nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- directed case, max_depth=None ---
+        ╙── 0 ╾ 1, 2, 3, 4
+            ├─╼ 1 ╾ 2, 3, 4
+            │   ├─╼ 2 ╾ 0, 3, 4
+            │   │   ├─╼ 3 ╾ 0, 1, 4
+            │   │   │   ├─╼ 4 ╾ 0, 1, 2
+            │   │   │   │   └─╼  ...
+            │   │   │   └─╼  ...
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- directed case, max_depth=0 ---
+        ╙ ...
+        --- directed case, max_depth=1 ---
+        ╙── 0 ╾ 1, 2, 3, 4
+            └─╼  ...
+        --- directed case, max_depth=2 ---
+        ╙── 0 ╾ 1, 2, 3, 4
+            ├─╼ 1 ╾ 2, 3, 4
+            │   └─╼  ...
+            ├─╼ 2 ╾ 1, 3, 4
+            │   └─╼  ...
+            ├─╼ 3 ╾ 1, 2, 4
+            │   └─╼  ...
+            └─╼ 4 ╾ 1, 2, 3
+                └─╼  ...
+        --- directed case, max_depth=3 ---
+        ╙── 0 ╾ 1, 2, 3, 4
+            ├─╼ 1 ╾ 2, 3, 4
+            │   ├─╼ 2 ╾ 0, 3, 4
+            │   │   └─╼  ...
+            │   ├─╼ 3 ╾ 0, 2, 4
+            │   │   └─╼  ...
+            │   ├─╼ 4 ╾ 0, 2, 3
+            │   │   └─╼  ...
+            │   └─╼  ...
+            └─╼  ...
+        --- undirected case, max_depth=None ---
+        ╙── 0
+            ├── 1
+            │   ├── 2 ─ 0
+            │   │   ├── 3 ─ 0, 1
+            │   │   │   └── 4 ─ 0, 1, 2
+            │   │   └──  ...
+            │   └──  ...
+            └──  ...
+        --- undirected case, max_depth=0 ---
+        ╙ ...
+        --- undirected case, max_depth=1 ---
+        ╙── 0 ─ 1, 2, 3, 4
+            └──  ...
+        --- undirected case, max_depth=2 ---
+        ╙── 0
+            ├── 1 ─ 2, 3, 4
+            │   └──  ...
+            ├── 2 ─ 1, 3, 4
+            │   └──  ...
+            ├── 3 ─ 1, 2, 4
+            │   └──  ...
+            └── 4 ─ 1, 2, 3
+        --- undirected case, max_depth=3 ---
+        ╙── 0
+            ├── 1
+            │   ├── 2 ─ 0, 3, 4
+            │   │   └──  ...
+            │   ├── 3 ─ 0, 2, 4
+            │   │   └──  ...
+            │   └── 4 ─ 0, 2, 3
+            └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_custom_label():
+    # Create a directed forest with labels
+    graph = nx.erdos_renyi_graph(5, 0.4, directed=True, seed=359222358)
+    for node in graph.nodes:
+        graph.nodes[node]["label"] = f"Node({node})"
+        graph.nodes[node]["chr"] = chr(node + ord("a") - 1)
+        if node % 2 == 0:
+            graph.nodes[node]["part"] = chr(node + ord("a"))
+
+    lines = []
+    write = lines.append
+    write("--- when with_labels=True, uses the 'label' attr ---")
+    nx.write_network_text(graph, path=write, with_labels=True, end="", max_depth=None)
+    write("--- when with_labels=False, uses str(node) value ---")
+    nx.write_network_text(graph, path=write, with_labels=False, end="", max_depth=None)
+    write("--- when with_labels is a string, use that attr ---")
+    nx.write_network_text(graph, path=write, with_labels="chr", end="", max_depth=None)
+    write("--- fallback to str(node) when the attr does not exist ---")
+    nx.write_network_text(graph, path=write, with_labels="part", end="", max_depth=None)
+
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- when with_labels=True, uses the 'label' attr ---
+        ╙── Node(1)
+            └─╼ Node(3) ╾ Node(2)
+                ├─╼ Node(0)
+                │   ├─╼ Node(2) ╾ Node(3), Node(4)
+                │   │   └─╼  ...
+                │   └─╼ Node(4)
+                │       └─╼  ...
+                └─╼  ...
+        --- when with_labels=False, uses str(node) value ---
+        ╙── 1
+            └─╼ 3 ╾ 2
+                ├─╼ 0
+                │   ├─╼ 2 ╾ 3, 4
+                │   │   └─╼  ...
+                │   └─╼ 4
+                │       └─╼  ...
+                └─╼  ...
+        --- when with_labels is a string, use that attr ---
+        ╙── a
+            └─╼ c ╾ b
+                ├─╼ `
+                │   ├─╼ b ╾ c, d
+                │   │   └─╼  ...
+                │   └─╼ d
+                │       └─╼  ...
+                └─╼  ...
+        --- fallback to str(node) when the attr does not exist ---
+        ╙── 1
+            └─╼ 3 ╾ c
+                ├─╼ a
+                │   ├─╼ c ╾ 3, e
+                │   │   └─╼  ...
+                │   └─╼ e
+                │       └─╼  ...
+                └─╼  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_write_network_text_vertical_chains():
+    graph1 = nx.lollipop_graph(4, 2, create_using=nx.Graph)
+    graph1.add_edge(0, -1)
+    graph1.add_edge(-1, -2)
+    graph1.add_edge(-2, -3)
+
+    graph2 = graph1.to_directed()
+    graph2.remove_edges_from([(u, v) for u, v in graph2.edges if v > u])
+
+    lines = []
+    write = lines.append
+    write("--- Undirected UTF ---")
+    nx.write_network_text(graph1, path=write, end="", vertical_chains=True)
+    write("--- Undirected ASCI ---")
+    nx.write_network_text(
+        graph1, path=write, end="", vertical_chains=True, ascii_only=True
+    )
+    write("--- Directed UTF ---")
+    nx.write_network_text(graph2, path=write, end="", vertical_chains=True)
+    write("--- Directed ASCI ---")
+    nx.write_network_text(
+        graph2, path=write, end="", vertical_chains=True, ascii_only=True
+    )
+
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- Undirected UTF ---
+        ╙── 5
+            │
+            4
+            │
+            3
+            ├── 0
+            │   ├── 1 ─ 3
+            │   │   │
+            │   │   2 ─ 0, 3
+            │   ├── -1
+            │   │   │
+            │   │   -2
+            │   │   │
+            │   │   -3
+            │   └──  ...
+            └──  ...
+        --- Undirected ASCI ---
+        +-- 5
+            |
+            4
+            |
+            3
+            |-- 0
+            |   |-- 1 - 3
+            |   |   |
+            |   |   2 - 0, 3
+            |   |-- -1
+            |   |   |
+            |   |   -2
+            |   |   |
+            |   |   -3
+            |   L--  ...
+            L--  ...
+        --- Directed UTF ---
+        ╙── 5
+            ╽
+            4
+            ╽
+            3
+            ├─╼ 0 ╾ 1, 2
+            │   ╽
+            │   -1
+            │   ╽
+            │   -2
+            │   ╽
+            │   -3
+            ├─╼ 1 ╾ 2
+            │   └─╼  ...
+            └─╼ 2
+                └─╼  ...
+        --- Directed ASCI ---
+        +-- 5
+            !
+            4
+            !
+            3
+            |-> 0 <- 1, 2
+            |   !
+            |   -1
+            |   !
+            |   -2
+            |   !
+            |   -3
+            |-> 1 <- 2
+            |   L->  ...
+            L-> 2
+                L->  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_collapse_directed():
+    graph = nx.balanced_tree(r=2, h=3, create_using=nx.DiGraph)
+    lines = []
+    write = lines.append
+    write("--- Original ---")
+    nx.write_network_text(graph, path=write, end="")
+    graph.nodes[1]["collapse"] = True
+    write("--- Collapse Node 1 ---")
+    nx.write_network_text(graph, path=write, end="")
+    write("--- Add alternate path (5, 3) to collapsed zone")
+    graph.add_edge(5, 3)
+    nx.write_network_text(graph, path=write, end="")
+    write("--- Collapse Node 0 ---")
+    graph.nodes[0]["collapse"] = True
+    nx.write_network_text(graph, path=write, end="")
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- Original ---
+        ╙── 0
+            ├─╼ 1
+            │   ├─╼ 3
+            │   │   ├─╼ 7
+            │   │   └─╼ 8
+            │   └─╼ 4
+            │       ├─╼ 9
+            │       └─╼ 10
+            └─╼ 2
+                ├─╼ 5
+                │   ├─╼ 11
+                │   └─╼ 12
+                └─╼ 6
+                    ├─╼ 13
+                    └─╼ 14
+        --- Collapse Node 1 ---
+        ╙── 0
+            ├─╼ 1
+            │   └─╼  ...
+            └─╼ 2
+                ├─╼ 5
+                │   ├─╼ 11
+                │   └─╼ 12
+                └─╼ 6
+                    ├─╼ 13
+                    └─╼ 14
+        --- Add alternate path (5, 3) to collapsed zone
+        ╙── 0
+            ├─╼ 1
+            │   └─╼  ...
+            └─╼ 2
+                ├─╼ 5
+                │   ├─╼ 11
+                │   ├─╼ 12
+                │   └─╼ 3 ╾ 1
+                │       ├─╼ 7
+                │       └─╼ 8
+                └─╼ 6
+                    ├─╼ 13
+                    └─╼ 14
+        --- Collapse Node 0 ---
+        ╙── 0
+            └─╼  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def test_collapse_undirected():
+    graph = nx.balanced_tree(r=2, h=3, create_using=nx.Graph)
+    lines = []
+    write = lines.append
+    write("--- Original ---")
+    nx.write_network_text(graph, path=write, end="", sources=[0])
+    graph.nodes[1]["collapse"] = True
+    write("--- Collapse Node 1 ---")
+    nx.write_network_text(graph, path=write, end="", sources=[0])
+    write("--- Add alternate path (5, 3) to collapsed zone")
+    graph.add_edge(5, 3)
+    nx.write_network_text(graph, path=write, end="", sources=[0])
+    write("--- Collapse Node 0 ---")
+    graph.nodes[0]["collapse"] = True
+    nx.write_network_text(graph, path=write, end="", sources=[0])
+    text = "\n".join(lines)
+    target = dedent(
+        """
+        --- Original ---
+        ╙── 0
+            ├── 1
+            │   ├── 3
+            │   │   ├── 7
+            │   │   └── 8
+            │   └── 4
+            │       ├── 9
+            │       └── 10
+            └── 2
+                ├── 5
+                │   ├── 11
+                │   └── 12
+                └── 6
+                    ├── 13
+                    └── 14
+        --- Collapse Node 1 ---
+        ╙── 0
+            ├── 1 ─ 3, 4
+            │   └──  ...
+            └── 2
+                ├── 5
+                │   ├── 11
+                │   └── 12
+                └── 6
+                    ├── 13
+                    └── 14
+        --- Add alternate path (5, 3) to collapsed zone
+        ╙── 0
+            ├── 1 ─ 3, 4
+            │   └──  ...
+            └── 2
+                ├── 5
+                │   ├── 11
+                │   ├── 12
+                │   └── 3 ─ 1
+                │       ├── 7
+                │       └── 8
+                └── 6
+                    ├── 13
+                    └── 14
+        --- Collapse Node 0 ---
+        ╙── 0 ─ 1, 2
+            └──  ...
+        """
+    ).strip()
+    assert target == text
+
+
+def generate_test_graphs():
+    """
+    Generate a gauntlet of different test graphs with different properties
+    """
+    import random
+
+    rng = random.Random(976689776)
+    num_randomized = 3
+
+    for directed in [0, 1]:
+        cls = nx.DiGraph if directed else nx.Graph
+
+        for num_nodes in range(17):
+            # Disconnected graph
+            graph = cls()
+            graph.add_nodes_from(range(num_nodes))
+            yield graph
+
+            # Randomize graphs
+            if num_nodes > 0:
+                for p in [0.1, 0.3, 0.5, 0.7, 0.9]:
+                    for seed in range(num_randomized):
+                        graph = nx.erdos_renyi_graph(
+                            num_nodes, p, directed=directed, seed=rng
+                        )
+                        yield graph
+
+                yield nx.complete_graph(num_nodes, cls)
+
+        yield nx.path_graph(3, create_using=cls)
+        yield nx.balanced_tree(r=1, h=3, create_using=cls)
+        if not directed:
+            yield nx.circular_ladder_graph(4, create_using=cls)
+            yield nx.star_graph(5, create_using=cls)
+            yield nx.lollipop_graph(4, 2, create_using=cls)
+            yield nx.wheel_graph(7, create_using=cls)
+            yield nx.dorogovtsev_goltsev_mendes_graph(4, create_using=cls)
+
+
+@pytest.mark.parametrize(
+    ("vertical_chains", "ascii_only"),
+    tuple(
+        [
+            (vertical_chains, ascii_only)
+            for vertical_chains in [0, 1]
+            for ascii_only in [0, 1]
+        ]
+    ),
+)
+def test_network_text_round_trip(vertical_chains, ascii_only):
+    """
+    Write the graph to network text format, then parse it back in, assert it is
+    the same as the original graph. Passing this test is strong validation of
+    both the format generator and parser.
+    """
+    from networkx.readwrite.text import _parse_network_text
+
+    for graph in generate_test_graphs():
+        graph = nx.relabel_nodes(graph, {n: str(n) for n in graph.nodes})
+        lines = list(
+            nx.generate_network_text(
+                graph, vertical_chains=vertical_chains, ascii_only=ascii_only
+            )
+        )
+        new = _parse_network_text(lines)
+        try:
+            assert new.nodes == graph.nodes
+            assert new.edges == graph.edges
+        except Exception:
+            nx.write_network_text(graph)
+            raise