[Piglit] [PATCH 18/20] dmesg.py: Use Abstract Base Class

Dylan Baker baker.dylan.c at gmail.com
Sat Jun 14 08:05:27 PDT 2014


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 <baker.dylan.c at gmail.com>
---
 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



More information about the Piglit mailing list