[Piglit] [PATCH v2] Add aborting option when a monitored error is detected
Dylan Baker
baker.dylan.c at gmail.com
Wed Apr 6 17:55:37 UTC 2016
Quoting Olivier Berthier (2016-04-01 09:39:06)
> This adds a policy which advises when the user should reboot the system
> to avoid noisy test results due to system becoming unstable, for
> instance, and therefore continues testing successfully.
>
> To do this, a new module is proposed. A class Monitoring is used for
> managing the monitoring rules. Two types of rules, MonitoringFile and
> MonitoringLinuxDmesg, derived from the abstract class MonitoringBase,
> have been implemented. The first allow to track a pattern on standard
> files or locked files. The second, derived from dmesg.LinuxDmesg, will
> track a pattern on the dmesg.
>
> The monitoring rules must be defined in piglit.conf at the section
> monitored-errors. If one of the regex is found, Piglit will raise a
> PiglitAbort exception, stop the test execution -terminating test thread
> pool- and exit with code 3.
>
> Then test execution resume, after rebooting the system or not, is done
> like usually with command line parameter "resume".
>
> To call it, use command line parameter: --abort-on-monitored-error
>
> This option implies --no-concurrent
>
> This include also a set of unit tests for this module.
> ---
> framework/exceptions.py | 14 ++
> framework/monitoring.py | 311 ++++++++++++++++++++++++++++++++++++++++++
> framework/options.py | 1 +
> framework/profile.py | 30 +++-
> framework/programs/run.py | 18 ++-
> framework/test/base.py | 5 +-
> piglit.conf.example | 29 ++++
> unittests/base_tests.py | 5 +-
> unittests/monitoring_tests.py | 190 ++++++++++++++++++++++++++
> 9 files changed, 596 insertions(+), 7 deletions(-)
> create mode 100644 framework/monitoring.py
> create mode 100644 unittests/monitoring_tests.py
>
> diff --git a/framework/exceptions.py b/framework/exceptions.py
> index 6e87bee..5d75c15 100644
> --- a/framework/exceptions.py
> +++ b/framework/exceptions.py
> @@ -30,6 +30,7 @@ __all__ = [
> 'PiglitInternalError',
> 'PiglitFatalError',
> 'PiglitException',
> + 'PiglitAbort',
> 'handler',
> ]
>
> @@ -51,6 +52,10 @@ def handler(func):
> except PiglitFatalError as e:
> print('Fatal Error: {}'.format(str(e)), file=sys.stderr)
> sys.exit(1)
> + except PiglitAbort as e:
> + print('Aborting Piglit execution: {}'.format(str(e)),
> + file=sys.stderr)
> + sys.exit(3)
>
> return _inner
>
> @@ -87,3 +92,12 @@ class PiglitFatalError(Exception):
> to the top of the program where it exits.
>
> """
> +
> +
> +class PiglitAbort(Exception):
> + """Class for non-errors that require piglit aborting.
> +
> + When this class (or a subclass) is raised it should be raised all the way
> + to the top of the program where it exits.
> +
> + """
> diff --git a/framework/monitoring.py b/framework/monitoring.py
> new file mode 100644
> index 0000000..f178bd6
> --- /dev/null
> +++ b/framework/monitoring.py
> @@ -0,0 +1,311 @@
> +# Copyright (c) 2016 Intel Corporation
> +#
> +# Permission is hereby granted, free of charge, to any person obtaining a
> +# copy of this software and associated documentation files (the "Software"),
> +# to deal in the Software without restriction, including without limitation
> +# the rights to use, copy, modify, merge, publish, distribute, sublicense,
> +# and/or sell copies of the Software, and to permit persons to whom the
> +# Software is furnished to do so, subject to the following conditions:
> +#
> +# The above copyright notice and this permission notice (including the next
> +# paragraph) shall be included in all copies or substantial portions of the
> +# Software.
> +#
> +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> +# IN THE SOFTWARE.
> +
> +"""Module implementing classes for monitoring
> +
> +This module provides a set of classes for monitoring the system on multiple
> +sources. The sources can be standard files, locked files or the dmesg.
> +The patterns of the errors are defined in piglit.conf, at the section
> +monitored-errors.
> +
> +When one of the regex is found in the corresponding source Piglit will abort
> +with code 3.
> +
> +"""
> +
> +from __future__ import (
> + absolute_import, division, print_function, unicode_literals
> +)
> +import abc
> +import errno
> +import fcntl
> +import os
> +import re
> +
> +import six
> +
> +from framework.core import PIGLIT_CONFIG
> +from framework.dmesg import LinuxDmesg
> +from framework import exceptions
> +
> +__all__ = [
> + 'BaseMonitoring',
> + 'Monitoring',
> + 'MonitoringFile',
> + 'MonitoringLinuxDmesg',
> +]
> +
> +
> +
> +class Monitoring(object):
> + """Class that initialize and update monitoring classes
> +
> + This reads the rules from the section monitored-errors in piglit.conf
> + and initializes the monitoring objects. Their type must be derived from
> + BaseMonitoring and specialized according the field 'type'.
> +
> + """
> +
> + # starting time: user must know current machine/GPU state before
> + # starting piglit: we can resume test execution based on last stopping
> + # if we consider that our system is still in good shape (ie not blocking
> + # run forever)
> + _abort_error = None
> + _monitoring_rules = None
> +
> + def __init__(self, monitoring_enabled):
> + """Create a LinuxMonitored instance"""
> + # Get the monitoring rules from piglit.conf and store them into a dict.
> + self._monitoring_rules = {}
> +
> + if monitoring_enabled and PIGLIT_CONFIG.has_section('monitored-errors'):
> + for key, _ in PIGLIT_CONFIG.items('monitored-errors'):
> + if PIGLIT_CONFIG.has_section(key):
> + type = PIGLIT_CONFIG.required_get(key, 'type')
> + regex = PIGLIT_CONFIG.required_get(key, 'regex')
> + parameters = PIGLIT_CONFIG.required_get(key, 'parameters')
> +
> + self.add_rule(key, type, parameters, regex)
> +
> + @property
> + def abort_needed(self):
> + """Simply return if _abort_error variable is not empty"""
> + return self._abort_error is not None
> +
> + @property
> + def error_message(self):
> + """Simply return _abort_error message"""
> + return self._abort_error
> +
> + def add_rule(self, key, type, parameters, regex):
> + """Add a new monitoring rule
> +
> + This method adds a new monitoring rule. The type must be file,
> + locked_file or dmesg.
> +
> + Arguments:
> + key -- The key for storing the rule in a dict
> + type -- The type of monitoring
> + parameters -- Depending on the type, can be a file path or a command
> + options
> + regex -- The rule regex
> +
> + """
> + rule = None
> +
> + if type == 'file':
> + rule = MonitoringFile(parameters,
> + regex)
> + elif type == 'locked_file':
> + rule = MonitoringFile(parameters,
> + regex, True)
> + elif type == 'dmesg':
> + rule = MonitoringLinuxDmesg(parameters,
> + regex)
> + else:
> + raise exceptions.PiglitFatalError(
> + "No available monitoring class for the type {}.".format(type))
> +
> + self._monitoring_rules[key] = rule
> +
> + def delete_rule(self, key):
> + """Remove a monitoring rule
> +
> + Arguments:
> + key -- The rule key
> +
> + """
> + self._monitoring_rules.pop(key, None)
> +
> + def update_monitoring(self):
> + """Update the new messages for each monitoring object"""
> + if self._monitoring_rules:
> + for monitoring_rule in six.itervalues(self._monitoring_rules):
> + monitoring_rule.update_monitoring()
> +
> + def check_monitoring(self):
> + """Check monitoring objects statue
> +
> + This method checks the state for each monitoring object.
> + If one of them found the pattern in the new messages,
> + set itself on abort_needed state.
> +
> + """
> + # Get a new snapshot of the source
> + self.update_monitoring()
> +
> + if self._monitoring_rules:
> + for rule_key, monitoring_rule in six.iteritems(
> + self._monitoring_rules):
> + # Get error message
> + self._abort_error = monitoring_rule.check_monitoring()
> + # if error message is not empty, abort is requested
> + if self.abort_needed:
> + self._abort_error = "From the rule {}:\n{}".format(
> + rule_key,
> + self._abort_error)
> + break
> +
> +
> + at six.add_metaclass(abc.ABCMeta)
> +class BaseMonitoring(object):
> + """Abstract base class for Monitoring derived objects
> +
> + This provides the bases of the constructor and most subclasses should call
> + super() in __init__(). It provides a concrete implementation of the
> + check_monitoring() method, which should be suitible for all subclasses.
> +
> + The update_monitoring() method need to be override for all subclasses.
> +
> + """
> + @abc.abstractmethod
> + def __init__(self, monitoring_source, regex):
> + """Abstract constructor for BaseMonitoring subclasses
> +
> + Arguments;
> + monitoring_source -- The source to monitor
> + regex -- The errors pattern
> +
> + """
> + self._monitoring_source = monitoring_source
> + self._monitoring_regex = re.compile(regex)
> + self._new_messages = ''
> +
> + @property
> + def new_messages(self):
> + """Simply return if _abort_error variable is not empty"""
> + return self._new_messages
> +
> + @abc.abstractmethod
> + def update_monitoring(self):
> + """Update _new_messages list
> +
> + This method should read a monitoring source like a file or a program
> + output, determine new entries since the last read of the source,
> + and update self._new_messages with those entries
> +
> + """
> + pass
> +
> + def check_monitoring(self):
> + """Check _new_messages
> +
> + This method checks if the regex is found in the new messages,
> + then return a string that contain the monitored location and
> + the matched line
> +
> + """
> + if self._new_messages and self._monitoring_regex:
> + for line in self._new_messages:
> + if self._monitoring_regex.search(line):
> + return line
> +
> +
> +class MonitoringFile(BaseMonitoring):
> + """Monitoring from a file
> +
> + This class is for monitoring the system from a file that
> + can be a standard file or a locked file. The locked file
> + algorithm uses fnctl, so it doesn't work on non Unix systems
> +
> + Arguments:
> + is_locked -- True if the target is a locked file
> +
> + """
> + _is_locked = False
> +
> + def __init__(self, monitoring_source, regex, is_locked=False):
> + """Create a MonitoringFile instance"""
> + self._last_message = None
> + self._is_locked = is_locked
> + super(MonitoringFile, self).__init__(monitoring_source, regex)
> +
> + def update_monitoring(self):
> + """Open the file and get the differences
> +
> + Get the contents of the file, then calculate new messages.
> + This implements also a specific method for reading locked files.
> +
> + """
> +
> + try:
> + with open(self._monitoring_source, 'r') as f:
> + lines = []
> + if self._is_locked:
> + # Create a duplicated file descriptor, this avoid lock
> + fd = os.dup(f.fileno())
> + # use I/O control for reading the lines
> + fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
> +
> + while True:
> + try:
> + line = os.read(fd, 1024)
> + if not line:
> + break;
> + except OSError as e:
> + if e.errno == errno.EAGAIN:
> + break
> + else:
> + raise e
> + lines.append(line.decode("utf-8", "strict"))
> + os.close(fd)
> +
> + else:
> + lines = f.read().splitlines()
> +
> + f.close()
> +
> + # Find all new entries, do this by slicing the list of the lines to only
> + # returns elements after the last element stored. If there are not
> + # matches a value error is raised, that means all of the lines are new
> + l = 0
> + for index, item in enumerate(reversed(lines)):
> + if item == self._last_message:
> + l = len(lines) - index # don't include the matched element
> + break
> + self._new_messages = lines[l:]
> + # Attempt to store the last element of lines,
> + # unless there was no line
> + self._last_message = lines[-1] if lines else None
> + except Exception:
> + # if an error occured, we consider there are no new messages
> + self._new_messages = []
> + pass
> +
> +
> +class MonitoringLinuxDmesg(BaseMonitoring, LinuxDmesg):
> + """Monitoring on dmesg
> +
> + This class is for monitoring on the system dmesg. It's inherited
> + from LinuxDmesg for the dmesg processing methods.
> +
> + Work only on Linux operating system.
> +
> + """
> + def __init__(self, monitoring_source, regex):
> + """Create a MonitoringLinuxDmesg instance"""
> + self.DMESG_COMMAND = ['dmesg']+monitoring_source.split()
> + BaseMonitoring.__init__(self, monitoring_source, regex)
> + LinuxDmesg.__init__(self)
> +
> + def update_monitoring(self):
> + """Call update_dmesg"""
> + self.update_dmesg()
> diff --git a/framework/options.py b/framework/options.py
> index cf49520..268cc1f 100644
> --- a/framework/options.py
> +++ b/framework/options.py
> @@ -178,6 +178,7 @@ class _Options(object): # pylint: disable=too-many-instance-attributes
> exclude_filter -- list of compiled regex which exclude tests that match
> valgrind -- True if valgrind is to be used
> dmesg -- True if dmesg checking is desired. This forces concurrency off
> + monitored -- True if monitoring is desired. This forces concurrency off
> env -- environment variables set for each test before run
>
> """
> diff --git a/framework/profile.py b/framework/profile.py
> index fc38c56..01ef6cc 100644
> --- a/framework/profile.py
> +++ b/framework/profile.py
> @@ -41,6 +41,7 @@ import six
> from framework import grouptools, exceptions, options
> from framework.dmesg import get_dmesg
> from framework.log import LogManager
> +from framework.monitoring import Monitoring
> from framework.test.base import Test
>
> __all__ = [
> @@ -162,6 +163,8 @@ class TestProfile(object):
> self._dmesg = None
> self.dmesg = False
> self.results_dir = None
> + self._monitoring = None
> + self.monitoring = False
>
> @property
> def dmesg(self):
> @@ -179,6 +182,22 @@ class TestProfile(object):
> """
> self._dmesg = get_dmesg(not_dummy)
>
> + @property
> + def monitoring(self):
> + """ Return monitoring """
> + return self._monitoring
> +
> + @monitoring.setter
> + def monitoring(self, monitored):
> + """ Set monitoring
> +
> + Arguments:
> + monitored -- if Truthy Monitoring will enable monitoring according the
> + defined rules
> +
> + """
> + self._monitoring = Monitoring(monitored)
> +
> def _prepare_test_list(self):
> """ Prepare tests for running
>
> @@ -257,16 +276,18 @@ class TestProfile(object):
> self._prepare_test_list()
> log = LogManager(logger, len(self.test_list))
>
> - def test(pair):
> + def test(pair, this_pool=None):
> """Function to call test.execute from map"""
> name, test = pair
> with backend.write_test(name) as w:
> - test.execute(name, log.get(), self.dmesg)
> + test.execute(name, log.get(), self.dmesg, self.monitoring)
> w(test.result)
> + if self._monitoring.abort_needed:
> + this_pool.terminate()
>
> def run_threads(pool, testlist):
> """ Open a pool, close it, and join it """
> - pool.imap(test, testlist, chunksize)
> + pool.imap(lambda pair: test(pair, pool), testlist, chunksize)
> pool.close()
> pool.join()
>
> @@ -293,6 +314,9 @@ class TestProfile(object):
>
> self._post_run_hook()
>
> + if self._monitoring.abort_needed:
> + raise exceptions.PiglitAbort(self._monitoring.error_message)
> +
> def filter_tests(self, function):
> """Filter out tests that return false from the supplied function
>
> diff --git a/framework/programs/run.py b/framework/programs/run.py
> index 581b350..69716f6 100644
> --- a/framework/programs/run.py
> +++ b/framework/programs/run.py
> @@ -137,6 +137,13 @@ def _run_parser(input_):
> action="store_true",
> help="Capture a difference in dmesg before and "
> "after each test. Implies -1/--no-concurrency")
> + parser.add_argument("--abort-on-monitored-error",
> + action="store_true",
> + dest="monitored",
> + help="Enable monitoring according the rules defined "
> + "in piglit.conf, and stop the execution when a "
> + "monitored error is detected. Exit code 3. "
> + "Implies -1/--no-concurrency")
> parser.add_argument("-s", "--sync",
> action="store_true",
> help="Sync results to disk after every test")
> @@ -224,7 +231,7 @@ def run(input_):
>
> # If dmesg is requested we must have serial run, this is becasue dmesg
> # isn't reliable with threaded run
> - if args.dmesg:
> + if args.dmesg or args.monitored:
> args.concurrency = "none"
>
> # build up the include filter based on test_list
> @@ -240,6 +247,7 @@ def run(input_):
> options.OPTIONS.execute = args.execute
> options.OPTIONS.valgrind = args.valgrind
> options.OPTIONS.dmesg = args.dmesg
> + options.OPTIONS.monitored = args.monitored
> options.OPTIONS.sync = args.sync
>
> # Set the platform to pass to waffle
> @@ -284,6 +292,10 @@ def run(input_):
> # Set the dmesg type
> if args.dmesg:
> profile.dmesg = args.dmesg
> +
> + if args.monitored:
> + profile.monitoring = args.monitored
> +
> profile.run(args.log_level, backend)
>
> results.time_elapsed.end = time.time()
> @@ -319,6 +331,7 @@ def resume(input_):
> options.OPTIONS.execute = results.options['execute']
> options.OPTIONS.valgrind = results.options['valgrind']
> options.OPTIONS.dmesg = results.options['dmesg']
> + options.OPTIONS.monitored = results.options['monitored']
> options.OPTIONS.sync = results.options['sync']
>
> core.get_config(args.config_file)
> @@ -345,6 +358,9 @@ def resume(input_):
> if options.OPTIONS.dmesg:
> profile.dmesg = options.OPTIONS.dmesg
>
> + if options.OPTIONS.monitored:
> + profile.monitoring = options.OPTIONS.monitored
> +
> # This is resumed, don't bother with time since it wont be accurate anyway
> profile.run(results.options['log_level'], backend)
>
> diff --git a/framework/test/base.py b/framework/test/base.py
> index 0fe0f74..d2a193c 100644
> --- a/framework/test/base.py
> +++ b/framework/test/base.py
> @@ -183,7 +183,7 @@ class Test(object):
> assert isinstance(timeout, int)
> self.timeout = timeout
>
> - def execute(self, path, log, dmesg):
> + def execute(self, path, log, dmesg, monitoring):
> """ Run a test
>
> Run a test, but with features. This times the test, uses dmesg checking
> @@ -193,6 +193,7 @@ class Test(object):
> path -- the name of the test
> log -- a log.Log instance
> dmesg -- a dmesg.BaseDmesg derived class
> + monitoring -- a monitoring.Monitoring instance
>
> """
> log.start(path)
> @@ -201,9 +202,11 @@ class Test(object):
> try:
> self.result.time.start = time.time()
> dmesg.update_dmesg()
> + monitoring.update_monitoring()
> self.run()
> self.result.time.end = time.time()
> self.result = dmesg.update_result(self.result)
> + monitoring.check_monitoring()
> # This is a rare case where a bare exception is okay, since we're
> # using it to log exceptions
> except:
> diff --git a/piglit.conf.example b/piglit.conf.example
> index 944d5d9..829081d 100644
> --- a/piglit.conf.example
> +++ b/piglit.conf.example
> @@ -152,3 +152,32 @@ run_test=./%(test_name)s
> [expected-crashes]
> ; Like expected-failures, but specifies that a test is expected to
> ; crash.
> +
> +[monitored-errors]
> +; Set the monitoring rules for checking when the system need to be rebooted.
> +; Piglit must be launched with --abort-on-monitored-error
> +;
> +; For each activated monitoring rule a section must be created in this file that
> +; contains the type of monitoring (dmesg, file or locked_file).
> +; Depending on the type, the parameter 'parameters' is a filename or a list of
> +; options. The regex is the pattern that causes Piglit aborting when it's found.
> +; Examples :
> +;
> +;i915_error_state
> +;kmsg_monitoring
> +;dmesg_error
> +;
> +;[i915_error_state]
> +;type=file
> +;parameters=/sys/kernel/debug/dri/0/i915_error_state
> +;regex=^((?!no error state collected).)*$
> +;
> +;[kmsg_monitoring]
> +;type=locked_file
> +;parameters=/dev/kmsg
> +;regex=\*ERROR\* ring create req|\*ERROR\* Failed to reset chip
> +;
> +;[dmesg_error]
> +;type=dmesg
> +;parameters=--level emerg,alert,crit,err,warn,notice
> +;regex=\*ERROR\* ring create req|\*ERROR\* Failed to reset chip|BUG:|Oops:|turning off the locking correctness validator
> diff --git a/unittests/base_tests.py b/unittests/base_tests.py
> index 2e64662..470483a 100644
> --- a/unittests/base_tests.py
> +++ b/unittests/base_tests.py
> @@ -51,7 +51,7 @@ from framework.test.base import (
> WindowResizeMixin,
> )
> from framework.options import _Options as Options
> -from framework import log, dmesg
> +from framework import log, dmesg, monitoring
>
> # pylint: disable=invalid-name
>
> @@ -410,7 +410,8 @@ class TestExecuteTraceback(object):
>
> test.execute(mock.Mock(spec=six.text_type),
> mock.Mock(spec=log.BaseLog),
> - mock.Mock(spec=dmesg.BaseDmesg))
> + mock.Mock(spec=dmesg.BaseDmesg),
> + mock.Mock(spec=monitoring.Monitoring))
>
> cls.test = test.result
>
> diff --git a/unittests/monitoring_tests.py b/unittests/monitoring_tests.py
> new file mode 100644
> index 0000000..20feb57
> --- /dev/null
> +++ b/unittests/monitoring_tests.py
> @@ -0,0 +1,190 @@
> +# Copyright (c) 2016 Intel Corporation
> +
> +# Permission is hereby granted, free of charge, to any person obtaining a copy
> +# of this software and associated documentation files (the "Software"), to deal
> +# in the Software without restriction, including without limitation the rights
> +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> +# copies of the Software, and to permit persons to whom the Software is
> +# furnished to do so, subject to the following conditions:
> +
> +# The above copyright notice and this permission notice shall be included in
> +# all copies or substantial portions of the Software.
> +
> +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> +# SOFTWARE.
> +
> +"""Tests for the monitoring module.
> +
> +This provides tests for the framework.monitoring modules.
> +
> +"""
> +
> +from __future__ import (
> + absolute_import, division, print_function, unicode_literals
> +)
> +
> +try:
> + from unittest import mock
> +except ImportError:
> + import mock
> +
> +import nose.tools as nt
> +
> +from . import utils
> +from framework import monitoring, exceptions
> +
> +
> +class TestMonitoring(object):
> + """Tests for Monitoring methods."""
> +
> + def __init__(self):
> + """Setup for TestMonitoring
> +
> + This create a monitoring.Monitoring instance with monitoring disabled
> + to avoid reading the rules in piglit.conf.
> +
> + """
> + self.regex = r'\*ERROR\*|BUG:'
> + self.init_contents = r'foo bar\n'
> + self.no_error_contents = r'foo bar\n'
> + self.error_contents = r'BUG:bar\n'
> + self.monitoring = monitoring.Monitoring(False)
> +
> + def test_Monitoring_delete_rule(self):
> + """monitorin.Monitoring: add and delete rule."""
> +
> + with utils.tempfile(self.init_contents) as tfile:
> + self.monitoring.add_rule('error_file',
> + 'file',
> + tfile,
> + self.regex)
> + self.monitoring.update_monitoring()
> +
> + self.monitoring.delete_rule('error_file')
> + with open(tfile, 'w') as fp:
> + fp.write(self.error_contents)
> + fp.close()
> + self.monitoring.check_monitoring()
> +
> + nt.assert_equal(self.monitoring.abort_needed, False)
> +
> + @nt.raises(exceptions.PiglitFatalError)
> + def test_Monitoring_add_rule_bad_format(self):
> + """monitoring.Monitoring: add non existing type rule."""
> +
> + with utils.tempfile(self.init_contents) as tfile:
> + self.monitoring.add_rule('error_file_bad_type',
> + 'bad_type',
> + tfile,
> + self.regex)
> +
> + def test_Monitoring_file_error(self):
> + """monitoring.Monitoring: error found on a file."""
> +
> + with utils.tempfile(self.init_contents) as tfile:
> + self.monitoring.add_rule('error_file',
> + 'file',
> + tfile,
> + self.regex)
> + self.monitoring.update_monitoring()
> +
> + with open(tfile, 'w') as fp:
> + fp.write(self.error_contents)
> + fp.close()
> + self.monitoring.check_monitoring()
> +
> + nt.assert_equal(self.monitoring.abort_needed, True)
> +
> + def test_Monitoring_file_no_error(self):
> + """monitoring.Monitoring: no error found on a file."""
> +
> + with utils.tempfile(self.init_contents) as tfile:
> + self.monitoring.add_rule('no_error_file',
> + 'file',
> + tfile,
> + self.regex)
> + self.monitoring.update_monitoring()
> +
> + with open(tfile, 'w') as fp:
> + fp.write(self.no_error_contents)
> + fp.close()
> + self.monitoring.check_monitoring()
> +
> + nt.assert_equal(self.monitoring.abort_needed, False)
> +
> + def test_Monitoring_locked_file_error(self):
> + """monitoring.Monitoring: error found on a locked file."""
> +
> + with utils.tempfile(self.init_contents) as tfile:
> + self.monitoring.add_rule('error_locked_file',
> + 'locked_file',
> + tfile,
> + self.regex)
> + self.monitoring.update_monitoring()
> +
> + with open(tfile, 'w') as fp:
> + fp.write(self.error_contents)
> + fp.close()
> + self.monitoring.check_monitoring()
> +
> + nt.assert_equal(self.monitoring.abort_needed, True)
> +
> + def test_Monitoring_locked_file_no_error(self):
> + """monitoring.Monitoring: no error found on a locked file."""
> +
> + with utils.tempfile(self.init_contents) as tfile:
> + self.monitoring.add_rule('no_error_file',
> + 'locked_file',
> + tfile,
> + self.regex)
> + self.monitoring.update_monitoring()
> +
> + with open(tfile, 'w') as fp:
> + fp.write(self.no_error_contents)
> + fp.close()
> + self.monitoring.check_monitoring()
> +
> + nt.assert_equal(self.monitoring.abort_needed, False)
> +
> + def test_Monitoring_dmesg_error(self):
> + """monitoring.Monitoring: error found on the dmesg."""
> +
> + utils.platform_check('linux')
> +
> + mock_out = mock.Mock(return_value=b'[1.0]This\n[2.0]is\n[3.0]dmesg')
> + with mock.patch('framework.dmesg.subprocess.check_output', mock_out):
> + self.monitoring.add_rule('no_error_file',
> + 'dmesg',
> + '--level emerg,alert,crit,err',
> + self.regex)
> + self.monitoring.update_monitoring()
> +
> + mock_out.return_value = b'[4.0]foo\n[5.0]*ERROR* bar'
> + with mock.patch('framework.dmesg.subprocess.check_output', mock_out):
> + self.monitoring.check_monitoring()
> +
> + nt.assert_equal(self.monitoring.abort_needed, True)
> +
> + def test_Monitoring_dmesg_no_error(self):
> + """monitoring.Monitoring: no error found on the dmesg."""
> +
> + utils.platform_check('linux')
> +
> + mock_out = mock.Mock(return_value=b'[1.0]This\n[2.0]is\n[3.0]dmesg')
> + with mock.patch('framework.dmesg.subprocess.check_output', mock_out):
> + self.monitoring.add_rule('no_error_file',
> + 'dmesg',
> + '--level emerg,alert,crit,err',
> + self.regex)
> + self.monitoring.update_monitoring()
> +
> + mock_out.return_value = b'[4.0]foo\n[5.0] bar'
> + with mock.patch('framework.dmesg.subprocess.check_output', mock_out):
> + self.monitoring.check_monitoring()
> +
> + nt.assert_equal(self.monitoring.abort_needed, False)
> --
> 2.7.0
>
> _______________________________________________
> Piglit mailing list
> Piglit at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/piglit
This seems pretty reasonable at this point. It doesn't seem to have a
lot of overhead anymore. Thanks for making the cleanups, adding the
tests, and being patient (I must admit I forgot about it).
Reviewed-by: Dylan Baker <baker.dylan.c at gmail.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: signature
URL: <https://lists.freedesktop.org/archives/piglit/attachments/20160406/7d2e3459/attachment-0001.sig>
More information about the Piglit
mailing list