diff --git a/tests/integration/features/backforward.feature b/tests/integration/features/backforward.feature index 47c4b169e..fd4257f64 100644 --- a/tests/integration/features/backforward.feature +++ b/tests/integration/features/backforward.feature @@ -13,3 +13,13 @@ Feature: Going back and forward. data/backforward/2.txt data/backforward/1.txt data/backforward/2.txt + +# Scenario: Going back without history +# Given I open data/backforward/1.txt +# When I run :back +# Then the error "At beginning of history." should be shown. +# +# Scenario: Going forward without history +# Given I open data/backforward/1.txt +# When I run :forward +# Then the error "At end of history." should be shown. diff --git a/tests/integration/features/test_features.py b/tests/integration/features/test_features.py index 73b67857f..a1bed906c 100644 --- a/tests/integration/features/test_features.py +++ b/tests/integration/features/test_features.py @@ -59,3 +59,8 @@ def lost_of_loaded_pages(httpbin, pages): requests = [httpbin.Request('GET', '/' + path.strip()) for path in pages.split('\n')] assert httpbin.get_requests() == requests + + +@bdd.then(bdd.parsers.parse('the error "{msg}" should be shown.')) +def expect_error(quteproc, msg): + quteproc.mark_expected(category='message', loglevel='ERROR', msg=msg) diff --git a/tests/integration/quteprocess.py b/tests/integration/quteprocess.py index d198423cc..a5f2ffccd 100644 --- a/tests/integration/quteprocess.py +++ b/tests/integration/quteprocess.py @@ -35,9 +35,29 @@ import testprocess # pylint: disable=import-error from qutebrowser.misc import ipc -LogLine = collections.namedtuple('LogLine', [ - 'timestamp', 'loglevel', 'category', 'module', 'function', 'line', - 'message']) +class NoLineMatch(Exception): + + """Raised by LogLine on unmatched lines.""" + + pass + + +class LogLine: + + LOG_RE = re.compile(r""" + (?P\d\d:\d\d:\d\d) + \ (?PVDEBUG|DEBUG|INFO|WARNING|ERROR) + \ +(?P\w+) + \ +(?P(\w+|Unknown\ module)):(?P\w+):(?P\d+) + \ (?P.+) + """, re.VERBOSE) + + def __init__(self, line): + match = self.LOG_RE.match(line) + if match is None: + raise NoLineMatch(line) + self.__dict__.update(match.groupdict()) + self.expected = False class QuteProc(testprocess.Process): @@ -49,14 +69,6 @@ class QuteProc(testprocess.Process): _httpbin: The HTTPBin webserver. """ - LOG_RE = re.compile(r""" - (?P\d\d:\d\d:\d\d) - \ (?PVDEBUG|DEBUG|INFO|WARNING|ERROR) - \ +(?P\w+) - \ +(?P(\w+|Unknown\ module)):(?P\w+):(?P\d+) - \ (?P.+) - """, re.VERBOSE) - executing_command = pyqtSignal() setting_done = pyqtSignal() url_loaded = pyqtSignal() @@ -68,8 +80,9 @@ class QuteProc(testprocess.Process): self._ipc_socket = None def _parse_line(self, line): - match = self.LOG_RE.match(line) - if match is None: + try: + log_line = LogLine(line) + except NoLineMatch: if line.startswith(' '): # Multiple lines in some log output... return None @@ -77,7 +90,6 @@ class QuteProc(testprocess.Process): return None else: raise testprocess.InvalidLine - log_line = LogLine(**match.groupdict()) if (log_line.loglevel in ['INFO', 'WARNING', 'ERROR'] or pytest.config.getoption('--verbose')): @@ -126,7 +138,8 @@ class QuteProc(testprocess.Process): def after_test(self): bad_msgs = [msg for msg in self._data - if msg.loglevel not in ['VDEBUG', 'DEBUG', 'INFO']] + if msg.loglevel not in ['VDEBUG', 'DEBUG', 'INFO'] + and not msg.expected] super().after_test() if bad_msgs: text = 'Logged unexpected errors:\n\n' + '\n'.join( @@ -153,6 +166,17 @@ class QuteProc(testprocess.Process): else: self.send_cmd(':open ' + url) + def mark_expected(self, category=None, loglevel=None, msg=None): + """Mark a given logging message as expected.""" + for item in self._data: + if category is not None and item.category != category: + continue + elif loglevel is not None and item.loglevel != loglevel: + continue + elif msg is not None and item.message != msg: + continue + item.expected = True + @pytest.yield_fixture def quteproc(qapp, httpbin):