Repository: networkx/networkx

Issue:
Improve test coverage for generators/tree.py
Increase coverage over [`tree.py`](https://github.com/networkx/networkx/blob/main/networkx/generators/trees.py) so all the code blocks that are highlighted in 
https://app.codecov.io/gh/networkx/networkx/blob/main/networkx/generators/trees.py are properly tested. 


Discussion:

Patch:
diff --git a/examples/drawing/plot_eigenvalues.py b/examples/drawing/plot_eigenvalues.py
index b0df67ae9..67322cfd1 100644
--- a/examples/drawing/plot_eigenvalues.py
+++ b/examples/drawing/plot_eigenvalues.py
@@ -14,7 +14,7 @@ m = 5000  # 5000 edges
 G = nx.gnm_random_graph(n, m, seed=5040)  # Seed for reproducibility
 
 L = nx.normalized_laplacian_matrix(G)
-e = numpy.linalg.eigvals(L.A)
+e = numpy.linalg.eigvals(L.toarray())
 print("Largest eigenvalue:", max(e))
 print("Smallest eigenvalue:", min(e))
 plt.hist(e, bins=100)  # histogram with 100 bins
diff --git a/networkx/algorithms/dag.py b/networkx/algorithms/dag.py
index 826b87ff6..d5e2735b1 100644
--- a/networkx/algorithms/dag.py
+++ b/networkx/algorithms/dag.py
@@ -1006,7 +1006,15 @@ def dag_longest_path(G, weight="weight", default_weight=1, topo_order=None):
     dist = {}  # stores {v : (length, u)}
     for v in topo_order:
         us = [
-            (dist[u][0] + data.get(weight, default_weight), u)
+            (
+                dist[u][0]
+                + (
+                    max(data.values(), key=lambda x: x.get(weight, default_weight))
+                    if G.is_multigraph()
+                    else data
+                ).get(weight, default_weight),
+                u,
+            )
             for u, data in G.pred[v].items()
         ]
 
@@ -1068,8 +1076,13 @@ def dag_longest_path_length(G, weight="weight", default_weight=1):
     """
     path = nx.dag_longest_path(G, weight, default_weight)
     path_length = 0
-    for (u, v) in pairwise(path):
-        path_length += G[u][v].get(weight, default_weight)
+    if G.is_multigraph():
+        for u, v in pairwise(path):
+            i = max(G[u][v], key=lambda x: G[u][v][x].get(weight, default_weight))
+            path_length += G[u][v][i].get(weight, default_weight)
+    else:
+        for (u, v) in pairwise(path):
+            path_length += G[u][v].get(weight, default_weight)
 
     return path_length
 
diff --git a/networkx/algorithms/flow/maxflow.py b/networkx/algorithms/flow/maxflow.py
index 8d2fb8f03..b0098d0a0 100644
--- a/networkx/algorithms/flow/maxflow.py
+++ b/networkx/algorithms/flow/maxflow.py
@@ -12,14 +12,6 @@ from .utils import build_flow_dict
 
 # Define the default flow function for computing maximum flow.
 default_flow_func = preflow_push
-# Functions that don't support cutoff for minimum cut computations.
-flow_funcs = [
-    boykov_kolmogorov,
-    dinitz,
-    edmonds_karp,
-    preflow_push,
-    shortest_augmenting_path,
-]
 
 __all__ = ["maximum_flow", "maximum_flow_value", "minimum_cut", "minimum_cut_value"]
 
@@ -452,7 +444,7 @@ def minimum_cut(flowG, _s, _t, capacity="capacity", flow_func=None, **kwargs):
     if not callable(flow_func):
         raise nx.NetworkXError("flow_func has to be callable.")
 
-    if kwargs.get("cutoff") is not None and flow_func in flow_funcs:
+    if kwargs.get("cutoff") is not None and flow_func is preflow_push:
         raise nx.NetworkXError("cutoff should not be specified.")
 
     R = flow_func(flowG, _s, _t, capacity=capacity, value_only=True, **kwargs)
@@ -603,7 +595,7 @@ def minimum_cut_value(flowG, _s, _t, capacity="capacity", flow_func=None, **kwar
     if not callable(flow_func):
         raise nx.NetworkXError("flow_func has to be callable.")
 
-    if kwargs.get("cutoff") is not None and flow_func in flow_funcs:
+    if kwargs.get("cutoff") is not None and flow_func is preflow_push:
         raise nx.NetworkXError("cutoff should not be specified.")
 
     R = flow_func(flowG, _s, _t, capacity=capacity, value_only=True, **kwargs)
diff --git a/networkx/drawing/nx_pylab.py b/networkx/drawing/nx_pylab.py
index 09e5ed3a9..674c28a45 100644
--- a/networkx/drawing/nx_pylab.py
+++ b/networkx/drawing/nx_pylab.py
@@ -650,6 +650,42 @@ def draw_networkx_edges(
     # undirected graphs (for performance reasons) and use FancyArrowPatches
     # for directed graphs.
     # The `arrows` keyword can be used to override the default behavior
+    use_linecollection = not G.is_directed()
+    if arrows in (True, False):
+        use_linecollection = not arrows
+
+    # Some kwargs only apply to FancyArrowPatches. Warn users when they use
+    # non-default values for these kwargs when LineCollection is being used
+    # instead of silently ignoring the specified option
+    if use_linecollection and any(
+        [
+            arrowstyle is not None,
+            arrowsize != 10,
+            connectionstyle != "arc3",
+            min_source_margin != 0,
+            min_target_margin != 0,
+        ]
+    ):
+        import warnings
+
+        msg = (
+            "\n\nThe {0} keyword argument is not applicable when drawing edges\n"
+            "with LineCollection.\n\n"
+            "To make this warning go away, either specify `arrows=True` to\n"
+            "force FancyArrowPatches or use the default value for {0}.\n"
+            "Note that using FancyArrowPatches may be slow for large graphs.\n"
+        )
+        if arrowstyle is not None:
+            msg = msg.format("arrowstyle")
+        if arrowsize != 10:
+            msg = msg.format("arrowsize")
+        if connectionstyle != "arc3":
+            msg = msg.format("connectionstyle")
+        if min_source_margin != 0:
+            msg = msg.format("min_source_margin")
+        if min_target_margin != 0:
+            msg = msg.format("min_target_margin")
+        warnings.warn(msg, category=UserWarning, stacklevel=2)
 
     if arrowstyle == None:
         if G.is_directed():
@@ -657,10 +693,6 @@ def draw_networkx_edges(
         else:
             arrowstyle = "-"
 
-    use_linecollection = not G.is_directed()
-    if arrows in (True, False):
-        use_linecollection = not arrows
-
     if ax is None:
         ax = plt.gca()
 
diff --git a/networkx/readwrite/gml.py b/networkx/readwrite/gml.py
index da5ccca58..758c0c73b 100644
--- a/networkx/readwrite/gml.py
+++ b/networkx/readwrite/gml.py
@@ -363,6 +363,11 @@ def parse_gml_lines(lines, label, destringizer):
                         value = destringizer(value)
                     except ValueError:
                         pass
+                # Special handling for empty lists and tuples
+                if value == "()":
+                    value = tuple()
+                if value == "[]":
+                    value = list()
                 curr_token = next(tokens)
             elif category == Pattern.DICT_START:
                 curr_token, value = parse_dict(curr_token)
@@ -728,12 +733,9 @@ def generate_gml(G, stringizer=None):
                 for key, value in value.items():
                     yield from stringize(key, value, (), next_indent)
                 yield indent + "]"
-            elif (
-                isinstance(value, (list, tuple))
-                and key != "label"
-                and value
-                and not in_list
-            ):
+            elif isinstance(value, (list, tuple)) and key != "label" and not in_list:
+                if len(value) == 0:
+                    yield indent + key + " " + f'"{value!r}"'
                 if len(value) == 1:
                     yield indent + key + " " + f'"{LIST_START_VALUE}"'
                 for val in value:


Test patch:
diff --git a/networkx/algorithms/approximation/tests/test_dominating_set.py b/networkx/algorithms/approximation/tests/test_dominating_set.py
index 892ce34e2..6b90d85ec 100644
--- a/networkx/algorithms/approximation/tests/test_dominating_set.py
+++ b/networkx/algorithms/approximation/tests/test_dominating_set.py
@@ -1,3 +1,5 @@
+import pytest
+
 import networkx as nx
 from networkx.algorithms.approximation import (
     min_edge_dominating_set,
@@ -37,6 +39,11 @@ class TestMinWeightDominatingSet:
         G = nx.relabel_nodes(G, {0: 9, 9: 0})
         assert min_weighted_dominating_set(G) == {9}
 
+    def test_null_graph(self):
+        """Tests that the unique dominating set for the null graph is an empty set"""
+        G = nx.Graph()
+        assert min_weighted_dominating_set(G) == set()
+
     def test_min_edge_dominating_set(self):
         graph = nx.path_graph(5)
         dom_set = min_edge_dominating_set(graph)
@@ -65,3 +72,7 @@ class TestMinWeightDominatingSet:
                 for dom_edge in dom_set:
                     found |= u == dom_edge[0] or u == dom_edge[1]
                 assert found, "Non adjacent edge found!"
+
+        graph = nx.Graph()  # empty Networkx graph
+        with pytest.raises(ValueError, match="Expected non-empty NetworkX graph!"):
+            min_edge_dominating_set(graph)
diff --git a/networkx/algorithms/centrality/tests/test_load_centrality.py b/networkx/algorithms/centrality/tests/test_load_centrality.py
index 065b4fdaa..d994394f7 100644
--- a/networkx/algorithms/centrality/tests/test_load_centrality.py
+++ b/networkx/algorithms/centrality/tests/test_load_centrality.py
@@ -24,6 +24,7 @@ class TestLoadCentrality:
         cls.P3 = nx.path_graph(3)
         cls.P4 = nx.path_graph(4)
         cls.K5 = nx.complete_graph(5)
+        cls.P2 = nx.path_graph(2)
 
         cls.C4 = nx.cycle_graph(4)
         cls.T = nx.balanced_tree(r=2, h=2)
@@ -41,6 +42,13 @@ class TestLoadCentrality:
             assert result[n] == pytest.approx(b[n], abs=1e-3)
             assert result[n] == pytest.approx(nx.load_centrality(self.D, n), abs=1e-3)
 
+    def test_P2_normalized_load(self):
+        G = self.P2
+        c = nx.load_centrality(G, normalized=True)
+        d = {0: 0.000, 1: 0.000}
+        for n in sorted(G):
+            assert c[n] == pytest.approx(d[n], abs=1e-3)
+
     def test_weighted_load(self):
         b = nx.load_centrality(self.G, weight="weight", normalized=False)
         for n in sorted(self.G):
diff --git a/networkx/algorithms/flow/tests/test_maxflow.py b/networkx/algorithms/flow/tests/test_maxflow.py
index 36bc5ec7c..d4f9052ce 100644
--- a/networkx/algorithms/flow/tests/test_maxflow.py
+++ b/networkx/algorithms/flow/tests/test_maxflow.py
@@ -20,6 +20,7 @@ flow_funcs = {
     preflow_push,
     shortest_augmenting_path,
 }
+
 max_min_funcs = {nx.maximum_flow, nx.minimum_cut}
 flow_value_funcs = {nx.maximum_flow_value, nx.minimum_cut_value}
 interface_funcs = max_min_funcs & flow_value_funcs
@@ -427,25 +428,24 @@ class TestMaxFlowMinCutInterface:
 
     def test_minimum_cut_no_cutoff(self):
         G = self.G
-        for flow_func in flow_funcs:
-            pytest.raises(
-                nx.NetworkXError,
-                nx.minimum_cut,
-                G,
-                "x",
-                "y",
-                flow_func=flow_func,
-                cutoff=1.0,
-            )
-            pytest.raises(
-                nx.NetworkXError,
-                nx.minimum_cut_value,
-                G,
-                "x",
-                "y",
-                flow_func=flow_func,
-                cutoff=1.0,
-            )
+        pytest.raises(
+            nx.NetworkXError,
+            nx.minimum_cut,
+            G,
+            "x",
+            "y",
+            flow_func=preflow_push,
+            cutoff=1.0,
+        )
+        pytest.raises(
+            nx.NetworkXError,
+            nx.minimum_cut_value,
+            G,
+            "x",
+            "y",
+            flow_func=preflow_push,
+            cutoff=1.0,
+        )
 
     def test_kwargs(self):
         G = self.H
@@ -539,11 +539,20 @@ class TestCutoff:
         assert k <= R.graph["flow_value"] <= (2 * k)
         R = edmonds_karp(G, "s", "t", cutoff=k)
         assert k <= R.graph["flow_value"] <= (2 * k)
+        R = dinitz(G, "s", "t", cutoff=k)
+        assert k <= R.graph["flow_value"] <= (2 * k)
+        R = boykov_kolmogorov(G, "s", "t", cutoff=k)
+        assert k <= R.graph["flow_value"] <= (2 * k)
 
     def test_complete_graph_cutoff(self):
         G = nx.complete_graph(5)
         nx.set_edge_attributes(G, {(u, v): 1 for u, v in G.edges()}, "capacity")
-        for flow_func in [shortest_augmenting_path, edmonds_karp]:
+        for flow_func in [
+            shortest_augmenting_path,
+            edmonds_karp,
+            dinitz,
+            boykov_kolmogorov,
+        ]:
             for cutoff in [3, 2, 1]:
                 result = nx.maximum_flow_value(
                     G, 0, 4, flow_func=flow_func, cutoff=cutoff
diff --git a/networkx/algorithms/tests/test_dag.py b/networkx/algorithms/tests/test_dag.py
index 56f16c4f5..7ad6a77fe 100644
--- a/networkx/algorithms/tests/test_dag.py
+++ b/networkx/algorithms/tests/test_dag.py
@@ -60,6 +60,31 @@ class TestDagLongestPath:
         # this will raise NotImplementedError when nodes need to be ordered
         nx.dag_longest_path(G)
 
+    def test_multigraph_unweighted(self):
+        edges = [(1, 2), (2, 3), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
+        G = nx.MultiDiGraph(edges)
+        assert nx.dag_longest_path(G) == [1, 2, 3, 4, 5]
+
+    def test_multigraph_weighted(self):
+        G = nx.MultiDiGraph()
+        edges = [
+            (1, 2, 2),
+            (2, 3, 2),
+            (1, 3, 1),
+            (1, 3, 5),
+            (1, 3, 2),
+        ]
+        G.add_weighted_edges_from(edges)
+        assert nx.dag_longest_path(G) == [1, 3]
+
+    def test_multigraph_weighted_default_weight(self):
+        G = nx.MultiDiGraph([(1, 2), (2, 3)])  # Unweighted edges
+        G.add_weighted_edges_from([(1, 3, 1), (1, 3, 5), (1, 3, 2)])
+
+        # Default value for default weight is 1
+        assert nx.dag_longest_path(G) == [1, 3]
+        assert nx.dag_longest_path(G, default_weight=3) == [1, 2, 3]
+
 
 class TestDagLongestPathLength:
     """Unit tests for computing the length of a longest path in a
@@ -91,6 +116,23 @@ class TestDagLongestPathLength:
         G.add_weighted_edges_from(edges)
         assert nx.dag_longest_path_length(G) == 5
 
+    def test_multigraph_unweighted(self):
+        edges = [(1, 2), (2, 3), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
+        G = nx.MultiDiGraph(edges)
+        assert nx.dag_longest_path_length(G) == 4
+
+    def test_multigraph_weighted(self):
+        G = nx.MultiDiGraph()
+        edges = [
+            (1, 2, 2),
+            (2, 3, 2),
+            (1, 3, 1),
+            (1, 3, 5),
+            (1, 3, 2),
+        ]
+        G.add_weighted_edges_from(edges)
+        assert nx.dag_longest_path_length(G) == 5
+
 
 class TestDAG:
     @classmethod
diff --git a/networkx/algorithms/tests/test_richclub.py b/networkx/algorithms/tests/test_richclub.py
index 1ed42d459..5638ddbf0 100644
--- a/networkx/algorithms/tests/test_richclub.py
+++ b/networkx/algorithms/tests/test_richclub.py
@@ -80,6 +80,17 @@ def test_rich_club_exception2():
         nx.rich_club_coefficient(G)
 
 
+def test_rich_club_selfloop():
+    G = nx.Graph()  # or DiGraph, MultiGraph, MultiDiGraph, etc
+    G.add_edge(1, 1)  # self loop
+    G.add_edge(1, 2)
+    with pytest.raises(
+        Exception,
+        match="rich_club_coefficient is not implemented for " "graphs with self loops.",
+    ):
+        nx.rich_club_coefficient(G)
+
+
 # def test_richclub2_normalized():
 #    T = nx.balanced_tree(2,10)
 #    rcNorm = nx.richclub.rich_club_coefficient(T,Q=2)
diff --git a/networkx/classes/tests/test_graph.py b/networkx/classes/tests/test_graph.py
index 2adb159bf..46d517361 100644
--- a/networkx/classes/tests/test_graph.py
+++ b/networkx/classes/tests/test_graph.py
@@ -683,6 +683,9 @@ class TestGraph(BaseAttrGraphTester):
         G = self.Graph()
         G.add_edge(*(0, 1))
         assert G.adj == {0: {1: {}}, 1: {0: {}}}
+        G = self.Graph()
+        with pytest.raises(ValueError):
+            G.add_edge(None, "anything")
 
     def test_add_edges_from(self):
         G = self.Graph()
@@ -706,6 +709,8 @@ class TestGraph(BaseAttrGraphTester):
             G.add_edges_from([(0, 1, 2, 3)])  # too many in tuple
         with pytest.raises(TypeError):
             G.add_edges_from([0])  # not a tuple
+        with pytest.raises(ValueError):
+            G.add_edges_from([(None, 3), (3, 2)])  # None cannot be a node
 
     def test_remove_edge(self):
         G = self.K3.copy()
diff --git a/networkx/classes/tests/test_multigraph.py b/networkx/classes/tests/test_multigraph.py
index 0584f5620..07f7f5db6 100644
--- a/networkx/classes/tests/test_multigraph.py
+++ b/networkx/classes/tests/test_multigraph.py
@@ -303,6 +303,9 @@ class TestMultiGraph(BaseMultiGraphTester, _TestGraph):
         G = self.Graph()
         G.add_edge(*(0, 1))
         assert G.adj == {0: {1: {0: {}}}, 1: {0: {0: {}}}}
+        G = self.Graph()
+        with pytest.raises(ValueError):
+            G.add_edge(None, "anything")
 
     def test_add_edge_conflicting_key(self):
         G = self.Graph()
diff --git a/networkx/drawing/tests/test_pylab.py b/networkx/drawing/tests/test_pylab.py
index f642dcc47..cef2702df 100644
--- a/networkx/drawing/tests/test_pylab.py
+++ b/networkx/drawing/tests/test_pylab.py
@@ -1,6 +1,7 @@
 """Unit tests for matplotlib drawing functions."""
 import itertools
 import os
+import warnings
 
 import pytest
 
@@ -396,6 +397,7 @@ def test_labels_and_colors():
         G,
         pos,
         edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)],
+        arrows=True,
         min_source_margin=0.5,
         min_target_margin=0.75,
         width=8,
@@ -752,3 +754,38 @@ def test_draw_networkx_edges_undirected_selfloop_colors():
     for fap, clr, slp in zip(ax.patches, edge_colors[-3:], sl_points):
         assert fap.get_path().contains_point(slp)
         assert mpl.colors.same_color(fap.get_edgecolor(), clr)
+    plt.delaxes(ax)
+
+
+@pytest.mark.parametrize(
+    "fap_only_kwarg",  # Non-default values for kwargs that only apply to FAPs
+    (
+        {"arrowstyle": "-"},
+        {"arrowsize": 20},
+        {"connectionstyle": "arc3,rad=0.2"},
+        {"min_source_margin": 10},
+        {"min_target_margin": 10},
+    ),
+)
+def test_user_warnings_for_unused_edge_drawing_kwargs(fap_only_kwarg):
+    """Users should get a warning when they specify a non-default value for
+    one of the kwargs that applies only to edges drawn with FancyArrowPatches,
+    but FancyArrowPatches aren't being used under the hood."""
+    G = nx.path_graph(3)
+    pos = {n: (n, n) for n in G}
+    fig, ax = plt.subplots()
+    # By default, an undirected graph will use LineCollection to represent
+    # the edges
+    kwarg_name = list(fap_only_kwarg.keys())[0]
+    with pytest.warns(
+        UserWarning, match=f"\n\nThe {kwarg_name} keyword argument is not applicable"
+    ):
+        nx.draw_networkx_edges(G, pos, ax=ax, **fap_only_kwarg)
+    # FancyArrowPatches are always used when `arrows=True` is specified.
+    # Check that warnings are *not* raised in this case
+    with warnings.catch_warnings():
+        # Escalate warnings -> errors so tests fail if warnings are raised
+        warnings.simplefilter("error")
+        nx.draw_networkx_edges(G, pos, ax=ax, arrows=True, **fap_only_kwarg)
+
+    plt.delaxes(ax)
diff --git a/networkx/generators/tests/test_expanders.py b/networkx/generators/tests/test_expanders.py
index 58f6b1b8c..f84b04a74 100644
--- a/networkx/generators/tests/test_expanders.py
+++ b/networkx/generators/tests/test_expanders.py
@@ -64,9 +64,24 @@ def test_paley_graph(p):
             assert (v, u) in G.edges
 
 
-@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph))
+@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph, nx.MultiDiGraph))
 def test_margulis_gabber_galil_graph_badinput(graph_type):
     with pytest.raises(
         nx.NetworkXError, match="`create_using` must be an undirected multigraph"
     ):
         nx.margulis_gabber_galil_graph(3, create_using=graph_type)
+
+
+@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph, nx.MultiDiGraph))
+def test_chordal_cycle_graph_badinput(graph_type):
+    with pytest.raises(
+        nx.NetworkXError, match="`create_using` must be an undirected multigraph"
+    ):
+        nx.chordal_cycle_graph(3, create_using=graph_type)
+
+
+def test_paley_graph_badinput():
+    with pytest.raises(
+        nx.NetworkXError, match="`create_using` cannot be a multigraph."
+    ):
+        nx.paley_graph(3, create_using=nx.MultiGraph)
diff --git a/networkx/generators/tests/test_trees.py b/networkx/generators/tests/test_trees.py
index a13644a11..ea1f7d0c4 100644
--- a/networkx/generators/tests/test_trees.py
+++ b/networkx/generators/tests/test_trees.py
@@ -86,3 +86,16 @@ def test_random_tree():
     """Tests that a random tree is in fact a tree."""
     T = nx.random_tree(10, seed=1234)
     assert nx.is_tree(T)
+
+
+def test_random_tree_n_zero():
+    """Tests if n = 0 then the NetworkXPointlessConcept exception is raised."""
+    with pytest.raises(nx.NetworkXPointlessConcept):
+        T = nx.random_tree(0, seed=1234)
+
+
+def test_random_tree_using_generator():
+    """Tests that creating a ramdom tree with a generator works"""
+    G = nx.Graph()
+    T = nx.random_tree(10, seed=1234, create_using=G)
+    assert nx.is_tree(T)
diff --git a/networkx/readwrite/tests/test_gml.py b/networkx/readwrite/tests/test_gml.py
index 6e6ce0f3a..68b0da6f8 100644
--- a/networkx/readwrite/tests/test_gml.py
+++ b/networkx/readwrite/tests/test_gml.py
@@ -571,10 +571,6 @@ graph
         G = nx.Graph()
         G.graph["data"] = frozenset([1, 2, 3])
         assert_generate_error(G, stringizer=literal_stringizer)
-        G = nx.Graph()
-        G.graph["data"] = []
-        assert_generate_error(G)
-        assert_generate_error(G, stringizer=len)
 
     def test_label_kwarg(self):
         G = nx.parse_gml(self.simple_data, label="id")
@@ -712,3 +708,23 @@ class TestPropertyLists:
             f.seek(0)
             graph = nx.read_gml(f)
         assert graph.nodes(data=True)["n1"] == {"properties": ["element"]}
+
+
+@pytest.mark.parametrize("coll", (list(), tuple()))
+def test_stringize_empty_list_tuple(coll):
+    G = nx.path_graph(2)
+    G.nodes[0]["test"] = coll  # test serializing an empty collection
+    f = io.BytesIO()
+    nx.write_gml(G, f)  # Smoke test - should not raise
+    f.seek(0)
+    H = nx.read_gml(f)
+    assert H.nodes["0"]["test"] == coll  # Check empty list round-trips properly
+    # Check full round-tripping. Note that nodes are loaded as strings by
+    # default, so there needs to be some remapping prior to comparison
+    H = nx.relabel_nodes(H, {"0": 0, "1": 1})
+    assert nx.utils.graphs_equal(G, H)
+    # Same as above, but use destringizer for node remapping. Should have no
+    # effect on node attr
+    f.seek(0)
+    H = nx.read_gml(f, destringizer=int)
+    assert nx.utils.graphs_equal(G, H)