From 3290048458fb30af1714a0a5499375c5e4a1c9a9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 13 Nov 2015 07:49:33 +0100 Subject: [PATCH] tests: Do custom fnmatch-like matching. fnmatch treats [, ] and ? as shell metacharacters too, and has no way to escape them. We need a literal [] and really only need * for filtering. --- tests/helpers/test_helper_utils.py | 21 +++++++++++++++++++++ tests/helpers/utils.py | 14 ++++++++++++-- tests/integration/test_testprocess.py | 7 ++++--- tests/integration/testprocess.py | 7 ++++--- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/tests/helpers/test_helper_utils.py b/tests/helpers/test_helper_utils.py index 9512a964f..6311c08e7 100644 --- a/tests/helpers/test_helper_utils.py +++ b/tests/helpers/test_helper_utils.py @@ -48,3 +48,24 @@ def test_partial_compare_equal(val1, val2): ]) def test_partial_compare_not_equal(val1, val2): assert not utils.partial_compare(val1, val2) + + +@pytest.mark.parametrize('pattern, value, expected', [ + ('foo', 'foo', True), + ('foo', 'bar', False), + ('foo', 'Foo', False), + ('foo', 'foobar', False), + ('foo', 'barfoo', False), + + ('foo*', 'foobarbaz', True), + ('*bar', 'foobar', True), + ('foo*baz', 'foobarbaz', True), + + ('foo[b]ar', 'foobar', False), + ('foo[b]ar', 'foo[b]ar', True), + + ('foo?ar', 'foobar', False), + ('foo?ar', 'foo?ar', True), +]) +def test_pattern_match(pattern, value, expected): + assert utils.pattern_match(pattern=pattern, value=value) == expected diff --git a/tests/helpers/utils.py b/tests/helpers/utils.py index 22ccec6d7..ccfadccd2 100644 --- a/tests/helpers/utils.py +++ b/tests/helpers/utils.py @@ -20,7 +20,7 @@ """Partial comparison of dicts/lists.""" -import fnmatch +import re import pprint @@ -86,9 +86,19 @@ def partial_compare(val1, val2, *, indent=0): equal = abs(val1 - val2) < 0.00001 elif isinstance(val2, str): print_i("|======= Doing string comparison", indent) - equal = fnmatch.fnmatchcase(val1, val2) + equal = pattern_match(pattern=val2, value=val1) else: print_i("|======= Comparing via ==", indent) equal = val1 == val2 print_i("---> {}".format(equal), indent) return equal + + +def pattern_match(*, pattern, value): + """Do fnmatch.fnmatchcase like matching, but only with * active. + + Return: + True on a match, False otherwise. + """ + re_pattern = '.*'.join(re.escape(part) for part in pattern.split('*')) + return re.fullmatch(re_pattern, value) is not None diff --git a/tests/integration/test_testprocess.py b/tests/integration/test_testprocess.py index 9738619f9..9e56e1bbd 100644 --- a/tests/integration/test_testprocess.py +++ b/tests/integration/test_testprocess.py @@ -103,12 +103,13 @@ class TestWaitFor: with pytest.raises(testprocess.WaitForTimeout): pyproc.wait_for(data="foobar", timeout=100) - def test_existing_message(self, pyproc): + @pytest.mark.parametrize('message', ['foobar', 'literal [x]']) + def test_existing_message(self, message, pyproc): """Test with a message which already passed when waiting.""" - pyproc.code = "print('foobar')" + pyproc.code = "print('{}')".format(message) pyproc.start() time.sleep(0.5) # to make sure the message is printed - pyproc.wait_for(data="foobar") + pyproc.wait_for(data=message) def test_existing_message_previous_test(self, pyproc): """Make sure the message of a previous test gets ignored.""" diff --git a/tests/integration/testprocess.py b/tests/integration/testprocess.py index 0f0bfef85..9f7c2789a 100644 --- a/tests/integration/testprocess.py +++ b/tests/integration/testprocess.py @@ -22,12 +22,13 @@ import re import os import time -import fnmatch import pytestqt.plugin # pylint: disable=import-error from PyQt5.QtCore import pyqtSlot, pyqtSignal, QProcess, QObject, QElapsedTimer from PyQt5.QtTest import QSignalSpy +from helpers import utils # pylint: disable=import-error + class InvalidLine(Exception): @@ -199,7 +200,7 @@ class Process(QObject): - If expected is None, the filter always matches. - 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 glob pattern (with only * active). - If the value is a string or bytes object and the expected value is a compiled regex, it is used for matching. - If the value is any other type, == is used. @@ -213,7 +214,7 @@ class Process(QObject): elif isinstance(expected, regex_type): return expected.match(value) elif isinstance(value, (bytes, str)): - return fnmatch.fnmatchcase(value, expected) + return utils.pattern_match(pattern=expected, value=value) else: return value == expected