diff --git a/lwr/tools/validator.py b/lwr/tools/validator.py new file mode 100644 index 0000000000000000000000000000000000000000..0c949cebcb8cacf61591995fd0a463a42481e88d --- /dev/null +++ b/lwr/tools/validator.py @@ -0,0 +1,59 @@ +from xml.etree.ElementTree import fromstring +from re import escape, compile + +from os.path import join + + +class ExpressionValidator(object): + + def __init__(self, xml_el): + if type(xml_el) == str: + xml_el = fromstring(xml_el) + self.xml_el = xml_el + + def validate(self, job_directory, string): + regex = "^%s$" % self._expression_to_regex(job_directory, self.xml_el) + return compile(regex).match(string) is not None + + def _expression_to_regex(self, job_directory, element): + return r"\s+".join([self._element_to_regex(child, job_directory) for child in list(element)]) + + def _element_to_regex(self, element, job_directory): + tag = element.tag + method_name = "_%s_to_regex" % tag + try: + method = getattr(self, method_name) + except NameError: + raise NameError("Unknown XML validation tag [%s]" % tag) + return method(element, job_directory) + + def _literal_to_regex(self, element, job_directory): + return escape(self.__value_or_text(element)) + + def _parameter_to_regex(self, element, job_directory): + parameter_name = element.get('name') + value_regex = self._expression_to_regex(job_directory, element) + return r"%s(?:=|\s+)%s" % (parameter_name, value_regex) + + def _integer_to_regex(self, element, job_directory): + return r"\d+" + + def _float_to_regex(self, element, job_directory): + # http://stackoverflow.com/a/9392612 + return r"[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?" + + def _tool_wrapper_to_regex(self, element, job_directory): + wrapper_name = self.__value_or_text(element, "name") + path = join(job_directory.path, "tool_files", wrapper_name) + return escape(path) + + def _configfile_to_regex(self, element, job_directory): + wrapper_name = self.__value_or_text(element, "name") + path = join(job_directory.path, "configs", wrapper_name) + return escape(path) + + def __value_or_text(self, element, attribute="value"): + value = element.get(attribute, None) + if value is None: + value = element.text + return value diff --git a/test/test_utils.py b/test/test_utils.py index ebfeba7ff2d5f899054ed9e1138015dfe0ec9952..51909ea1eb8bd1e197aef26ad928073477c952a1 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -3,6 +3,19 @@ from lwr.tools import ToolBox from os import pardir from os.path import join, dirname +from unittest import TestCase +from tempfile import mkdtemp +from shutil import rmtree + + +class TempDirectoryTestCase(TestCase): + + def setUp(self): + self.temp_directory = mkdtemp() + + def tearDown(self): + rmtree(self.temp_directory) + def get_test_toolbox(): toolbox_path = join(dirname(__file__), pardir, "test_data", "test_shed_toolbox.xml") diff --git a/test/validator_test.py b/test/validator_test.py new file mode 100644 index 0000000000000000000000000000000000000000..422bb850af50cc6465b4637624a7d72e9796e74e --- /dev/null +++ b/test/validator_test.py @@ -0,0 +1,102 @@ +from test_utils import TempDirectoryTestCase + +from lwr.util import JobDirectory +from lwr.tools.validator import ExpressionValidator + +from os.path import join + + +class ValidatorTest(TempDirectoryTestCase): + + def test_literal(self): + xml = """ + <expression> + <literal value="tophat2" /> + </expression>""" + self.__assertValid(xml, "tophat2") + self.__assertInvalid(xml, "bowtie") + + def test_two_literals(self): + xml = """ + <expression> + <literal value="python" /> + <literal value="setup.py" /> + </expression>""" + self.__assertValid(xml, "python setup.py") + self.__assertInvalid(xml, "pythonsetup.py") + + def test_parameter(self): + xml = """ + <expression> + <literal value="tophat2" /> + <parameter name="--mate-std-dev"> + <literal value="4" /> + </parameter> + </expression>""" + self.__assertValid(xml, "tophat2 --mate-std-dev 4") + self.__assertValid(xml, "tophat2 --mate-std-dev=4") + self.__assertInvalid(xml, "tophat2 --mate-std-dev=5") + + def test_integer(self): + xml = """ + <expression> + <literal value="tophat2" /> + <parameter name="--mate-std-dev"> + <integer /> + </parameter> + </expression>""" + self.__assertValid(xml, "tophat2 --mate-std-dev 4") + self.__assertValid(xml, "tophat2 --mate-std-dev=4") + self.__assertInvalid(xml, "tophat2 --mate-std-dev=5.0") + + def test_float(self): + xml = """ + <expression> + <literal value="tophat2" /> + <parameter name="--mate-std-dev"> + <float /> + </parameter> + </expression>""" + self.__assertValid(xml, "tophat2 --mate-std-dev 4") + self.__assertValid(xml, "tophat2 --mate-std-dev=4") + self.__assertValid(xml, "tophat2 --mate-std-dev=5.0") + self.__assertValid(xml, "tophat2 --mate-std-dev=4e10") + self.__assertValid(xml, "tophat2 --mate-std-dev=-1.0e10") + self.__assertValid(xml, "tophat2 --mate-std-dev=-.0e10") + self.__assertInvalid(xml, "tophat2 --mate-std-dev=cat") + + def test_tool_wrapper(self): + xml = """ + <expression> + <tool_wrapper name="tool1_wrapper.py" /> + </expression> + """ + self.__assertValid(xml, "%s" % join(self.temp_directory, '1', 'tool_files', 'tool1_wrapper.py')) + self.__assertInvalid(xml, "tool1_wrapper.py") + + def test_config_file(self): + xml = """ + <expression> + <literal value="tophat2" /> + <configfile name="top_opts" /> + </expression> + """ + self.__assertValid(xml, "tophat2 %s" % join(self.temp_directory, '1', 'configs', 'top_opts')) + self.__assertInvalid(xml, "tophat2 ../%s" % join(self.temp_directory, '1', 'configs', 'top_opts')) + self.__assertInvalid(xml, "tophat2 %s" % join(self.temp_directory, '1', 'configs', 'top_optsX')) + + def __validator(self, xml): + return ExpressionValidator(xml) + + @property + def job_directory(self): + return JobDirectory(self.temp_directory, '1') + + def __is_valid(self, xml, contents): + return self.__validator(xml).validate(self.job_directory, contents) + + def __assertValid(self, xml, contents): + self.assertTrue(self.__is_valid(xml, contents), "%s did not validate against %s" % (contents, xml)) + + def __assertInvalid(self, xml, contents): + self.assertFalse(self.__is_valid(xml, contents), "%s falsely validated against %s" % (contents, xml))