123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- """Unit tests for pydot drawing functions."""
- import os
- import tempfile
- from io import StringIO
- import pytest
- import networkx as nx
- from networkx.utils import graphs_equal
- pydot = pytest.importorskip("pydot")
- @pytest.mark.xfail
- class TestPydot:
- def pydot_checks(self, G, prog):
- """
- Validate :mod:`pydot`-based usage of the passed NetworkX graph with the
- passed basename of an external GraphViz command (e.g., `dot`, `neato`).
- """
- # Set the name of this graph to... "G". Failing to do so will
- # subsequently trip an assertion expecting this name.
- G.graph["name"] = "G"
- # Add arbitrary nodes and edges to the passed empty graph.
- G.add_edges_from([("A", "B"), ("A", "C"), ("B", "C"), ("A", "D")])
- G.add_node("E")
- # Validate layout of this graph with the passed GraphViz command.
- graph_layout = nx.nx_pydot.pydot_layout(G, prog=prog)
- assert isinstance(graph_layout, dict)
- # Convert this graph into a "pydot.Dot" instance.
- P = nx.nx_pydot.to_pydot(G)
- # Convert this "pydot.Dot" instance back into a graph of the same type.
- G2 = G.__class__(nx.nx_pydot.from_pydot(P))
- # Validate the original and resulting graphs to be the same.
- assert graphs_equal(G, G2)
- fd, fname = tempfile.mkstemp()
- # Serialize this "pydot.Dot" instance to a temporary file in dot format
- P.write_raw(fname)
- # Deserialize a list of new "pydot.Dot" instances back from this file.
- Pin_list = pydot.graph_from_dot_file(path=fname, encoding="utf-8")
- # Validate this file to contain only one graph.
- assert len(Pin_list) == 1
- # The single "pydot.Dot" instance deserialized from this file.
- Pin = Pin_list[0]
- # Sorted list of all nodes in the original "pydot.Dot" instance.
- n1 = sorted(p.get_name() for p in P.get_node_list())
- # Sorted list of all nodes in the deserialized "pydot.Dot" instance.
- n2 = sorted(p.get_name() for p in Pin.get_node_list())
- # Validate these instances to contain the same nodes.
- assert n1 == n2
- # Sorted list of all edges in the original "pydot.Dot" instance.
- e1 = sorted((e.get_source(), e.get_destination()) for e in P.get_edge_list())
- # Sorted list of all edges in the original "pydot.Dot" instance.
- e2 = sorted((e.get_source(), e.get_destination()) for e in Pin.get_edge_list())
- # Validate these instances to contain the same edges.
- assert e1 == e2
- # Deserialize a new graph of the same type back from this file.
- Hin = nx.nx_pydot.read_dot(fname)
- Hin = G.__class__(Hin)
- # Validate the original and resulting graphs to be the same.
- assert graphs_equal(G, Hin)
- os.close(fd)
- os.unlink(fname)
- def test_undirected(self):
- self.pydot_checks(nx.Graph(), prog="neato")
- def test_directed(self):
- self.pydot_checks(nx.DiGraph(), prog="dot")
- def test_read_write(self):
- G = nx.MultiGraph()
- G.graph["name"] = "G"
- G.add_edge("1", "2", key="0") # read assumes strings
- fh = StringIO()
- nx.nx_pydot.write_dot(G, fh)
- fh.seek(0)
- H = nx.nx_pydot.read_dot(fh)
- assert graphs_equal(G, H)
- def test_pydot_issue_258():
- G = nx.Graph([("Example:A", 1)])
- with pytest.raises(ValueError):
- nx.nx_pydot.to_pydot(G)
- with pytest.raises(ValueError):
- nx.nx_pydot.pydot_layout(G)
- G = nx.Graph()
- G.add_node("1.2", style="filled", fillcolor="red:yellow")
- with pytest.raises(ValueError):
- nx.nx_pydot.to_pydot(G)
- G.remove_node("1.2")
- G.add_node("1.2", style="filled", fillcolor='"red:yellow"')
- assert (
- G.nodes.data() == nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).nodes.data()
- )
- G = nx.DiGraph()
- G.add_edge("1", "2", foo="bar:1")
- with pytest.raises(ValueError):
- nx.nx_pydot.to_pydot(G)
- G = nx.DiGraph()
- G.add_edge("1", "2", foo='"bar:1"')
- assert G["1"]["2"] == nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G))["1"]["2"]
- G = nx.MultiGraph()
- G.add_edge("1", "2", foo="b:1")
- G.add_edge("1", "2", bar="foo:foo")
- with pytest.raises(ValueError):
- nx.nx_pydot.to_pydot(G)
- G = nx.MultiGraph()
- G.add_edge("1", "2", foo='"b:1"')
- G.add_edge("1", "2", bar='"foo:foo"')
- # Keys as integers aren't preserved in the conversion. They are read as strings.
- assert [attr for _, _, attr in G.edges.data()] == [
- attr
- for _, _, attr in nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).edges.data()
- ]
- G = nx.Graph()
- G.add_edge("1", "2")
- G["1"]["2"]["f:oo"] = "bar"
- with pytest.raises(ValueError):
- nx.nx_pydot.to_pydot(G)
- G = nx.Graph()
- G.add_edge("1", "2")
- G["1"]["2"]['"f:oo"'] = "bar"
- assert G["1"]["2"] == nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G))["1"]["2"]
- G = nx.Graph([('"Example:A"', 1)])
- layout = nx.nx_pydot.pydot_layout(G)
- assert isinstance(layout, dict)
- @pytest.mark.parametrize(
- "graph_type", [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph]
- )
- def test_hashable_pydot(graph_type):
- # gh-5790
- G = graph_type()
- G.add_edge("5", frozenset([1]), t='"Example:A"', l=False)
- G.add_edge("1", 2, w=True, t=("node1",), l=frozenset(["node1"]))
- G.add_edge("node", (3, 3), w="string")
- assert [
- {"t": '"Example:A"', "l": "False"},
- {"w": "True", "t": "('node1',)", "l": "frozenset({'node1'})"},
- {"w": "string"},
- ] == [
- attr
- for _, _, attr in nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).edges.data()
- ]
- assert {str(i) for i in G.nodes()} == set(
- nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).nodes
- )
- def test_pydot_numrical_name():
- G = nx.Graph()
- G.add_edges_from([("A", "B"), (0, 1)])
- graph_layout = nx.nx_pydot.pydot_layout(G, prog="dot")
- assert isinstance(graph_layout, dict)
- assert "0" not in graph_layout
- assert 0 in graph_layout
- assert "1" not in graph_layout
- assert 1 in graph_layout
- assert "A" in graph_layout
- assert "B" in graph_layout
|