summaryrefslogtreecommitdiff
path: root/framework/results.py
diff options
context:
space:
mode:
authorDylan Baker <baker.dylan.c@gmail.com>2015-08-13 14:25:37 -0700
committerDylan Baker <baker.dylan.c@gmail.com>2015-09-22 14:45:48 -0700
commit005502819fcb411c9f84b73408c7bc65488f7b03 (patch)
tree2e982fc934b7e589002009cc349d2b62748f4bd5 /framework/results.py
parent269bf51c9f08ad90bef38bf6c09b67d095f35d4f (diff)
framework: move unicode conversion handling to TestResult
This implements a python descriptor pattern to handle setting the out and err attributes, which should always be unicode. The familiar "property" decorator is an example of a descriptor, although it is a more terse way of doing it. And while that could have been done here, by not making it a property we can reuse the exact same class for both out and err, and could theoretically use it for other attributes. Signed-off-by: Dylan Baker <dylanx.c.baker@intel.com>
Diffstat (limited to 'framework/results.py')
-rw-r--r--framework/results.py49
1 files changed, 44 insertions, 5 deletions
diff --git a/framework/results.py b/framework/results.py
index 40f750cf0..2753fd580 100644
--- a/framework/results.py
+++ b/framework/results.py
@@ -50,15 +50,43 @@ class Subtests(dict):
return res
+class StringDescriptor(object): # pylint: disable=too-few-public-methods
+ """A Shared data descriptor class for TestResult.
+
+ This provides a property that can be passed a str or unicode, but always
+ returns a unicode object.
+
+ """
+ def __init__(self, name, default=unicode()):
+ assert isinstance(default, unicode)
+ self.__name = name
+ self.__default = default
+
+ def __get__(self, instance, cls):
+ return getattr(instance, self.__name, self.__default)
+
+ def __set__(self, instance, value):
+ if isinstance(value, str):
+ setattr(instance, self.__name, value.decode('utf-8', 'replace'))
+ elif isinstance(value, unicode):
+ setattr(instance, self.__name, value)
+ else:
+ raise TypeError('{} attribute must be a str or unicode instance, '
+ 'but was {}.'.format(self.__name, type(value)))
+
+ def __delete__(self, instance):
+ raise NotImplementedError
+
+
class TestResult(object):
"""An object represting the result of a single test."""
- __slots__ = ['returncode', 'err', 'out', 'time', 'command', 'environment',
- 'subtests', 'dmesg', '__result', 'images', 'traceback']
+ __slots__ = ['returncode', '_err', '_out', 'time', 'command', 'traceback',
+ 'environment', 'subtests', 'dmesg', '__result', 'images']
+ err = StringDescriptor('_err')
+ out = StringDescriptor('_out')
def __init__(self, result=None):
self.returncode = None
- self.err = str()
- self.out = str()
self.time = float()
self.command = str()
self.environment = str()
@@ -113,10 +141,14 @@ class TestResult(object):
status.Status object
"""
+ # pylint will say that assining to inst.out or inst.err is a non-slot
+ # because self.err and self.out are descriptors, methods that act like
+ # variables. Just silence pylint
+ # pylint: disable=assigning-non-slot
inst = cls()
# TODO: There's probably a more clever way to do this
- for each in ['returncode', 'err', 'out', 'time', 'command',
+ for each in ['returncode', 'time', 'command',
'environment', 'result', 'dmesg']:
if each in dict_:
setattr(inst, each, dict_[each])
@@ -125,6 +157,13 @@ class TestResult(object):
for name, value in dict_['subtests'].iteritems():
inst.subtests[name] = value
+ # out and err must be set manually to avoid replacing the setter
+ if 'out' in dict_:
+ inst.out = dict_['out']
+
+ if 'err' in dict_:
+ inst.err = dict_['err']
+
return inst
def update(self, dict_):