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
|
#!/usr/bin/env python
# coding=utf-8
"""
Test elements extra logic from svg xml lxml custom classes.
"""
from ..utils import PY3
from ..inx import InxFile
INTERNAL_ARGS = ("help", "output", "id", "selected-nodes")
ARG_TYPES = {
"Boolean": "bool",
"Color": "color",
"str": "string",
"int": "int",
"float": "float",
}
class InxMixin:
"""Tools for Testing INX files, use as a mixin class:
class MyTests(InxMixin, TestCase):
def test_inx_file(self):
self.assertInxIsGood("some_inx_file.inx")
"""
def assertInxIsGood(self, inx_file): # pylint: disable=invalid-name
"""Test the inx file for consistancy and correctness"""
self.assertTrue(PY3, "INX files can only be tested in python3")
inx = InxFile(inx_file)
if "help" in inx.ident or inx.script.get("interpreter", None) != "python":
return
cls = inx.extension_class
# Check class can be matched in python file
self.assertTrue(cls, f"Can not find class for {inx.filename}")
# Check name is reasonable for the class
if not cls.multi_inx:
self.assertEqual(
cls.__name__,
inx.slug,
f"Name of extension class {cls.__module__}.{cls.__name__} "
f"is different from ident {inx.slug}",
)
self.assertParams(inx, cls)
def assertParams(self, inx, cls): # pylint: disable=invalid-name
"""Confirm the params in the inx match the python script
.. versionchanged:: 1.2
Also checks that the default values are identical"""
params = {param.name: self.parse_param(param) for param in inx.params}
args = dict(self.introspect_arg_parser(cls().arg_parser))
mismatch_a = list(set(params) ^ set(args) & set(params))
mismatch_b = list(set(args) ^ set(params) & set(args))
self.assertFalse(
mismatch_a, f"{inx.filename}: Inx params missing from arg parser"
)
self.assertFalse(
mismatch_b, f"{inx.filename}: Script args missing from inx xml"
)
for param in args:
if params[param]["type"] and args[param]["type"]:
self.assertEqual(
params[param]["type"],
args[param]["type"],
f"Type is not the same for {inx.filename}:param:{param}",
)
inxdefault = params[param]["default"]
argsdefault = args[param]["default"]
if inxdefault and argsdefault:
# for booleans, the inx is lowercase and the param is uppercase
if params[param]["type"] == "bool":
argsdefault = str(argsdefault).lower()
elif params[param]["type"] not in ["string", None, "color"] or args[
param
]["type"] in ["int", "float"]:
# try to parse the inx value to compare numbers to numbers
inxdefault = float(inxdefault)
if args[param]["type"] == "color" or callable(args[param]["default"]):
# skip color, method types
continue
self.assertEqual(
argsdefault,
inxdefault,
f"Default value is not the same for {inx.filename}:param:{param}",
)
def introspect_arg_parser(self, arg_parser):
"""Pull apart the arg parser to find out what we have in it"""
for (
action
) in arg_parser._optionals._actions: # pylint: disable=protected-access
for opt in action.option_strings:
# Ignore params internal to inkscape (thus not in the inx)
if opt.startswith("--") and opt[2:] not in INTERNAL_ARGS:
yield (opt[2:], self.introspect_action(action))
@staticmethod
def introspect_action(action):
"""Pull apart a single action to get at the juicy insides"""
return {
"type": ARG_TYPES.get((action.type or str).__name__, "string"),
"default": action.default,
"choices": action.choices,
"help": action.help,
}
@staticmethod
def parse_param(param):
"""Pull apart the param element in the inx file"""
if param.param_type in ("optiongroup", "notebook"):
options = param.options
return {
"type": None,
"choices": options,
"default": options and options[0] or None,
}
param_type = param.param_type
if param.param_type in ("path",):
param_type = "string"
return {
"type": param_type,
"default": param.text,
"choices": None,
}
|