diff --git a/bin/autojump_match.py b/bin/autojump_match.py index c390a75..75f9aca 100644 --- a/bin/autojump_match.py +++ b/bin/autojump_match.py @@ -5,11 +5,10 @@ import re from difflib import SequenceMatcher from autojump_utils import is_python3 -from autojump_utils import is_windows from autojump_utils import last -if is_python3(): +if is_python3(): # pragma: no cover ifilter = filter imap = map os.getcwdu = os.getcwd @@ -60,6 +59,7 @@ def match_consecutive(needles, haystack, ignore_case=False): (path='/foo/baz', weight=10), ] + # We can't actually use re.compile because of re.UNICODE regex_needle = re.compile(r''' foo # needle #1 [^/]* # all characters except os.sep zero or more times @@ -75,13 +75,10 @@ def match_consecutive(needles, haystack, ignore_case=False): (path='/foo/baz', weight=10), ] """ - # The normal \\ separator needs to be escaped again for use in regex. - sep = '\\\\' if is_windows() else os.sep - regex_no_sep = '[^' + sep + ']*' + regex_no_sep = '[^' + os.sep + ']*' regex_no_sep_end = regex_no_sep + '$' - regex_one_sep = regex_no_sep + sep + regex_no_sep - # can't use compiled regex because of flags - regex_needle = regex_one_sep.join(imap(re.escape, needles)).replace('\\', '\\\\') + regex_no_sep_end # noqa + regex_one_sep = regex_no_sep + os.sep + regex_no_sep + regex_needle = regex_one_sep.join(imap(re.escape, needles)) + regex_no_sep_end regex_flags = re.IGNORECASE | re.UNICODE if ignore_case else re.UNICODE found = lambda entry: re.search( regex_needle, diff --git a/tests/unit/autojump_match_test.py b/tests/unit/autojump_match_test.py index 2f249cb..bd02ff5 100644 --- a/tests/unit/autojump_match_test.py +++ b/tests/unit/autojump_match_test.py @@ -8,6 +8,7 @@ import pytest sys.path.append(os.path.join(os.getcwd(), 'bin')) # noqa from autojump_data import Entry from autojump_match import match_anywhere +from autojump_match import match_consecutive class TestMatchAnywhere(object): @@ -36,12 +37,7 @@ class TestMatchAnywhere(object): return [self.win_entry1, self.win_entry2, self.win_entry3] def test_single_needle(self, haystack): - assert list( - match_anywhere( - ['bar'], - haystack)) == [ - self.entry1, - self.entry2] + assert list(match_anywhere(['bar'], haystack)) == [self.entry1, self.entry2] def test_consecutive(self, haystack): assert list(match_anywhere(['foo', 'bar'], haystack)) \ @@ -69,3 +65,67 @@ class TestMatchAnywhere(object): # https://github.com/wting/autojump/issues/402 assert list(match_anywhere(['*', 'this'], haystack)) == [] assert list(match_anywhere(['this', '*'], haystack)) == [self.entry5] + + +class TestMatchConsecutive(object): + + entry1 = Entry('/foo/bar/baz', 10) + entry2 = Entry('/baz/foo/bar', 10) + entry3 = Entry('/foo/baz', 10) + entry4 = Entry('/中/zhong/国/guo', 10) + entry5 = Entry('/日/本', 10) + entry6 = Entry('/is\'t/this/a/b*tchin/edge/case?', 10) + win_entry1 = Entry('C:\Foo\Bar\Baz', 10) + win_entry2 = Entry('D:\Program Files (x86)\GIMP', 10) + win_entry3 = Entry('C:\Windows\System32', 10) + + @pytest.fixture + def haystack(self): + return [ + self.entry1, + self.entry2, + self.entry3, + self.entry4, + self.entry5, + ] + + @pytest.fixture + def windows_haystack(self): + return [self.win_entry1, self.win_entry2, self.win_entry3] + + def test_single_needle(self, haystack): + assert list(match_consecutive(['baz'], haystack)) == [self.entry1, self.entry3] + assert list(match_consecutive(['本'], haystack)) == [self.entry5] + + def test_consecutive(self, haystack): + assert list(match_consecutive(['bar', 'baz'], haystack)) == [self.entry1] + assert list(match_consecutive(['foo', 'bar'], haystack)) == [self.entry2] + assert list(match_consecutive(['国', 'guo'], haystack)) == [self.entry4] + assert list(match_consecutive(['bar', 'foo'], haystack)) == [] + + def test_ignore_case(self, haystack): + assert list(match_consecutive(['FoO', 'bAR'], haystack, ignore_case=True)) \ + == [self.entry2] + + def test_windows_ignore_case(self, windows_haystack): + assert list(match_consecutive(['gimp'], windows_haystack, True)) == [self.win_entry2] + + @pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/418') + def test_backslashes_for_windows_paths(self, windows_haystack): + assert list(match_consecutive(['program', 'gimp'], windows_haystack, True)) \ + == [self.win_entry2] + + @pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/418') + def test_foo_bar_baz(self, windows_haystack): + assert list(match_consecutive(['bar', 'baz'], windows_haystack, ignore_case=True)) \ + == [self.win_entry1] + + @pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/402') + def test_thing(self, windows_haystack): + assert list(match_consecutive(['win', '32'], windows_haystack, True)) \ + == [self.win_entry3] + + @pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/402') + def test_wildcard_in_needle(self, haystack): + assert list(match_consecutive(['*', 'this'], haystack)) == [] + assert list(match_consecutive(['*', 'edge', 'case'], haystack)) == [self.entry6] diff --git a/tox.ini b/tox.ini index d2d98a0..8a21b97 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ deps = coverage ipdb ipython - pytest + pytest >= 2.9 commands = coverage run --source=bin/ --omit=bin/autojump_argparse.py -m \ py.test -vv -rxs --tb native -s --strict {posargs:tests}