[Piglit] [PATCH 20/35] profile: Add support for loading xml based profiles

Dylan Baker dylan at pnwbakers.com
Wed Apr 4 22:27:08 UTC 2018


Which includes de-serialization on demand. This will vastly reduce the
amount of resident memory that is necessary to hold the tests since test
instances are made on demand, and can be garbage collected as soon as
the run in complete.
---
 framework/profile.py                | 108 +++++++++++++++++++++++++++++-
 unittests/framework/test_profile.py |   5 +-
 2 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/framework/profile.py b/framework/profile.py
index 925271e..7b1b41b 100644
--- a/framework/profile.py
+++ b/framework/profile.py
@@ -30,15 +30,18 @@ tests, and the Test instance.
 from __future__ import (
     absolute_import, division, print_function, unicode_literals
 )
+import ast
 import collections
 import contextlib
 import copy
 import importlib
+import io
 import itertools
 import multiprocessing
 import multiprocessing.dummy
 import os
 import re
+import xml.etree.cElementTree as et
 
 import six
 
@@ -47,6 +50,12 @@ from framework.dmesg import get_dmesg
 from framework.log import LogManager
 from framework.monitoring import Monitoring
 from framework.test.base import Test, DummyTest
+from framework.test.piglit_test import (
+    PiglitCLTest, PiglitGLTest, ASMParserTest, BuiltInConstantsTest,
+    CLProgramTester, ROOT_DIR,
+)
+from framework.test.shader_test import ShaderTest, MultiShaderTest
+from framework.test.glsl_parser_test import GLSLParserTest
 
 __all__ = [
     'RegexFilter',
@@ -256,6 +265,100 @@ class TestDict(collections.MutableMapping):
         self.__allow_reassignment -= 1
 
 
+def make_test(element):
+    """Rebuild a test instance from xml."""
+    def process(elem, opt):
+        k = elem.attrib['name']
+        v = elem.attrib['value']
+        try:
+            opt[k] = ast.literal_eval(v)
+        except ValueError:
+            opt[k] = v
+
+    type_ = element.attrib['type']
+    options = {}
+    for e in element.findall('./option'):
+        process(e, options)
+
+    if type_ == 'gl':
+        return PiglitGLTest(**options)
+    if type_ == 'gl_builtin':
+        return BuiltInConstantsTest(**options)
+    if type_ == 'cl':
+        return PiglitCLTest(**options)
+    if type_ == 'cl_prog':
+        return CLProgramTester(**options)
+    if type_ == 'shader':
+        return ShaderTest(**options)
+    if type_ == 'glsl_parser':
+        return GLSLParserTest(**options)
+    if type_ == 'asm_parser':
+        return ASMParserTest(**options)
+    if type_ == 'multi_shader':
+        options['skips'] = []
+        for e in element.findall('./Skips/Skip/option'):
+            skips = {}
+            process(e, skips)
+            options['skips'].append(skips)
+        return MultiShaderTest(**options)
+    raise Exception('Unreachable')
+
+
+class XMLProfile(object):
+
+    def __init__(self, filename):
+        self.filename = filename
+        self.forced_test_list = []
+        self.filters = []
+        self.options = {
+            'dmesg': get_dmesg(False),
+            'monitor': Monitoring(False),
+            'ignore_missing': False,
+        }
+
+    def __len__(self):
+        if not (self.filters or self.forced_test_list):
+            with io.open(self.filename, 'rt') as f:
+                iter_ = et.iterparse(f, events=(b'start', ))
+                for _, elem in iter_:
+                    if elem.tag == 'PiglitTestList':
+                        return int(elem.attrib['count'])
+        return sum(1 for _ in self.itertests())
+
+    def setup(self):
+        pass
+
+    def teardown(self):
+        pass
+
+    def _itertests(self):
+        """Always iterates tests instead of using the forced test_list."""
+        with io.open(self.filename, 'rt') as f:
+            doc = et.iterparse(f, events=(b'end', ))
+            _, root = next(doc)  # get the root so we can keep clearing it
+            for _, e in doc:
+                if e.tag != 'Test':
+                    continue
+                k = e.attrib['name']
+                v = make_test(e)
+                if all(f(k, v) for f in self.filters):
+                    yield k, v
+                root.clear()
+
+    def itertests(self):
+        if self.forced_test_list:
+            alltests = dict(self._itertests())
+            opts = collections.OrderedDict()
+            for n in self.forced_test_list:
+                if self.options['ignore_missing'] and n not in alltests:
+                    opts[n] = DummyTest(n, status.NOTRUN)
+                else:
+                    opts[n] = alltests[n]
+            return six.iteritems(opts)
+        else:
+            return iter(self._itertests())
+
+
 class TestProfile(object):
     """Class that holds a list of tests for execution.
 
@@ -347,6 +450,11 @@ def load_test_profile(filename):
     Arguments:
     filename -- the name of a python module to get a 'profile' from
     """
+    name = os.path.splitext(os.path.basename(filename))[0]
+    xml = os.path.join(ROOT_DIR, 'tests', name + '.xml')
+    if os.path.exists(xml):
+        return XMLProfile(xml)
+
     try:
         mod = importlib.import_module('tests.{0}'.format(
             os.path.splitext(os.path.basename(filename))[0]))
diff --git a/unittests/framework/test_profile.py b/unittests/framework/test_profile.py
index d86db1b..714f7e0 100644
--- a/unittests/framework/test_profile.py
+++ b/unittests/framework/test_profile.py
@@ -61,11 +61,6 @@ class TestLoadTestProfile(object):
         with pytest.raises(exceptions.PiglitFatalError):
             profile.load_test_profile('this_module_will_never_ever_exist')
 
-    def test_return_type(self):
-        """profile.load_test_profile: returns a TestProfile instance."""
-        assert isinstance(profile.load_test_profile('sanity'),
-                          profile.TestProfile)
-
 
 class TestTestProfile(object):
     """Tests for profile.TestProfile."""
-- 
git-series 0.9.1


More information about the Piglit mailing list