# coding=iso-8859-1 from __future__ import division from __future__ import print_function import os import sys import warnings from textwrap import dedent import mock import pytest import pydot_ng as pydot PY3 = not sys.version_info < (3, 0, 0) if PY3: NULL_SEP = b"" xrange = range else: NULL_SEP = "" bytes = str DOT_BINARY_PATH = pydot.find_graphviz()["dot"] TEST_DIR = os.path.dirname(__file__) REGRESSION_TESTS_DIR = os.path.join(TEST_DIR, "graphs") MY_REGRESSION_TESTS_DIR = os.path.join(TEST_DIR, "my_tests") @pytest.fixture def digraph(): return pydot.Graph("testgraph", graph_type="digraph") @pytest.mark.parametrize("graph_type", ("graph", "digraph")) def test_keep_graph_type(graph_type): graph = pydot.Dot(graph_name="Test", graph_type=graph_type) assert graph.get_type() == graph_type assert graph.graph_type == graph_type def test_node_style(): node = pydot.Node("mynode") assert node.get_style() is None node.add_style("abc") assert node.get_style() == "abc" node.add_style("def") assert node.get_style() == "abc,def" node.add_style("ghi") assert node.get_style() == "abc,def,ghi" def test_create_simple_graph_with_node(): graph = pydot.Dot(graph_type="digraph") node = pydot.Node("legend") node.set("shape", "box") node.set("label", "mine") graph.add_node(node) assert graph.to_string() == dedent( """\ digraph G { legend [label=mine, shape=box]; } """ ) def test_attribute_with_implicit_value(): dot = dedent( """ digraph { na -> b[label="hi", decorate]; } """ ) graph = pydot.graph_from_dot_data(dot) edge = graph.get_edges()[0] attrs = edge.get_attributes() assert attrs == {"decorate": None, "label": '"hi"'} def test_subgraphs(): graph = pydot.Graph() subgraph = pydot.Subgraph("foo") assert graph.get_subgraphs() == [] assert graph.get_subgraph_list() == [] graph.add_subgraph(subgraph) assert len(graph.get_subgraphs()) == 1 assert len(graph.get_subgraph_list()) == 1 assert graph.get_subgraphs()[0].get_name() == subgraph.get_name() assert graph.get_subgraph_list()[0].get_name() == subgraph.get_name() def test_graph_is_picklabe(): import pickle graph = pydot.Graph() subgraph = pydot.Subgraph("foo") graph.add_subgraph(subgraph) graph.add_edge(pydot.Edge("A", "B")) graph.add_edge(pydot.Edge("A", "C")) graph.add_edge(pydot.Edge(("D", "E"))) graph.add_node(pydot.Node("node!")) assert isinstance(pickle.dumps(graph), bytes) def test_unicode_ids(): node1 = '"aánñoöüé€"' node2 = '"îôø®çßΩ"' graph = pydot.Dot() graph.set_charset("latin1") graph.add_node(pydot.Node(node1)) graph.add_node(pydot.Node(node2)) graph.add_edge(pydot.Edge(node1, node2)) assert graph.get_node(node1)[0].get_name() == node1 assert graph.get_node(node2)[0].get_name() == node2 assert graph.get_edges()[0].get_source() == node1 assert graph.get_edges()[0].get_destination() == node2 graph2 = pydot.graph_from_dot_data(graph.to_string()) assert graph2.get_node(node1)[0].get_name() == node1 assert graph2.get_node(node2)[0].get_name() == node2 assert graph2.get_edges()[0].get_source() == node1 assert graph2.get_edges()[0].get_destination() == node2 def test_parse_multiple_graphs(): graph_data = dedent( """\ graph A { a->b }; graph B {c->d} """ ) graphs = pydot.graph_from_dot_data(graph_data) assert len(graphs) == 2 assert sorted(g.get_name() for g in graphs) == sorted(["A", "B"]) def test_numeric_node_id(digraph): digraph.add_node(pydot.Node(1)) assert digraph.get_nodes()[0].get_name() == "1" def test_quoted_node_id(digraph): digraph.add_node(pydot.Node('"node"')) assert digraph.get_nodes()[0].get_name() == '"node"' def test_quoted_node_id_to_string_no_attributes(digraph): digraph.add_node(pydot.Node('"node"')) assert digraph.get_nodes()[0].to_string() == '"node";' def test_keyword_node_id(digraph): digraph.add_node(pydot.Node("node")) assert digraph.get_nodes()[0].get_name() == "node" def test_keyword_node_id_to_string_no_attributes(digraph): digraph.add_node(pydot.Node("node")) assert digraph.get_nodes()[0].to_string() == "" def test_keyword_node_id_to_string_with_attributes(digraph): digraph.add_node(pydot.Node("node", shape="box")) assert digraph.get_nodes()[0].to_string() == "node [shape=box];" def test_names_of_a_thousand_nodes(digraph): names = set(["node_%05d" % i for i in xrange(10 ** 4)]) for name in names: digraph.add_node(pydot.Node(name, label=name)) assert set([n.get_name() for n in digraph.get_nodes()]) == names def test_executable_not_found_exception(): graph = pydot.Dot("graphname", graph_type="digraph") paths = {"dot": "invalid_executable_path"} graph.set_graphviz_executables(paths) with pytest.raises(pydot.InvocationException): graph.create() @pytest.mark.parametrize("node", (1, "a", None)) def test_graph_add_node_argument_type(digraph, node): with pytest.raises(TypeError) as exc_info: digraph.add_node(node) assert "add_node() received a non node class object" in str(exc_info.value) @pytest.mark.parametrize("edge", (1, "a", None)) def test_graph_add_edge_argument_type(digraph, edge): with pytest.raises(TypeError) as exc_info: digraph.add_edge(edge) assert "add_edge() received a non edge class object" in str(exc_info.value) @pytest.mark.parametrize("subgraph", (1, "a", None)) def test_graph_add_subgraph_argument_type(digraph, subgraph): with pytest.raises(TypeError) as exc_info: digraph.add_subgraph(subgraph) assert "add_subgraph() received a non subgraph class object" in str( exc_info.value ) def test_quoting(): import string g = pydot.Dot() g.add_node(pydot.Node("test", label=string.printable)) data = g.create(format="jpe") assert len(data) > 0 @pytest.mark.parametrize( "input, expected", ( ("A:", '"A:"'), (":B", '":B"'), ("A:B", "A:B"), ("1A", '"1A"'), ("A", "A"), ("11", "11"), ("_xyz", "_xyz"), (".11", '".11"'), ("-.09", '"-.09"'), ("1.8", '"1.8"'), # ('', '""'), ('"1abc"', '"1abc"'), ("@", '"@"'), ("ÿ", '"ÿ"'), ( "$GUID__/ffb73e1c-7495-40b3-9618-9e5462fc89c7", '"$GUID__/ffb73e1c-7495-40b3-9618-9e5462fc89c7"', ), ), ) def test_quote_if_necessary(input, expected): assert pydot.quote_if_necessary(input) == expected @pytest.mark.xfail( (3, 7) > sys.version_info >= (3, 6), reason="python 3.6 on Travis is failing this and no way to debug it now", ) def test_dotparser_import_warning(): with mock.patch.dict(sys.modules, {"pydot_ng._dotparser": None}): with pytest.warns( UserWarning, match="Couldn't import _dotparser, loading" " of dot files will not be possible.", ): del sys.modules["pydot_ng"] warnings.simplefilter("always") import pydot_ng # noqa: F401 def test_find_executables_fake_path(): assert pydot.__find_executables("/fake/path/") is None def test_find_executables_real_path_no_programs(tmpdir): assert pydot.__find_executables(str(tmpdir)) is None def test_find_executables_path_needs_strip(tmpdir): path = tmpdir.mkdir("subdir") prog_path = str(path.join("dot")) path_with_spaces = " {} ".format(path) with open(prog_path, "w"): progs = pydot.__find_executables(path_with_spaces) assert progs["dot"] == prog_path assert sorted( ("dot", "twopi", "neato", "circo", "fdp", "sfdp") ) == sorted(progs) def test_find_executables_unix_and_exe_exists(tmpdir): path = str(tmpdir) prog_unix_path = str(tmpdir.join("dot")) prog_exe_path = str(tmpdir.join("dot.exe")) with open(prog_unix_path, "w"): with open(prog_exe_path, "w"): progs = pydot.__find_executables(path) assert progs["dot"] == prog_unix_path assert progs["dot"] != prog_exe_path @pytest.mark.parametrize("quoted", (True, False), ids=("quoted", "unqoted")) @pytest.mark.parametrize("extension", ("", ".exe")) @pytest.mark.parametrize( "program", ("dot", "twopi", "neato", "circo", "fdp", "sfdp") ) def test_find_executables(tmpdir, program, extension, quoted): path = tmpdir.mkdir("PYDOT is_da best!") prog_path = str(path.join(program + extension)) with open(prog_path, "w"): if quoted: path = "\"{}\"".format(path) prog_path = "\"{}\"".format(prog_path) progs = pydot.__find_executables(str(path)) assert progs[program] == prog_path assert sorted( ("dot", "twopi", "neato", "circo", "fdp", "sfdp") ) == sorted(progs)