Skip to content

Tests

Here you can read about the tests of meshnetworkx.

conftest

This module contains pytest fixtures for testing meshnetworkx.

mnx_graph()

Fixture to create and teardown GraphZ instance.

Source code in tests/conftest.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@pytest.fixture
def mnx_graph():
    """Fixture to create and teardown GraphZ instance."""
    g = mx.GraphZ()

    # This should not be necessary,
    # but just in case a previous test did not cleanup properly
    g.clear()

    yield g

    g.clear()
    g.close()

test_graph

This module contains basic tests for the GraphZ class.

test_add_edge(mnx_graph)

Test adding an edge to the graph.

Source code in tests/test_graph.py
243
244
245
246
247
248
249
250
def test_add_edge(mnx_graph):
    """Test adding an edge to the graph."""
    G = mnx_graph
    G.add_edge(0, 1)
    assert G.adj == {"0": {"1": {}}, "1": {"0": {}}}

    with pytest.raises(TypeError):
        G.add_edge(None, "anything")

test_add_edges_from(self)

Test adding edges from a list with various attributes.

Source code in tests/test_graph.py
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
@pytest.mark.skip("TODO")
def test_add_edges_from(self):
    """Test adding edges from a list with various attributes."""
    G = self.Graph()
    G.add_edges_from([(0, 1), (0, 2, {"weight": 3})])
    assert G.adj == {
        0: {1: {}, 2: {"weight": 3}},
        1: {0: {}},
        2: {0: {"weight": 3}},
    }
    G = self.Graph()
    G.add_edges_from([(0, 1), (0, 2, {"weight": 3}), (1, 2, {"data": 4})], data=2)
    assert G.adj == {
        0: {1: {"data": 2}, 2: {"weight": 3, "data": 2}},
        1: {0: {"data": 2}, 2: {"data": 4}},
        2: {0: {"weight": 3, "data": 2}, 1: {"data": 4}},
    }

    with pytest.raises(nx.NetworkXError):
        G.add_edges_from([(0,)])  # too few in tuple
    with pytest.raises(nx.NetworkXError):
        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

test_add_node_simple(mnx_graph)

Test adding a node to the graph.

Source code in tests/test_graph.py
11
12
13
14
15
16
def test_add_node_simple(mnx_graph):
    """Test adding a node to the graph."""
    # Test adding a node to the graph
    mnx_graph.add_node("node1", color="blue")
    nodes = mnx_graph.nodes()
    assert "node1" in nodes

test_add_node_with_attributes(mnx_graph)

Test adding a node with attributes.

Source code in tests/test_graph.py
69
70
71
72
73
74
75
76
77
78
def test_add_node_with_attributes(mnx_graph):
    """Test adding a node with attributes."""
    # Test adding a node with attributes
    WEIGHT = 5
    mnx_graph.add_node("node2", color="green", weight=WEIGHT)
    nodes = mnx_graph.nodes(data=True)
    assert any(
        node == "node2" and data["color"] == "green" and data["weight"] == WEIGHT
        for node, data in nodes
    )

test_add_nodes_from(mnx_graph)

Test adding nodes from a list and removing some of them.

Source code in tests/test_graph.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def test_add_nodes_from(mnx_graph):
    """Test adding nodes from a list and removing some of them."""
    G = mnx_graph
    G.add_nodes_from(list("ABCDEFGHIJKL"))
    assert G.has_node("L")
    G.remove_nodes_from(["H", "I", "J", "K", "L"])
    G.add_nodes_from([1, 2, 3, 4])
    assert sorted(G.nodes(), key=str) == [
        "1",
        "2",
        "3",
        "4",
        "A",
        "B",
        "C",
        "D",
        "E",
        "F",
        "G",
    ]
    # test __iter__
    assert sorted(G, key=str) == ["1", "2", "3", "4", "A", "B", "C", "D", "E", "F", "G"]

test_clear(mnx_graph, times)

Test clearing all nodes from the graph.

Source code in tests/test_graph.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
@pytest.mark.parametrize("times", [1, 10, 100])
def test_clear(mnx_graph, times: int):
    """Test clearing all nodes from the graph."""
    # Test clearing all nodes
    NR_NODES = 4

    for _ in range(times):
        mnx_graph.add_node("node1")
        mnx_graph.add_node("node2")
        mnx_graph.add_edge("node1", "node2")
        mnx_graph.add_edge("node3", "node4")
        assert len(mnx_graph.nodes()) == NR_NODES

        mnx_graph.clear()
        assert len(mnx_graph.nodes()) == 0

test_clear_orig(mnx_graph)

Test clearing the graph and its attributes.

Source code in tests/test_graph.py
325
326
327
328
329
330
331
332
333
334
def test_clear_orig(mnx_graph):
    """Test clearing the graph and its attributes."""
    G = mnx_graph
    G.add_node(1)
    G.add_edge(1, 2, color="red")
    # G.graph["name"] = "K3"
    G.clear()
    # assert list(G.nodes) == []  # FIXME: need to update NodeView so it works like this
    assert list(G.nodes()) == []
    assert G.adj == {}

test_duplicate_node_warning(mnx_graph)

How do we handle adding a node with a key that already exists?

Source code in tests/test_graph.py
235
236
237
238
239
240
@pytest.mark.skip("Not implemented")
def test_duplicate_node_warning(mnx_graph):
    """How do we handle adding a node with a key that already exists?"""
    mnx_graph.add_node("node10", color="orange")
    mnx_graph.add_node("node10", color="purple")
    raise AssertionError()

test_has_edge(mnx_graph)

Test checking if an edge exists in the graph.

Source code in tests/test_graph.py
265
266
267
268
269
270
271
272
273
274
def test_has_edge(mnx_graph):
    """Test checking if an edge exists in the graph."""
    G = mnx_graph
    G.add_edge(1, 2)

    # simple graph has bidirectional edges
    assert G.has_edge(1, 2)
    assert G.has_edge(2, 1)

    assert not G.has_edge(3, 1)

test_has_node(mnx_graph)

Test checking if a node exists in the graph.

Source code in tests/test_graph.py
370
371
372
373
374
375
def test_has_node(mnx_graph):
    """Test checking if a node exists in the graph."""
    G = mnx_graph
    G.add_node(1)
    assert G.has_node(1)
    assert not G.has_node(2)

test_has_node_str(mnx_graph)

Test checking if a node exists in the graph with a string.

Source code in tests/test_graph.py
378
379
380
381
382
383
def test_has_node_str(mnx_graph):
    """Test checking if a node exists in the graph with a string."""
    G = mnx_graph
    G.add_node("1")
    assert G.has_node("1")
    assert not G.has_node("2")

test_mnx_to_nx()

Test converting a GraphZ instance to a NetworkX graph.

Source code in tests/test_graph.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
@pytest.mark.xfail(reason="Sorting of edges is not implemented yet.")
def test_mnx_to_nx():
    """Test converting a GraphZ instance to a NetworkX graph."""
    G = mx.GraphZ()
    G.add_nodes_from(list("ABCDEFGHIJKL"))
    # G.add_edge("A", "B", color="purple")
    G.add_edge("B", "A", color="purple")

    G2 = G.to_networkx()

    assert sorted(G.nodes()) == sorted(G2.nodes())
    assert sorted(G.edges()) == sorted(G2.edges())

    G.clear()
    G.close()

test_node_attribute_management(mnx_graph)

Test adding nodes with various attributes and updating them correctly.

Source code in tests/test_graph.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def test_node_attribute_management(mnx_graph):
    """Test adding nodes with various attributes and updating them correctly."""
    G = mnx_graph
    G.add_node(0)

    # TODO: enable adj
    # FIXME: adj is not working as intended yet.
    # With a single node it returns an empty dict, instead of an unconnected node.
    # assert G.adj == {0: {}}

    # test add attributes
    G.add_node(1, c="red")
    G.add_node(2, c="blue")
    G.add_node(3, c="red")
    assert G.nodes["1"]["c"] == "red"
    assert G.nodes["2"]["c"] == "blue"
    assert G.nodes["3"]["c"] == "red"
    # test updating attributes
    G.add_node(1, c="blue")
    G.add_node(2, c="red")
    G.add_node(3, c="blue")
    assert G.nodes["1"]["c"] == "blue"
    assert G.nodes["2"]["c"] == "red"
    assert G.nodes["3"]["c"] == "blue"

test_node_view_assignment_raises_error(mnx_graph)

Test that assigning to the node view raises an error.

Source code in tests/test_graph.py
350
351
352
353
354
355
356
def test_node_view_assignment_raises_error(mnx_graph):
    """Test that assigning to the node view raises an error."""
    G = mnx_graph
    G.add_node(1, color="red")

    with pytest.raises(TypeError):
        G.nodes[1] = "foo"

test_node_view_data_assignment_raises_error(mnx_graph)

Test that assigning to the node view raises an error.

Source code in tests/test_graph.py
359
360
361
362
363
364
365
366
367
@pytest.mark.skip("TODO: node data is still unprotected since it is a regular dict.")
def test_node_view_data_assignment_raises_error(mnx_graph):
    """Test that assigning to the node view raises an error."""
    G = mnx_graph
    G.add_node(1, color="red")

    # fails
    with pytest.raises(TypeError):
        G.nodes["1"]["color"] = "green"

test_nodes_with_data(mnx_graph)

Test getting nodes with attributes.

Source code in tests/test_graph.py
145
146
147
148
149
150
151
152
def test_nodes_with_data(mnx_graph):
    """Test getting nodes with attributes."""
    # Test getting nodes with attributes
    mnx_graph.add_node("node10", color="orange")
    mnx_graph.add_node("node11", color="purple")
    nodes = mnx_graph.nodes(data=True)
    assert any(node == "node10" and data["color"] == "orange" for node, data in nodes)
    assert any(node == "node11" and data["color"] == "purple" for node, data in nodes)

test_nodes_without_data(mnx_graph)

Test getting nodes without attributes.

Source code in tests/test_graph.py
135
136
137
138
139
140
141
142
def test_nodes_without_data(mnx_graph):
    """Test getting nodes without attributes."""
    # Test getting nodes without attributes
    mnx_graph.add_node("node8")
    mnx_graph.add_node("node9")
    nodes = mnx_graph.nodes()
    assert "node8" in nodes
    assert "node9" in nodes

test_nx_to_mnx()

Test converting a NetworkX graph to a GraphZ instance.

Source code in tests/test_graph.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
@pytest.mark.xfail(reason="Edges are not yet converted.")
def test_nx_to_mnx():
    """Test converting a NetworkX graph to a GraphZ instance."""
    G = nx.Graph()
    G.add_nodes_from(list("ABCDEFGHIJKL"))
    G.add_edge("A", "B", color="purple")

    Z = mx.GraphZ.from_networkx(G)

    assert sorted(G.nodes()) == sorted(Z.nodes())
    assert sorted(G.edges()) == sorted(Z.edges())

    Z.clear()
    Z.close()

test_nx_to_mnx_to_nx_nodes_only()

Test converting a NetworkX graph to a GraphZ instance and back to NetworkX.

Source code in tests/test_graph.py
188
189
190
191
192
193
194
195
196
197
198
199
def test_nx_to_mnx_to_nx_nodes_only():
    """Test converting a NetworkX graph to a GraphZ instance and back to NetworkX."""
    G = nx.Graph()
    G.add_nodes_from(list("ABCDEFGHIJKL"))
    Z = mx.GraphZ.from_networkx(G)

    G2 = Z.to_networkx()

    assert sorted(G.nodes()) == sorted(G2.nodes())

    Z.clear()
    Z.close()

test_nx_to_zgraph_to_nx()

Test converting a NetworkX graph to a GraphZ instance and back to NetworkX.

Source code in tests/test_graph.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
@pytest.mark.xfail(reason="Sorting of edges is somehow random.")
def test_nx_to_zgraph_to_nx():
    """Test converting a NetworkX graph to a GraphZ instance and back to NetworkX."""
    G = nx.Graph()
    G.add_edge("1", "2", color="purple")

    Z = mx.GraphZ.from_networkx(G)
    G2 = Z.to_networkx()

    assert sorted(G.nodes()) == sorted(G2.nodes())
    assert sorted(G.edges()) == sorted(G2.edges())

    Z.clear()
    Z.close()

test_nx_to_zgraph_to_nx_int()

Test converting a NetworkX graph to a GraphZ instance and back to NetworkX.

Source code in tests/test_graph.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
@pytest.mark.skip(
    "Because we convert the node data to a string, cannot compare the data."
)
def test_nx_to_zgraph_to_nx_int():
    """Test converting a NetworkX graph to a GraphZ instance and back to NetworkX."""
    G = nx.Graph()
    G.add_edge("1", "2", color="purple")

    Z = mx.GraphZ.from_networkx(G)
    G2 = Z.to_networkx()

    assert sorted(G.nodes()) == sorted(G2.nodes())
    assert sorted(G.edges()) == sorted(G2.edges())
    Z.clear()
    Z.close()

test_remove_edge(mnx_graph)

Test removing an edge from the graph.

Source code in tests/test_graph.py
253
254
255
256
257
258
259
260
261
262
def test_remove_edge(mnx_graph):
    """Test removing an edge from the graph."""
    G = mnx_graph
    G.add_edge(1, 2, weight=3)
    assert G.adj == {"1": {"2": {}}, "2": {"1": {}}}
    G.remove_edge(1, 2)
    assert G.adj == {}
    # TODO: (match nx) raise error when edge does not exist
    with pytest.raises(mx.MeshNetworkXError):
        G.remove_edge(-1, 0)

test_remove_edge_2(mnx_graph)

Test removing an edge from the graph with multiple edges.

Source code in tests/test_graph.py
277
278
279
280
281
282
283
284
285
def test_remove_edge_2(mnx_graph):
    """Test removing an edge from the graph with multiple edges."""
    G = mnx_graph
    G.add_edge(1, 2, weight=3)
    assert G.adj == {"1": {"2": {}}, "2": {"1": {}}}
    G.add_edge(0, 1, weight=2)
    assert G.adj == {"0": {"1": {}}, "1": {"2": {}, "0": {}}, "2": {"1": {}}}
    G.remove_edge(0, 1)
    assert G.adj == {"1": {"2": {}}, "2": {"1": {}}}

test_remove_edges_from(self)

Test removing edges from the graph.

Source code in tests/test_graph.py
316
317
318
319
320
321
322
@pytest.mark.skip("TODO")
def test_remove_edges_from(self):
    """Test removing edges from the graph."""
    G = self.K3.copy()
    G.remove_edges_from([(0, 1)])
    assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
    G.remove_edges_from([(0, 0)])  # silent fail

test_remove_node(mnx_graph)

Test removing a node from the graph.

Source code in tests/test_graph.py
81
82
83
84
85
86
87
88
89
90
def test_remove_node(mnx_graph):
    """Test removing a node from the graph."""
    # Test removing a node
    mnx_graph.add_node("node3")
    nodes = mnx_graph.nodes()
    assert "node3" in nodes

    mnx_graph.remove_node("node3")
    nodes = mnx_graph.nodes()
    assert "node3" not in nodes

test_remove_node_hardcore(mnx_graph)

Test removing a node with edges from the graph.

Source code in tests/test_graph.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
def test_remove_node_hardcore(mnx_graph):
    """Test removing a node with edges from the graph."""
    G = mnx_graph
    G.add_edge(1, 2)
    assert G.adj == {"1": {"2": {}}, "2": {"1": {}}}
    G.add_node(0)
    G.remove_node(0)
    assert G.adj == {"1": {"2": {}}, "2": {"1": {}}}
    with pytest.raises(mx.MeshNetworkXError):
        G.remove_node(-1)

test_removing_node_removes_edge(mnx_graph)

Test removing a node also removes its edges.

Source code in tests/test_graph.py
338
339
340
341
342
343
344
345
346
347
def test_removing_node_removes_edge(mnx_graph):
    """Test removing a node also removes its edges."""
    G = mnx_graph
    G.add_edge(1, 2, weight=3)
    assert G.adj == {"1": {"2": {}}, "2": {"1": {}}}
    G.add_edge(0, 1, weight=2)
    assert G.adj == {"0": {"1": {}}, "1": {"2": {}, "0": {}}, "2": {"1": {}}}
    G.remove_node(0)
    time.sleep(1)
    assert G.adj == {"1": {"2": {}}, "2": {"1": {}}}

test_to_networkx(mnx_graph)

Test converting GraphZ instance to NetworkX graph.

Source code in tests/test_graph.py
122
123
124
125
126
127
128
129
130
131
132
def test_to_networkx(mnx_graph):
    """Test converting GraphZ instance to NetworkX graph."""
    # Test converting to NetworkX graph
    mnx_graph.add_node("node6", color="yellow")
    mnx_graph.add_node("node7", color="red")
    g = mnx_graph.to_networkx()
    assert isinstance(g, nx.Graph)
    assert "node6" in g.nodes
    assert g.nodes["node6"]["color"] == "yellow"
    assert "node7" in g.nodes
    assert g.nodes["node7"]["color"] == "red"

test_types_for_nodes(mnx_graph, input_data, error)

Test that we can use different types for nodes.

Source code in tests/test_graph.py
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
@pytest.mark.parametrize(
    "input_data, error",
    [
        (1, None),
        ("1", None),
        (1.0, None),
        (1j, None),
        (True, None),
        (False, None),
        (None, TypeError),
        ("1.0", None),
        ("1j", None),
        ("True", None),
        ("False", None),
        ("None", None),
        (b"1", None),
        (b"True", None),
        (b"False", None),
        (bytearray(b"1"), None),
        ("/", mx.MeshNetworkXError),
        ("?", mx.MeshNetworkXError),
        ("*", mx.MeshNetworkXError),
        ("**", mx.MeshNetworkXError),
    ],
)
def test_types_for_nodes(mnx_graph, input_data, error):
    """Test that we can use different types for nodes."""
    G = mnx_graph
    if error:
        with pytest.raises(error):
            G.add_node(input_data)
    else:
        G.add_node(input_data)

test_interface

Tests to ensure that the GraphZ interface matches the NetworkX Graph interface.

test_graph_attributes()

Test that all attributes in NetworkX Graph are present in GraphZ.

Source code in tests/test_interface.py
115
116
117
118
119
120
121
122
123
124
125
def test_graph_attributes():
    """Test that all attributes in NetworkX Graph are present in GraphZ."""
    nx_graph = nx.Graph()
    graphz_graph = mx.GraphZ()

    for attr in dir(nx_graph):
        if (
            not callable(getattr(nx_graph, attr))
            and attr not in WHITELIST["attributes"]
        ):
            assert hasattr(graphz_graph, attr), f"Graphz is missing attribute: {attr}"

test_graph_interface()

Test that the GraphZ interface matches the NetworkX Graph interface.

Source code in tests/test_interface.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def test_graph_interface():
    """Test that the GraphZ interface matches the NetworkX Graph interface."""
    nx_graph = nx.Graph()
    graphz_graph = mx.GraphZ()

    # Check if both have the same methods
    nx_methods = set(dir(nx_graph))
    graphz_methods = set(dir(graphz_graph))

    method_differences = (nx_methods - graphz_methods) - set(WHITELIST["methods"])
    _log_differences(method_differences, "methods")
    assert (
        not method_differences
    ), "Graphz interface does not match NetworkX Graph interface"

    # Check if both have the same attributes
    nx_attrs = {attr for attr in nx_methods if not callable(getattr(nx_graph, attr))}
    graphz_attrs = {
        attr for attr in graphz_methods if not callable(getattr(graphz_graph, attr))
    }

    attr_differences = (nx_attrs - graphz_attrs) - set(WHITELIST["attributes"])
    _log_differences(attr_differences, "attributes")
    assert (
        not attr_differences
    ), "Graphz attributes do not match NetworkX Graph attributes"

test_graph_methods()

Test that all methods in NetworkX Graph are present in GraphZ.

Source code in tests/test_interface.py
105
106
107
108
109
110
111
112
def test_graph_methods():
    """Test that all methods in NetworkX Graph are present in GraphZ."""
    nx_graph = nx.Graph()
    graphz_graph = mx.GraphZ()

    for method in dir(nx_graph):
        if callable(getattr(nx_graph, method)) and method not in WHITELIST["methods"]:
            assert hasattr(graphz_graph, method), f"Graphz is missing method: {method}"

test_mesh

This module contains tests while running in multiple processes.

test_subprocess_communication(mnx_graph)

Test communication in a subprocess.

Source code in tests/test_mesh.py
18
19
20
21
22
23
24
25
def test_subprocess_communication(mnx_graph):
    """Test communication in a subprocess."""
    p = mp.Process(target=_start_and_write_graph)
    p.start()
    p.join()

    assert mnx_graph.has_node("a")
    assert mnx_graph.nodes["a"]["color"] == "red"

test_subprocess_multiple_nodes(mnx_graph)

Test adding multiple nodes in a subprocess.

Source code in tests/test_mesh.py
38
39
40
41
42
43
44
45
46
47
48
49
def test_subprocess_multiple_nodes(mnx_graph):
    """Test adding multiple nodes in a subprocess."""
    p = mp.Process(target=_start_and_add_multiple_nodes)
    p.start()
    p.join()

    assert mnx_graph.has_node("a")
    assert mnx_graph.has_node("b")
    assert mnx_graph.has_node("c")
    assert mnx_graph.nodes["a"]["color"] == "red"
    assert mnx_graph.nodes["b"]["color"] == "blue"
    assert mnx_graph.nodes["c"]["color"] == "green"

test_subprocess_remove_node(mnx_graph)

Test removing a node in a subprocess.

Source code in tests/test_mesh.py
60
61
62
63
64
65
66
67
68
def test_subprocess_remove_node(mnx_graph):
    """Test removing a node in a subprocess."""
    mnx_graph.add_node("a", color="red")

    p = mp.Process(target=_start_and_remove_node)
    p.start()
    p.join()

    assert not mnx_graph.has_node("a")