1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
"""
Graphviz miscellaneous test cases
"""
import itertools
import json
import os
import sys
from pathlib import Path
from typing import Union
sys.path.append(os.path.dirname(__file__))
from gvtest import ROOT, compile_c, dot, run # pylint: disable=wrong-import-position
def test_json_node_order():
"""
test that nodes appear in JSON output in the same order as they were input
"""
# a simple graph with some nodes
input = (
"digraph G {\n"
" ordering=out;\n"
' {rank=same;"1";"2"};\n'
' "1"->"2";\n'
' {rank=same;"4";"5";};\n'
' "4"->"5";\n'
' "7"->"5";\n'
' "7"->"4";\n'
' "6"->"1";\n'
' "3"->"6";\n'
' "6"->"4";\n'
' "3"->"8";\n'
"}"
)
# turn it into JSON
output = dot("json", source=input)
# parse this into a data structure we can inspect
data = json.loads(output)
# extract the nodes, filtering out subgraphs
nodes = [n["name"] for n in data["objects"] if "nodes" not in n]
# the nodes should appear in the order in which they were seen in the input
assert nodes == ["1", "2", "4", "5", "7", "6", "3", "8"]
def test_json_edge_order():
"""
test that edges appear in JSON output in the same order as they were input
"""
# a simple graph with some edges
input = (
"digraph G {\n"
" ordering=out;\n"
' {rank=same;"1";"2"};\n'
' "1"->"2";\n'
' {rank=same;"4";"5";};\n'
' "4"->"5";\n'
' "7"->"5";\n'
' "7"->"4";\n'
' "6"->"1";\n'
' "3"->"6";\n'
' "6"->"4";\n'
' "3"->"8";\n'
"}"
)
# turn it into JSON
output = dot("json", source=input)
# parse this into a data structure we can inspect
data = json.loads(output)
# the edges should appear in the order in which they were seen in the input
expected = [
("1", "2"),
("4", "5"),
("7", "5"),
("7", "4"),
("6", "1"),
("3", "6"),
("6", "4"),
("3", "8"),
]
edges = [
(data["objects"][e["tail"]]["name"], data["objects"][e["head"]]["name"])
for e in data["edges"]
]
assert edges == expected
def test_xml_escape(tmp_path: Path):
"""
Check the functionality of ../lib/util/xml.c:gv_xml_escape.
"""
# locate our test program
xml_c = Path(__file__).parent / "../lib/util/xml.c"
assert xml_c.exists(), "missing xml.c"
# compile the stub to something we can run
xml_exe = tmp_path / "xml.exe"
lib = ROOT / "lib"
cflags = ["-DTEST_XML", f"-I{lib}"]
compile_c(xml_c, cflags, dst=xml_exe)
def escape(
dash: bool, nbsp: bool, raw: bool, utf8: bool, s: Union[bytes, str]
) -> str:
source = tmp_path / "input"
if isinstance(s, bytes):
source.write_bytes(s)
else:
source.write_text(s, encoding="utf-8")
destination = tmp_path / "output"
args = [xml_exe]
if dash:
args += ["--dash"]
if nbsp:
args += ["--nbsp"]
if raw:
args += ["--raw"]
if utf8:
args += ["--utf8"]
args += [source, destination]
run(args)
return destination.read_text(encoding="utf-8")
for dash, nbsp, raw, utf8 in itertools.product((False, True), repeat=4):
# something basic with nothing escapable
plain = "the quick brown fox"
plain_escaped = escape(dash, nbsp, raw, utf8, plain)
assert plain == plain_escaped, "text incorrectly modified"
# basic tag escaping
tag = "template <typename T> void foo(T t);"
tag_escaped = escape(dash, nbsp, raw, utf8, tag)
assert (
tag_escaped == "template <typename T> void foo(T t);"
), "incorrect < or > escaping"
# something with an embedded escape
embedded = "salt & pepper"
embedded_escaped = escape(dash, nbsp, raw, utf8, embedded)
if raw:
assert embedded_escaped == "salt &amp; pepper", "missing & escape"
else:
assert embedded_escaped == embedded, "text incorrectly modified"
# hyphen escaping
hyphen = "UTF-8"
hyphen_escaped = escape(dash, nbsp, raw, utf8, hyphen)
if dash:
assert hyphen_escaped == "UTF-8", "incorrect dash escape"
else:
assert hyphen_escaped == hyphen, "text incorrectly modified"
# line endings
nl = b"the quick\nbrown\rfox"
nl_escaped = escape(dash, nbsp, raw, utf8, nl)
if raw:
assert (
nl_escaped == "the quick brown fox"
), "incorrect new line escape"
else:
# allow benign modification of the \r
assert nl_escaped in (
"the quick\nbrown\rfox",
"the quick\nbrown\nfox",
), "text incorrectly modified"
# non-breaking space escaping
two = "the quick brown fox"
two_escaped = escape(dash, nbsp, raw, utf8, two)
if nbsp:
assert two_escaped == "the quick  brown fox", "incorrect nbsp escape"
else:
assert two_escaped == two, "text incorrectly modified"
# cases from table in https://en.wikipedia.org/wiki/UTF-8
for c, expected in (
("$", "$"),
("¢", "¢"),
("ह", "ह"),
("€", "€"),
("한", "한"),
("𐍈", "𐍈"),
):
unescaped = f"character |{c}|"
escaped = escape(dash, nbsp, raw, utf8, unescaped)
if utf8:
assert escaped == f"character |{expected}|", "bad UTF-8 escaping"
else:
assert escaped == unescaped, "bad UTF-8 passthrough"
|