[Piglit] [PATCH 03/26] framework/results.py: add TestrunResult.from_dict method

Dylan Baker baker.dylan.c at gmail.com
Fri Sep 11 15:55:29 PDT 2015


This allows us to easily restore from json serialization, it also
removes the type hint that we've added in the to_json method.

Signed-off-by: Dylan Baker <dylanx.c.baker at intel.com>
---
 framework/backends/json.py       | 28 +++++++++++------
 framework/results.py             | 32 +++++++++++++++++++
 framework/tests/results_tests.py | 67 ++++++++++++++++++++++++++++++++++++++++
 framework/tests/utils.py         |  1 +
 4 files changed, 118 insertions(+), 10 deletions(-)

diff --git a/framework/backends/json.py b/framework/backends/json.py
index fc816f7..b5a38d0 100644
--- a/framework/backends/json.py
+++ b/framework/backends/json.py
@@ -47,6 +47,13 @@ CURRENT_JSON_VERSION = 6
 # The level to indent a final file
 INDENT = 4
 
+_DECODER_TABLE = {
+    'Subtests': results.Subtests,
+    'TestResult': results.TestResult,
+    'TestrunResult': results.TestrunResult,
+    'Totals': results.Totals,
+}
+
 
 def piglit_encoder(obj):
     """ Encoder for piglit that can transform additional classes into json
@@ -66,10 +73,10 @@ def piglit_encoder(obj):
 def piglit_decoder(obj):
     """Json decoder for piglit that can load TestResult objects."""
     if isinstance(obj, dict):
-        if obj.get('__type__', '') == 'TestResult':
-            return results.TestResult.from_dict(obj)
-        elif obj.get('__type__', '') == 'Subtests':
-            return results.Subtests.from_dict(obj)
+        try:
+            return _DECODER_TABLE[obj['__type__']].from_dict(obj)
+        except KeyError:
+            pass
     return obj
 
 
@@ -141,11 +148,13 @@ class JSONBackend(FileBackend):
                 # writing worked and is valid or it didn't work.
                 try:
                     with open(test, 'r') as f:
-                        data['tests'].update(json.load(f))
+                        data['tests'].update(json.load(f, object_hook=piglit_decoder))
                 except ValueError:
                     pass
         assert data['tests']
 
+        data = results.TestrunResult.from_dict(data)
+
         # write out the combined file. Use the compression writer from the
         # FileBackend
         with self._write_final(os.path.join(self._dest, 'results.json')) as f:
@@ -221,18 +230,17 @@ def _load(results_file):
     This function converts an existing, fully completed json run.
 
     """
-    result = results.TestrunResult()
-    result.results_version = 0  # This should get overwritten
     try:
-        result.__dict__.update(
-            json.load(results_file, object_hook=piglit_decoder))
+        result = json.load(results_file, object_hook=piglit_decoder)
     except ValueError as e:
         raise exceptions.PiglitFatalError(
             'While loading json results file: "{}",\n'
             'the following error occured:\n{}'.format(results_file.name,
                                                       str(e)))
 
-    return result
+    if isinstance(result, results.TestrunResult):
+        return result
+    return results.TestrunResult.from_dict(result, _no_totals=True)
 
 
 def _resume(results_dir):
diff --git a/framework/results.py b/framework/results.py
index dd7fdc0..61841b7 100644
--- a/framework/results.py
+++ b/framework/results.py
@@ -204,6 +204,11 @@ class Totals(dict):
         result['__type__'] = 'Totals'
         return result
 
+    @classmethod
+    def from_dict(cls, dict_):
+        """Convert a dictionary into a Totals object."""
+        return cls(dict_)
+
 
 class TestrunResult(object):
     """The result of a single piglit run."""
@@ -246,3 +251,30 @@ class TestrunResult(object):
         rep = copy.copy(self.__dict__)
         rep['__type__'] = 'TestrunResult'
         return rep
+
+    @classmethod
+    def from_dict(cls, dict_, _no_totals=False):
+        """Convert a dictionary into a TestrunResult.
+
+        This method is meant to be used for loading results from json or
+        similar formats
+
+        _no_totals is not meant to be used externally, it allows us to control
+        the generation of totals when loading old results formats.
+
+        """
+        res = cls()
+        for name in ['name', 'uname', 'options', 'glxinfo', 'wglinfo', 'lspci',
+                     'tests', 'totals', 'results_version']:
+            value = dict_.get(name)
+            if value:
+                setattr(res, name, value)
+
+        # This could be replaced with a getter/setter property
+        time = dict_.get('time_elapsed')
+        res.time_elapsed = None if time is None else float(time)
+
+        if not res.totals and not _no_totals:
+            res.calculate_group_totals()
+
+        return res
diff --git a/framework/tests/results_tests.py b/framework/tests/results_tests.py
index 9a4b358..2f09127 100644
--- a/framework/tests/results_tests.py
+++ b/framework/tests/results_tests.py
@@ -498,3 +498,70 @@ class TestTestrunResultToJson(object):
     def test_type(self):
         """results.TestrunResult.to_json: __type__ is added"""
         nt.eq_(self.test['__type__'], 'TestrunResult')
+
+
+class TestTestrunResultFromDict(object):
+    """Tests for TestrunResult.from_dict."""
+    @classmethod
+    def setup_class(cls):
+        subtest = results.TestResult('fail')
+        subtest.subtests['foo'] = 'pass'
+
+        test = results.TestrunResult()
+        test.name = 'name'
+        test.uname = 'this is uname'
+        test.options = {'some': 'option'}
+        test.glxinfo = 'glxinfo'
+        test.wglinfo = 'wglinfo'
+        test.lspci = 'this is lspci'
+        test.time_elapsed = 1.23
+        test.tests = {
+            'a test': results.TestResult('pass'),
+            'subtest': subtest,
+        }
+        test.results_version = 100000  # This will never be less than current
+
+        cls.baseline = test
+        cls.test = results.TestrunResult.from_dict(test.to_json())
+
+    @utils.nose_generator
+    def test_basic(self):
+        """Generate tests for basic attributes"""
+
+        def test(baseline, test):
+            nt.eq_(baseline, test)
+
+        for attrib in ['name', 'uname', 'glxinfo', 'wglinfo', 'lspci',
+                       'time_elapsed', 'results_version']:
+            test.description = ('results.TestrunResult.from_dict: '
+                                '{} is restored correctly'.format(attrib))
+            yield (test,
+                   getattr(self.baseline, attrib),
+                   getattr(self.test, attrib))
+
+    def test_tests(self):
+        """results.TestrunResult.from_dict: tests is restored correctly"""
+        nt.eq_(self.test.tests['a test'].result,
+               self.baseline.tests['a test'].result)
+
+    def test_test_type(self):
+        """results.TestrunResult.from_dict: tests is restored correctly"""
+        nt.ok_(isinstance(self.test.tests['a test'].result, status.Status),
+               msg='Tests should be type Status, but was "{}"'.format(
+                   type(self.test.tests['a test'].result)))
+
+    def test_totals(self):
+        """results.TestrunResult.from_dict: totals is restored correctly"""
+        nt.assert_dict_equal(self.baseline.totals, self.test.totals)
+
+    def test_subtests(self):
+        """results.TestrunResult.from_dict: subtests are restored correctly"""
+        nt.eq_(self.test.tests['subtest'].subtests['foo'],
+               self.baseline.tests['subtest'].subtests['foo'])
+
+    def test_subtest_type(self):
+        """results.TestrunResult.from_dict: subtests are Status instances"""
+        nt.ok_(isinstance(self.test.tests['subtest'].subtests['foo'],
+                          status.Status),
+               msg='Subtests should be type Status, but was "{}"'.format(
+                   type(self.test.tests['subtest'].subtests['foo'])))
diff --git a/framework/tests/utils.py b/framework/tests/utils.py
index 0077716..6ad2ff0 100644
--- a/framework/tests/utils.py
+++ b/framework/tests/utils.py
@@ -234,6 +234,7 @@ def resultfile():
     data = copy.deepcopy(JSON_DATA)
     data['tests']['sometest'] = results.TestResult('pass')
     data['tests']['sometest'].time = 1.2
+    data = results.TestrunResult.from_dict(data)
     with tempfile_.NamedTemporaryFile(delete=False) as f:
         json.dump(data, f, default=backends.json.piglit_encoder)
 
-- 
2.5.1



More information about the Piglit mailing list