summaryrefslogtreecommitdiffstats
path: root/share/extensions/inkex/tester/inx.py
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions/inkex/tester/inx.py')
-rw-r--r--share/extensions/inkex/tester/inx.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/share/extensions/inkex/tester/inx.py b/share/extensions/inkex/tester/inx.py
new file mode 100644
index 0000000..bad6d81
--- /dev/null
+++ b/share/extensions/inkex/tester/inx.py
@@ -0,0 +1,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,
+ }