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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+