This change isn't really a functional change, but it should allow someone to implement a dmesg class for another OS (OSX, BSD, Linux without timestamps, etc) more readily, since it simplifies subclassing.
The immediate reason for doing this however, is that it creates a clean separation between update_dmesg() and update_result(), which makes it easier to test update_result() without needing privileged access (or even a posix machine). Signed-off-by: Dylan Baker <[email protected]> --- framework/dmesg.py | 116 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 30 deletions(-) diff --git a/framework/dmesg.py b/framework/dmesg.py index 6590c1f..1a8faa2 100644 --- a/framework/dmesg.py +++ b/framework/dmesg.py @@ -29,49 +29,69 @@ On OSX and *BSD one would likely want to implement a system that reads the sysloger, since timestamps can be added by the sysloger, and are not inserted by dmesg. +Most users do not want to call a specific dmesg implementation themselves, +they will want to use the get_dmesg() helper, which provides the proper +dmesg implementation for their OS. + """ import re import sys import subprocess import warnings +import abc -__all__ = ['LinuxDmesg', 'DummyDmesg', 'get_dmesg'] +__all__ = [ + 'BaseDmesg', + 'LinuxDmesg', + 'DummyDmesg', + 'get_dmesg', +] -class LinuxDmesg(object): - """ Read dmesg on posix systems +class BaseDmesg(object): + """ Abstract base class for Dmesg derived objects - This reads the dmesg ring buffer, stores the contents of the buffer, and - then compares it whenever called, returning the difference between the - calls. It requires timestamps to be enabled. + This provides the bases of the constructor, and most subclasses should call + super() in __init__(). It provides a concrete implementation of the + update_result() method, which should be suitible for all subclasses. - """ - DMESG_COMMAND = ['dmesg', '--level', 'emerg,alert,crit,err,warn,notice'] + The update_dmesg() method is the primary method that each subclass needs to + override, as this method is used to to actually read the dmesg ringbuffer, + and the command used, and the options given are OS dependent. + + This class is not thread safe, becasue it does not black between the start + of the test and the reading of dmesg, which means that if two tests run at + the same time, and test A creates an entri in dmesg, but test B finishes + first, test B will be marked as having the dmesg error. + """ + @abc.abstractmethod def __init__(self): - """ Create a dmesg instance """ - self._last_message = None + # A list containing all messages since the last time dmesg was read. + # This is used by update result, and then emptied self._new_messages = [] + + # If this is set it should be an re.compile() object, which matches + # entries that are to be considered failures. If an entry does not + # match the regex it will be ignored. By default (None) all entries are + # considered self.regex = None # Populate self.dmesg initially, otherwise the first test will always - # be full of dmesg crud. + # be full of false positives. self.update_dmesg() - if not self._last_message: - # We need to ensure that there is something in dmesg to check - # timestamps. - warnings.warn("Cannot check dmesg for timestamp support. If you " - "do not have timestamps enabled in your kernel you " - "get incomplete dmesg captures", RuntimeWarning) - elif not re.match(r'\[\s*\d+\.\d+\]', self._last_message): - # Do an initial check to ensure that dmesg has timestamps, we need - # timestamps to work correctly. A proper linux dmesg timestamp - # looks like this: [ 0.00000] - raise DmesgError( - "Your kernel does not seem to support timestamps, which " - "are required by the --dmesg option") + @abc.abstractmethod + def update_dmesg(self): + """ Update _new_messages list + + This method should read dmesg, determine which entries are new since + the last read of dmesg, and update self._new_messages with those + entries. + + """ + pass def update_result(self, result): """ Takes a TestResult object and updates it with dmesg statuses @@ -84,7 +104,6 @@ class LinuxDmesg(object): result -- A TestResult instance """ - def replace(res): """ helper to replace statuses with the new dmesg status @@ -92,9 +111,11 @@ class LinuxDmesg(object): otherwise returns the input """ - return {"pass": "dmesg-warn", - "warn": "dmesg-fail", - "fail": "dmesg-fail"}.get(res, res) + return { + "pass": "dmesg-warn", + "warn": "dmesg-fail", + "fail": "dmesg-fail" + }.get(res, res) # Get a new snapshot of dmesg self.update_dmesg() @@ -112,7 +133,7 @@ class LinuxDmesg(object): result['result'] = replace(result['result']) - # Replace any subtests + # Replace the results of any subtests if 'subtest' in result: for key, value in result['subtest'].iteritems(): result['subtest'][key] = replace(value) @@ -122,6 +143,41 @@ class LinuxDmesg(object): return result + +class LinuxDmesg(BaseDmesg): + """ Read dmesg on posix systems + + This reads the dmesg ring buffer, stores the contents of the buffer, and + then compares it whenever called, returning the difference between the + calls. It requires timestamps to be enabled. + + """ + DMESG_COMMAND = ['dmesg', '--level', 'emerg,alert,crit,err,warn,notice'] + + def __init__(self): + """ Create a dmesg instance """ + # _last_message store the contents of the last entry in dmesg the last + # time it was read. Because dmesg is a ringbuffer old entries can fall + # off the end if it becomes too full. To track new entries search for + # this entry and take any entries that were added after it. This works + # on Linux because each entry is guaranteed unique by timestamps. + self._last_message = None + super(LinuxDmesg, self).__init__() + + if not self._last_message: + # We need to ensure that there is something in dmesg to check + # timestamps. + warnings.warn("Cannot check dmesg for timestamp support. If you " + "do not have timestamps enabled in your kernel you " + "get incomplete dmesg captures", RuntimeWarning) + elif not re.match(r'\[\s*\d+\.\d+\]', self._last_message): + # Do an initial check to ensure that dmesg has timestamps, we need + # timestamps to work correctly. A proper linux dmesg timestamp + # looks like this: [ 0.00000] + raise DmesgError( + "Your kernel does not seem to support timestamps, which " + "are required by the --dmesg option") + def update_dmesg(self): """ Call dmesg using subprocess.check_output @@ -145,7 +201,7 @@ class LinuxDmesg(object): self._last_message = dmesg[-1] if dmesg else None -class DummyDmesg(object): +class DummyDmesg(BaseDmesg): """ An dummy class for dmesg on non unix-like systems This implements a dummy version of the LinuxDmesg, and can be used anytime -- 2.0.0 _______________________________________________ Piglit mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/piglit
