1
# Copyright (C) 2006 by Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
fnmatch is not a very good pattern matcher.
21
It doesn't handle unicode patterns, nor special
28
from bzrlib.tests import TestCase
29
from bzrlib.glob_matcher import (glob_to_re, glob_to_matcher,
30
globs_to_re, globs_to_matcher)
33
class GlobToRe(TestCase):
34
"""This tests the direct conversion."""
36
def test_no_globs(self):
37
self.assertEqual('a$', glob_to_re('a'))
38
# fnmatch thinks that an unmatched [ should just
40
self.assertEqual('foo\\[$', glob_to_re('foo['))
43
self.assertEqual('a[^/\\\\]*$', glob_to_re('a*'))
44
self.assertEqual('[^/\\\\]*a$', glob_to_re('*a'))
46
def test_starstar(self):
47
self.assertEqual('a.*$', glob_to_re('a**'))
48
self.assertEqual('.*a$', glob_to_re('**a'))
49
self.assertEqual(r'.*\/a\/[^/\\]*b$', glob_to_re('**/a/*b'))
51
def test_sequence(self):
52
self.assertEqual('a[abcd]$', glob_to_re('a[abcd]'))
53
self.assertEqual('a[^abcd/\\\\]$', glob_to_re('a[!abcd]'))
54
self.assertEqual('a[\\^b]$' , glob_to_re('a[^b]'))
55
self.assertEqual('a[^^/\\\\]$', glob_to_re('a[!^]'))
57
def test_unicode(self):
58
self.assertEqual(u'a\\\xb5$', glob_to_re(u'a\xb5'))
61
class GlobMatching(TestCase):
62
"""More of a functional test, making sure globs match what we want."""
64
def assertMatching(self, glob, matching, not_matching):
65
"""Make sure glob matches matching, but not not_matching.
67
:param glob: A filename glob
68
:param matching: List of matching filenames
69
:param not_matching: List on non-matching filenames
71
matcher = glob_to_matcher(glob)
72
for fname in matching:
73
self.failUnless(matcher(fname), 'glob %s did not match %s' % (glob, fname))
74
for fname in not_matching:
75
self.failIf(matcher(fname), 'glob %s should not match %s' % (glob, fname))
77
def test_no_globs(self):
78
check = self.assertMatching
79
check('a', ['a'], ['b', 'a ', ' a', 'ba'])
80
check('foo[', ['foo['], ['[', 'foo', '[foo'])
81
check('a(b)', ['a(b)'], ['ab', 'ba', 'a(b'])
84
check = self.assertMatching
85
check('a*', ['a', 'ab', 'abc', 'a.txt'],
86
['a/', 'a/a', 'foo/a', 'a\\'])
87
# TODO jam 20060107 Some would say '*a' should not match .a
88
check('*a', ['a', 'ba', 'bca', '.a', 'c.a'],
89
['/a', 'a/a', 'foo/a', '\\a', 'a\\a'])
91
def test_starstar(self):
92
check = self.assertMatching
93
check('a**', ['a', 'ab', 'abc', 'a/', 'a/a', 'a\\'],
95
check('**a', ['a', 'ba', 'bca', '/a', '.a', './.a', '(foo)/a'],
96
['booty/ab', 'bca/b'])
99
def test_sequence(self):
100
check = self.assertMatching
101
check('a[abcd]', ['aa', 'ab', 'ac', 'ad'],
102
['a', 'ba', 'baa', 'ae', 'a/', 'abc', 'aab'])
103
check('a[!abcd]', ['ae', 'af', 'aq'],
104
['a', 'a/', 'ab', 'ac', 'ad', 'abc'])
105
check('a[^b]', ['ab', 'a^'], ['a', 'ac'])
106
check('a[!^]', ['ab', 'ac'], ['a', 'a^', 'a/'])
108
def test_unicode(self):
109
check = self.assertMatching
110
check(u'a\xb5', [u'a\xb5'], ['a', 'au', 'a/'])
111
check(u'a\xb5*.txt', [u'a\xb5.txt', u'a\xb5txt.txt', u'a\xb5\xb5.txt'],
112
[u'a.txt', u'a/a\xb5.txt'])
113
check('a*', ['a', u'a\xb5\xb5'], [u'a/\xb5'])
114
check('**a', ['a', u'\xb5/a', u'\xb5/\xb5a'],
117
check(u'a[\xb5b]', ['ab', u'a\xb5'], ['a/', 'a\\', u'ba\xb5'])
120
class GlobsToRe(TestCase):
121
"""Test that we can use multiple patterns at once"""
123
def test_basic(self):
124
self.assertEqual('(a)$', globs_to_re(['a']))
125
self.assertEqual('(a|b)$', globs_to_re(['a', 'b']))
128
class GlobsMatching(TestCase):
129
"""Functional test that multiple patterns match correctly"""
131
def assertMatching(self, globs, matching, not_matching):
132
"""Make sure globs match matching, but not not_matching.
134
:param globs: A list of filename globs
135
:param matching: List of matching filenames
136
:param not_matching: List on non-matching filenames
138
matcher = globs_to_matcher(globs)
139
for fname in matching:
140
self.failUnless(matcher(fname), 'globs %s did not match %s' % (globs, fname))
141
for fname in not_matching:
142
self.failIf(matcher(fname), 'globs %s should not match %s' % (globs, fname))
144
def test_basic(self):
145
check = self.assertMatching
146
check(['a'], ['a'], ['ab', 'b'])
147
check(['a', 'b'], ['a', 'b'], ['ab', 'ba'])
150
check = self.assertMatching
151
check(['a*', 'b*'], ['a', 'b', 'ab', 'ba', 'a(b)'],
152
['ca', 'cb', 'a/', 'b/'])
154
check(['a', 'b', 'ab*'], ['a', 'b', 'ab', 'abc'],
155
['ac', 'acb', 'a/', 'ab/'])
157
def test_starstar(self):
158
check = self.assertMatching
159
check(['a*', 'b**'], ['a', 'ab', 'abc', 'b/a', 'baa', 'b/ab'],
162
def test_bzrignore(self):
163
matches = ['.foo.swp', 'test.pyc']
164
not_matches = ['foo.py', 'test/foo.py', 'foo/test.pyc']
165
self.assertMatching(bzrlib.DEFAULT_IGNORE, matches, not_matches)
167
def test_extended_bzrignore(self):
168
# Is it safe to assume that all paths will have at least
169
# ./ at the beginning?
170
matches = ['./.foo.swp', './test.pyc', './foo/test.pyc']
171
not_matches = ['./foo.py', './test/foo.py']
173
for pat in bzrlib.DEFAULT_IGNORE:
175
patterns.append('**/' + pat)
178
self.assertMatching(patterns, matches, not_matches)