diff --git a/qubes/tests/__init__.py b/qubes/tests/__init__.py index 00721fcc..d1b63f20 100644 --- a/qubes/tests/__init__.py +++ b/qubes/tests/__init__.py @@ -3,6 +3,8 @@ import collections import unittest +import lxml.etree + import qubes.config import qubes.events @@ -132,3 +134,44 @@ class QubesTestCase(unittest.TestCase): self.fail('event {!r} did fire on {!r}'.format(event, emitter)) return + + + def assertXMLIsValid(self, xml, file=None, schema=None): + '''Check whether given XML fulfills Relax NG schema. + + Schema can be given in a couple of ways: + + - As separate file. This is most common, and also the only way to + handle file inclusion. Call with file name as second argument. + + - As string containing actual schema. Put that string in *schema* + keyword argument. + + :param lxml.etree._Element xml: XML element instance to check + :param str file: filename of Relax NG schema + :param str schema: optional explicit schema string + ''' + + if schema is not None and file is None: + relaxng = schema + if isinstance(relaxng, str): + relaxng = lxml.etree.XML(relaxng) + if isinstance(relaxng, lxml.etree._Element): + relaxng = lxml.etree.RelaxNG(relaxng) + + elif file is not None and schema is None: + relaxng = lxml.etree.RelaxNG(file=file) + + else: + raise TypeError("There should be excactly one of 'file' and " + "'schema' arguments specified.") + + # We have to be extra careful here in case someone messed up with + # self.failureException. It should by default be AssertionError, just + # what is spewed by RelaxNG(), but who knows what might happen. + try: + relaxng.assert_(xml) + except self.failureException: + raise + except AssertionError as e: + self.fail(str(e)) diff --git a/qubes/tests/vm/init.py b/qubes/tests/vm/init.py index 1608f189..06b28374 100644 --- a/qubes/tests/vm/init.py +++ b/qubes/tests/vm/init.py @@ -131,7 +131,7 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase): 'disabledservice': False, }) - lxml.etree.ElementTree(vm.__xml__()).write(sys.stderr, encoding='utf-8', pretty_print=True) + self.assertXMLIsValid(vm.__xml__(), '../../relaxng/domain.rng') def test_001_BaseVM_nxproperty(self): xml = lxml.etree.XML(''' diff --git a/relaxng/domain.rng b/relaxng/domain.rng new file mode 100644 index 00000000..3305db34 --- /dev/null +++ b/relaxng/domain.rng @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/relaxng/qubes.rng b/relaxng/qubes.rng new file mode 100644 index 00000000..82a38a1c --- /dev/null +++ b/relaxng/qubes.rng @@ -0,0 +1,308 @@ + + + + + + + + + + + + This is root element of whole qubes tree. + + + + + Specifies minimal Qubes OS version. + + + 3.0 + + + + + + + + Container for labels. + + + + + + Label which can be used by domain. One choice of + colour for padlock icon. + + + + + XML id attribute used for cross-referencing in + properties' ``ref`` attribute. + + + + + label-[0-9]+ + + + + + + + Label's colour, HTML-like. + + + + #[0-9a-f]{6} + + + + + + [a-z0-9_-]+ + + + + + + + + Container for domains. + + + + + + + + + + + + + One Qubes domain. + + + + + Type of the domain. This specifies Python's class that is + used for instantiation of this VM. + + + + + + + XML id attribute used for cross-referencing in properties' + ``ref`` attribute. + + + + + domain-[0-9]+ + + + + + + + + + Container for services. + + + + + + One service that is either enabled or disabled. + + + + + + Whether service is enabled or disabled. + Default is ``true``. + + + true|false + + + + + + + + [a-z0-9_-]+ + + + + + + + + + + Container for devices of particular class. + + + + + Class of devices in this container. Currently the + only supported is ``pci``. + + + + + pci + + + + + + One device. This tag should contain some + identifier, format of which depends on + particular device class. + + + + [0-9a-f]{2}:[0-9a-f]{2}.[0-9a-f]{2} + + + + + + + + + + Container for user-defined tags. + + + + + + Tag value. + + Tags are not used anywhere by qubes core, they + are for users reference. In the future they + will be available for use in policies. + + + + + Name of the tag. + + + + [a-z0-9_-]+ + + + + + + + + + + + + + + + + + Container for properties. + + + + + + One property and its value specified either directly + (as text contained in this tag) or as reference to + another XML element in the tree (by ``ref=`` + attribute). How it is saved, it depends on particular + property. + + + + + Property name. + + + + [a-z0-9_]+ + + + + + + + Alternative property value, by reference to another XML element. + + + + + + + + + + + + + + +