bdd: Match historic messages in wait_for.

Fixes #1083.
This commit is contained in:
Florian Bruhin 2015-11-05 06:37:35 +01:00
parent 6c718bc839
commit 924b0052c6
3 changed files with 59 additions and 36 deletions

View File

@ -205,25 +205,18 @@ class QuteProc(testprocess.Process):
def mark_expected(self, category=None, loglevel=None, message=None): def mark_expected(self, category=None, loglevel=None, message=None):
"""Mark a given logging message as expected.""" """Mark a given logging message as expected."""
found_message = False
# Search existing messages
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 message is not None and item.message != message:
continue
item.expected = True
found_message = True
# If there is none, wait for the message
if not found_message:
line = self.wait_for(category=category, loglevel=loglevel, line = self.wait_for(category=category, loglevel=loglevel,
message=message) message=message)
line.expected = True line.expected = True
def wait_for(self, timeout=15000, **kwargs):
"""Override testprocess.wait_for to check past messages.
self._data is cleared after every test to provide at least some
isolation.
"""
return super().wait_for(timeout, **kwargs)
def get_session(self): def get_session(self):
"""Save the session and get the parsed session data.""" """Save the session and get the parsed session data."""
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:

View File

@ -20,6 +20,7 @@
"""Test testprocess.Process.""" """Test testprocess.Process."""
import sys import sys
import time
import contextlib import contextlib
import datetime import datetime
@ -110,3 +111,19 @@ class TestWaitFor:
pyproc.start() pyproc.start()
with pytest.raises(testprocess.WaitForTimeout): with pytest.raises(testprocess.WaitForTimeout):
pyproc.wait_for(data="foobar", timeout=100) pyproc.wait_for(data="foobar", timeout=100)
def test_existing_message(self, pyproc):
"""Test with a message which already passed when waiting."""
pyproc.code = "print('foobar')"
pyproc.start()
time.sleep(0.5) # to make sure the message is printed
pyproc.wait_for(data="foobar")
def test_existing_message_previous_test(self, pyproc):
"""Make sure the message of a previous test gets ignored."""
pyproc.code = "print('foobar')"
pyproc.start()
time.sleep(0.5) # to make sure the message is printed
pyproc.after_test()
with pytest.raises(testprocess.WaitForTimeout):
pyproc.wait_for(data="foobar", timeout=100)

View File

@ -157,32 +157,54 @@ class Process(QObject):
"""Check if the process is currently running.""" """Check if the process is currently running."""
return self.proc.state() == QProcess.Running return self.proc.state() == QProcess.Running
def wait_for(self, timeout=15000, **kwargs): def _match_data(self, value, expected):
"""Wait until a given value is found in the data. """Helper for wait_for to match a given value.
Keyword arguments to this function get interpreted as attributes of the
searched data. Every given argument is treated as a pattern which
the attribute has to match against.
The behavior of this method is slightly different depending on the The behavior of this method is slightly different depending on the
types of the filtered values: types of the filtered values:
- If expected is None, the filter always matches.
- If the value is a string or bytes object and the expected value is - If the value is a string or bytes object and the expected value is
too, the pattern is treated as a fnmatch glob pattern. too, the pattern is treated as a fnmatch glob pattern.
- If the value is a string or bytes object and the expected value is a - If the value is a string or bytes object and the expected value is a
compiled regex, it is used for matching. compiled regex, it is used for matching.
- If the value is any other type, == is used. - If the value is any other type, == is used.
Return:
A bool
"""
regex_type = type(re.compile(''))
if expected is None:
return True
elif isinstance(expected, regex_type):
return expected.match(value)
elif isinstance(value, (bytes, str)):
return fnmatch.fnmatchcase(value, expected)
else:
return value == expected
def wait_for(self, timeout=15000, **kwargs):
"""Wait until a given value is found in the data.
Keyword arguments to this function get interpreted as attributes of the
searched data. Every given argument is treated as a pattern which
the attribute has to match against.
Return: Return:
The matched line. The matched line.
""" """
# Search existing messages
for item in self._data:
matches = []
# FIXME make this a context manager which inserts a marker in for key, expected in kwargs.items():
# self._data in __enter__ and checks if the signal already did arrive value = getattr(item, key)
# after marker in __exit__, and if not, waits? matches.append(self._match_data(value, expected))
regex_type = type(re.compile('')) if all(matches):
return item
# If there is none, wait for the message
spy = QSignalSpy(self.new_data) spy = QSignalSpy(self.new_data)
elapsed_timer = QElapsedTimer() elapsed_timer = QElapsedTimer()
elapsed_timer.start() elapsed_timer.start()
@ -200,17 +222,8 @@ class Process(QObject):
matches = [] matches = []
for key, expected in kwargs.items(): for key, expected in kwargs.items():
if expected is None:
continue
value = getattr(line, key) value = getattr(line, key)
matches.append(self._match_data(value, expected))
if isinstance(expected, regex_type):
matches.append(expected.match(value))
elif isinstance(value, (bytes, str)):
matches.append(fnmatch.fnmatchcase(value, expected))
else:
matches.append(value == expected)
if all(matches): if all(matches):
return line return line