[Piglit] [PATCH 01/26] framework/results.py: Add a method to TestrunResult to calculate totals

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


Currently this is done in the summary code, and is very complicated.
This change has a couple of advantages. First, having it in the
TestrunResult object seems a more obvious place for it. Second, this
will allows us to store the data in the json, removing the need to
calculate the data again and again.

Signed-off-by: Dylan Baker <dylanx.c.baker at intel.com>
---
 framework/results.py             |  45 ++++++++++++++++-
 framework/tests/results_tests.py | 101 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 144 insertions(+), 2 deletions(-)

diff --git a/framework/results.py b/framework/results.py
index 2753fd5..7bfc2fd 100644
--- a/framework/results.py
+++ b/framework/results.py
@@ -23,7 +23,9 @@
 
 from __future__ import print_function, absolute_import
 
-from framework import status, exceptions
+import collections
+
+from framework import status, exceptions, grouptools
 
 __all__ = [
     'TestrunResult',
@@ -180,7 +182,24 @@ class TestResult(object):
             self.subtests.update(dict_['subtest'])
 
 
+class Totals(dict):
+    def __init__(self, *args, **kwargs):
+        super(Totals, self).__init__(*args, **kwargs)
+        for each in status.ALL:
+            self[str(each)] = 0
+
+    def __nonzero__(self):
+        # Since totals are prepopulated, calling 'if not <Totals instance>'
+        # will always result in True, this will cause it to return True only if
+        # one of the values is not zero
+        for each in self.itervalues():
+            if each != 0:
+                return True
+        return False
+
+
 class TestrunResult(object):
+    """The result of a single piglit run."""
     def __init__(self):
         self.name = None
         self.uname = None
@@ -190,3 +209,27 @@ class TestrunResult(object):
         self.lspci = None
         self.time_elapsed = None
         self.tests = {}
+        self.totals = collections.defaultdict(Totals)
+
+    def calculate_group_totals(self):
+        """Calculate the number of pases, fails, etc at each level."""
+        for name, result in self.tests.iteritems():
+            # If there are subtests treat the test as if it is a group instead
+            # of a test.
+            if result.subtests:
+                for res in result.subtests.itervalues():
+                    res = str(res)
+                    temp = name
+
+                    self.totals[temp][res] += 1
+                    while temp:
+                        temp = grouptools.groupname(temp)
+                        self.totals[temp][res] += 1
+                    self.totals['root'][res] += 1
+            else:
+                res = str(result.result)
+                while name:
+                    name = grouptools.groupname(name)
+                    self.totals[name][res] += 1
+                self.totals['root'][res] += 1
+
diff --git a/framework/tests/results_tests.py b/framework/tests/results_tests.py
index 143ab09..d2c4206 100644
--- a/framework/tests/results_tests.py
+++ b/framework/tests/results_tests.py
@@ -25,7 +25,7 @@ from __future__ import print_function, absolute_import
 
 import nose.tools as nt
 
-from framework import results, status, exceptions
+from framework import results, status, exceptions, grouptools
 import framework.tests.utils as utils
 
 
@@ -346,3 +346,102 @@ class TestStringDescriptor(object):
     def test_delete(self):
         """results.StringDescriptor.__delete__: raises NotImplementedError"""
         del self.test.val
+
+
+class TestTestrunResultTotals(object):
+    """Test the totals generated by TestrunResult.calculate_group_totals()."""
+    @classmethod
+    def setup_class(cls):
+        pass_ = results.TestResult('pass')
+        fail = results.TestResult('fail')
+        #warn = results.TestResult('warn')
+        crash = results.TestResult('crash')
+        skip = results.TestResult('skip')
+        tr = results.TestrunResult()
+        tr.tests = {
+            'oink': pass_,
+            grouptools.join('foo', 'bar'): fail,
+            grouptools.join('foo', 'foo', 'bar'): crash,
+            grouptools.join('foo', 'foo', 'oink'): skip,
+        }
+
+        tr.calculate_group_totals()
+        cls.test = tr.totals
+
+    def test_root(self):
+        """results.TestrunResult.totals: The root is correct"""
+        root = results.Totals()
+        root['pass'] += 1
+        root['fail'] += 1
+        root['crash'] += 1
+        root['skip'] += 1
+
+        nt.assert_dict_equal(self.test['root'], root)
+
+    def test_recurse(self):
+        """results.TestrunResult.totals: Recurses correctly"""
+        expected = results.Totals()
+        expected['fail'] += 1
+        expected['crash'] += 1
+        expected['skip'] += 1
+        nt.assert_dict_equal(self.test['foo'], expected)
+
+    def test_two_parents(self):
+        """results.TestrunResult.totals: Handles multiple parents correctly"""
+        expected = results.Totals()
+        expected['crash'] += 1
+        expected['skip'] += 1
+        nt.assert_dict_equal(self.test[grouptools.join('foo', 'foo')], expected)
+
+
+class TestTestrunResultTotalsSubtests(object):
+    """results.TestrunResult.totals: Tests with subtests are handled correctly"""
+    @classmethod
+    def setup_class(cls):
+        tr = results.TestResult('incomplete')
+        tr.subtests['foo'] = status.PASS
+        tr.subtests['bar'] = status.CRASH
+        tr.subtests['oink'] = status.FAIL
+
+        run = results.TestrunResult()
+        run.tests[grouptools.join('sub', 'test')] = tr
+        run.calculate_group_totals()
+
+        cls.test = run.totals
+
+    def test_root(self):
+        """results.TestrunResult.totals: The root is correct with subtests"""
+        expect = results.Totals()
+        expect['pass'] += 1
+        expect['crash'] += 1
+        expect['fail'] += 1
+        nt.assert_dict_equal(self.test['root'], expect)
+
+    def test_node(self):
+        """results.TestrunResult.totals: Tests with subtests are treated as groups"""
+        key = grouptools.join('sub', 'test')
+        nt.ok_(key in self.test,
+               msg='Key: {} not found in {}'.format(key, self.test.keys()))
+
+    def test_node_values(self):
+        """results.TestrunResult.totals: Tests with subtests values are correct"""
+        expect = results.Totals()
+        expect['pass'] += 1
+        expect['crash'] += 1
+        expect['fail'] += 1
+        nt.assert_dict_equal(self.test[grouptools.join('sub', 'test')], expect)
+
+
+def test_totals_false():
+    """results.Totals: bool() returns False when all values are 0"""
+    nt.ok_(not bool(results.Totals()))
+
+
+def test_totals_true():
+    """results.Totals: bool() returns True when any value is not 0"""
+    # This might deserve a generator, but it seems so simple that it it's a lot
+    # of work for no gain
+    for key in results.Totals().iterkeys():
+        test = results.Totals()
+        test[key] += 1
+        nt.ok_(bool(test), msg='Returns false with status {}'.format(key))
-- 
2.5.1



More information about the Piglit mailing list