~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_globbing.py

  • Committer: Robert Collins
  • Date: 2010-04-08 04:34:03 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5139.
  • Revision ID: robertc@robertcollins.net-20100408043403-56z0d07vdqrx7f3t
Update bugfix for 528114 to trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2010 Canonical Ltd
 
2
# -*- coding: utf-8 -*-
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
 
 
18
from bzrlib.globbing import (
 
19
    Globster,
 
20
    ExceptionGlobster,
 
21
    _OrderedGlobster,
 
22
    normalize_pattern
 
23
    )
 
24
from bzrlib.tests import (
 
25
    TestCase,
 
26
    TestCaseInTempDir,
 
27
    )
 
28
 
 
29
 
 
30
class TestGlobster(TestCase):
 
31
 
 
32
    def assertMatch(self, matchset, glob_prefix=None):
 
33
        for glob, positive, negative in matchset:
 
34
            if glob_prefix:
 
35
                glob = glob_prefix + glob
 
36
            globster = Globster([glob])
 
37
            for name in positive:
 
38
                self.failUnless(globster.match(name), repr(
 
39
                    u'name "%s" does not match glob "%s" (re=%s)' %
 
40
                    (name, glob, globster._regex_patterns[0][0].pattern)))
 
41
            for name in negative:
 
42
                self.failIf(globster.match(name), repr(
 
43
                    u'name "%s" does match glob "%s" (re=%s)' %
 
44
                    (name, glob, globster._regex_patterns[0][0].pattern)))
 
45
 
 
46
    def assertMatchBasenameAndFullpath(self, matchset):
 
47
        # test basename matcher
 
48
        self.assertMatch(matchset)
 
49
        # test fullpath matcher
 
50
        self.assertMatch(matchset, glob_prefix='./')
 
51
 
 
52
    def test_char_group_digit(self):
 
53
        self.assertMatchBasenameAndFullpath([
 
54
            # The definition of digit this uses includes arabic digits from
 
55
            # non-latin scripts (arabic, indic, etc.) and subscript/superscript
 
56
            # digits, but neither roman numerals nor vulgar fractions.
 
57
            (u'[[:digit:]]',
 
58
             [u'0', u'5', u'\u0663', u'\u06f9', u'\u0f21', u'\xb9'],
 
59
             [u'T', u'q', u' ', u'\u8336', u'.']),
 
60
            (u'[^[:digit:]]',
 
61
             [u'T', u'q', u' ', u'\u8336', u'.'],
 
62
             [u'0', u'5', u'\u0663', u'\u06f9', u'\u0f21', u'\xb9']),
 
63
            ])
 
64
 
 
65
    def test_char_group_space(self):
 
66
        self.assertMatchBasenameAndFullpath([
 
67
            (u'[[:space:]]',
 
68
             [u' ', u'\t', u'\n', u'\xa0', u'\u2000', u'\u2002'],
 
69
             [u'a', u'-', u'\u8336', u'.']),
 
70
            (u'[^[:space:]]',
 
71
             [u'a', u'-', u'\u8336', u'.'],
 
72
             [u' ', u'\t', u'\n', u'\xa0', u'\u2000', u'\u2002']),
 
73
            ])
 
74
 
 
75
    def test_char_group_alnum(self):
 
76
        self.assertMatchBasenameAndFullpath([
 
77
            (u'[[:alnum:]]',
 
78
             [u'a', u'Z', u'\u017e', u'\u8336'],
 
79
             [u':', u'-', u'\u25cf', u'.']),
 
80
            (u'[^[:alnum:]]',
 
81
             [u':', u'-', u'\u25cf', u'.'],
 
82
             [u'a']),
 
83
            ])
 
84
 
 
85
    def test_char_group_ascii(self):
 
86
        self.assertMatchBasenameAndFullpath([
 
87
            (u'[[:ascii:]]',
 
88
             [u'a', u'Q', u'^', u'.'],
 
89
             [u'\xcc', u'\u8336']),
 
90
            (u'[^[:ascii:]]',
 
91
             [u'\xcc', u'\u8336'],
 
92
             [u'a', u'Q', u'^', u'.']),
 
93
            ])
 
94
 
 
95
    def test_char_group_blank(self):
 
96
        self.assertMatchBasenameAndFullpath([
 
97
            (u'[[:blank:]]',
 
98
             [u'\t'],
 
99
             [u'x', u'y', u'z', u'.']),
 
100
            (u'[^[:blank:]]',
 
101
             [u'x', u'y', u'z', u'.'],
 
102
             [u'\t']),
 
103
            ])
 
104
 
 
105
    def test_char_group_cntrl(self):
 
106
        self.assertMatchBasenameAndFullpath([
 
107
            (u'[[:cntrl:]]',
 
108
             [u'\b', u'\t', '\x7f'],
 
109
             [u'a', u'Q', u'\u8336', u'.']),
 
110
            (u'[^[:cntrl:]]',
 
111
             [u'a', u'Q', u'\u8336', u'.'],
 
112
             [u'\b', u'\t', '\x7f']),
 
113
            ])
 
114
 
 
115
    def test_char_group_range(self):
 
116
        self.assertMatchBasenameAndFullpath([
 
117
            (u'[a-z]',
 
118
             [u'a', u'q', u'f'],
 
119
             [u'A', u'Q', u'F']),
 
120
            (u'[^a-z]',
 
121
             [u'A', u'Q', u'F'],
 
122
             [u'a', u'q', u'f']),
 
123
            (u'[!a-z]foo',
 
124
             [u'Afoo', u'.foo'],
 
125
             [u'afoo', u'ABfoo']),
 
126
            (u'foo[!a-z]bar',
 
127
             [u'fooAbar', u'foo.bar'],
 
128
             [u'foojbar']),
 
129
            (u'[\x20-\x30\u8336]',
 
130
             [u'\040', u'\044', u'\u8336'],
 
131
             [u'\x1f']),
 
132
            (u'[^\x20-\x30\u8336]',
 
133
             [u'\x1f'],
 
134
             [u'\040', u'\044', u'\u8336']),
 
135
            ])
 
136
 
 
137
    def test_regex(self):
 
138
        self.assertMatch([
 
139
            (u'RE:(a|b|c+)',
 
140
             [u'a', u'b', u'ccc'],
 
141
             [u'd', u'aa', u'c+', u'-a']),
 
142
            (u'RE:(?:a|b|c+)',
 
143
             [u'a', u'b', u'ccc'],
 
144
             [u'd', u'aa', u'c+', u'-a']),
 
145
            (u'RE:(?P<a>.)(?P=a)',
 
146
             [u'a'],
 
147
             [u'ab', u'aa', u'aaa']),
 
148
            # test we can handle odd numbers of trailing backslashes
 
149
            (u'RE:a\\\\\\',
 
150
             [u'a\\'],
 
151
             [u'a', u'ab', u'aa', u'aaa']),
 
152
            ])
 
153
 
 
154
    def test_question_mark(self):
 
155
        self.assertMatch([
 
156
            (u'?foo',
 
157
             [u'xfoo', u'bar/xfoo', u'bar/\u8336foo', u'.foo', u'bar/.foo'],
 
158
             [u'bar/foo', u'foo']),
 
159
            (u'foo?bar',
 
160
             [u'fooxbar', u'foo.bar', u'foo\u8336bar', u'qyzzy/foo.bar'],
 
161
             [u'foo/bar']),
 
162
            (u'foo/?bar',
 
163
             [u'foo/xbar', u'foo/\u8336bar', u'foo/.bar'],
 
164
             [u'foo/bar', u'bar/foo/xbar']),
 
165
            ])
 
166
 
 
167
    def test_asterisk(self):
 
168
        self.assertMatch([
 
169
            (u'x*x',
 
170
             [u'xx', u'x.x', u'x\u8336..x', u'\u8336/x.x', u'x.y.x'],
 
171
             [u'x/x', u'bar/x/bar/x', u'bax/abaxab']),
 
172
            (u'foo/*x',
 
173
             [u'foo/x', u'foo/bax', u'foo/a.x', u'foo/.x', u'foo/.q.x'],
 
174
             [u'foo/bar/bax']),
 
175
            (u'*/*x',
 
176
             [u'\u8336/x', u'foo/x', u'foo/bax', u'x/a.x', u'.foo/x',
 
177
              u'\u8336/.x', u'foo/.q.x'],
 
178
             [u'foo/bar/bax']),
 
179
            (u'f*',
 
180
             [u'foo', u'foo.bar'],
 
181
             [u'.foo', u'foo/bar', u'foo/.bar']),
 
182
            (u'*bar',
 
183
             [u'bar', u'foobar', ur'foo\nbar', u'foo.bar', u'foo/bar',
 
184
              u'foo/foobar', u'foo/f.bar', u'.bar', u'foo/.bar'],
 
185
             []),
 
186
            ])
 
187
 
 
188
    def test_double_asterisk(self):
 
189
        self.assertMatch([
 
190
            # expected uses of double asterisk
 
191
            (u'foo/**/x',
 
192
             [u'foo/x', u'foo/bar/x'],
 
193
             [u'foox', u'foo/bax', u'foo/.x', u'foo/bar/bax']),
 
194
            (u'**/bar',
 
195
             [u'bar', u'foo/bar'],
 
196
             [u'foobar', u'foo.bar', u'foo/foobar', u'foo/f.bar',
 
197
              u'.bar', u'foo/.bar']),
 
198
            # check that we ignore extra *s, so *** is treated like ** not *.
 
199
            (u'foo/***/x',
 
200
             [u'foo/x', u'foo/bar/x'],
 
201
             [u'foox', u'foo/bax', u'foo/.x', u'foo/bar/bax']),
 
202
            (u'***/bar',
 
203
             [u'bar', u'foo/bar'],
 
204
             [u'foobar', u'foo.bar', u'foo/foobar', u'foo/f.bar',
 
205
              u'.bar', u'foo/.bar']),
 
206
            # the remaining tests check that ** is interpreted as *
 
207
            # unless it is a whole path component
 
208
            (u'x**/x',
 
209
             [u'x\u8336/x', u'x/x'],
 
210
             [u'xx', u'x.x', u'bar/x/bar/x', u'x.y.x', u'x/y/x']),
 
211
            (u'x**x',
 
212
             [u'xx', u'x.x', u'x\u8336..x', u'foo/x.x', u'x.y.x'],
 
213
             [u'bar/x/bar/x', u'xfoo/bar/x', u'x/x', u'bax/abaxab']),
 
214
            (u'foo/**x',
 
215
             [u'foo/x', u'foo/bax', u'foo/a.x', u'foo/.x', u'foo/.q.x'],
 
216
             [u'foo/bar/bax']),
 
217
            (u'f**',
 
218
             [u'foo', u'foo.bar'],
 
219
             [u'.foo', u'foo/bar', u'foo/.bar']),
 
220
            (u'**bar',
 
221
             [u'bar', u'foobar', ur'foo\nbar', u'foo.bar', u'foo/bar',
 
222
              u'foo/foobar', u'foo/f.bar', u'.bar', u'foo/.bar'],
 
223
             []),
 
224
            ])
 
225
 
 
226
    def test_leading_dot_slash(self):
 
227
        self.assertMatch([
 
228
            (u'./foo',
 
229
             [u'foo'],
 
230
             [u'\u8336/foo', u'barfoo', u'x/y/foo']),
 
231
            (u'./f*',
 
232
             [u'foo'],
 
233
             [u'foo/bar', u'foo/.bar', u'x/foo/y']),
 
234
            ])
 
235
 
 
236
    def test_backslash(self):
 
237
        self.assertMatch([
 
238
            (u'.\\foo',
 
239
             [u'foo'],
 
240
             [u'\u8336/foo', u'barfoo', u'x/y/foo']),
 
241
            (u'.\\f*',
 
242
             [u'foo'],
 
243
             [u'foo/bar', u'foo/.bar', u'x/foo/y']),
 
244
            (u'foo\\**\\x',
 
245
             [u'foo/x', u'foo/bar/x'],
 
246
             [u'foox', u'foo/bax', u'foo/.x', u'foo/bar/bax']),
 
247
            ])
 
248
 
 
249
    def test_trailing_slash(self):
 
250
        self.assertMatch([
 
251
            (u'./foo/',
 
252
             [u'foo'],
 
253
             [u'\u8336/foo', u'barfoo', u'x/y/foo']),
 
254
            (u'.\\foo\\',
 
255
             [u'foo'],
 
256
             [u'foo/', u'\u8336/foo', u'barfoo', u'x/y/foo']),
 
257
            ])
 
258
 
 
259
    def test_leading_asterisk_dot(self):
 
260
        self.assertMatch([
 
261
            (u'*.x',
 
262
             [u'foo/bar/baz.x', u'\u8336/Q.x', u'foo.y.x', u'.foo.x',
 
263
              u'bar/.foo.x', u'.x',],
 
264
             [u'foo.x.y']),
 
265
            (u'foo/*.bar',
 
266
             [u'foo/b.bar', u'foo/a.b.bar', u'foo/.bar'],
 
267
             [u'foo/bar']),
 
268
            (u'*.~*',
 
269
             [u'foo.py.~1~', u'.foo.py.~1~'],
 
270
             []),
 
271
            ])
 
272
 
 
273
    def test_end_anchor(self):
 
274
        self.assertMatch([
 
275
            (u'*.333',
 
276
             [u'foo.333'],
 
277
             [u'foo.3']),
 
278
            (u'*.3',
 
279
             [u'foo.3'],
 
280
             [u'foo.333']),
 
281
            ])
 
282
 
 
283
    def test_mixed_globs(self):
 
284
        """tests handling of combinations of path type matches.
 
285
 
 
286
        The types being extension, basename and full path.
 
287
        """
 
288
        patterns = [ u'*.foo', u'.*.swp', u'./*.png']
 
289
        globster = Globster(patterns)
 
290
        self.assertEqual(u'*.foo', globster.match('bar.foo'))
 
291
        self.assertEqual(u'./*.png', globster.match('foo.png'))
 
292
        self.assertEqual(None, globster.match('foo/bar.png'))
 
293
        self.assertEqual(u'.*.swp', globster.match('foo/.bar.py.swp'))
 
294
 
 
295
    def test_large_globset(self):
 
296
        """tests that the globster can handle a large set of patterns.
 
297
 
 
298
        Large is defined as more than supported by python regex groups,
 
299
        i.e. 99.
 
300
        This test assumes the globs are broken into regexs containing 99
 
301
        groups.
 
302
        """
 
303
        patterns = [ u'*.%03d' % i for i in xrange(0,300) ]
 
304
        globster = Globster(patterns)
 
305
        # test the fence posts
 
306
        for x in (0,98,99,197,198,296,297,299):
 
307
            filename = u'foo.%03d' % x
 
308
            self.assertEqual(patterns[x],globster.match(filename))
 
309
        self.assertEqual(None,globster.match('foobar.300'))
 
310
 
 
311
class TestExceptionGlobster(TestCase):
 
312
 
 
313
    def test_exclusion_patterns(self):
 
314
        """test that exception patterns are not matched"""
 
315
        patterns = [ u'*', u'!./local', u'!./local/**/*', u'!RE:\.z.*',u'!!./.zcompdump' ]
 
316
        globster = ExceptionGlobster(patterns)
 
317
        self.assertEqual(u'*', globster.match('tmp/foo.txt'))
 
318
        self.assertEqual(None, globster.match('local'))
 
319
        self.assertEqual(None, globster.match('local/bin/wombat'))
 
320
        self.assertEqual(None, globster.match('.zshrc'))
 
321
        self.assertEqual(None, globster.match('.zfunctions/fiddle/flam'))
 
322
        self.assertEqual(u'!!./.zcompdump', globster.match('.zcompdump'))
 
323
 
 
324
    def test_exclusion_order(self):
 
325
        """test that ordering of exclusion patterns does not matter"""
 
326
        patterns = [ u'static/**/*.html', u'!static/**/versionable.html']
 
327
        globster = ExceptionGlobster(patterns)
 
328
        self.assertEqual(u'static/**/*.html', globster.match('static/foo.html'))
 
329
        self.assertEqual(None, globster.match('static/versionable.html'))
 
330
        self.assertEqual(None, globster.match('static/bar/versionable.html'))
 
331
        globster = ExceptionGlobster(reversed(patterns))
 
332
        self.assertEqual(u'static/**/*.html', globster.match('static/foo.html'))
 
333
        self.assertEqual(None, globster.match('static/versionable.html'))
 
334
        self.assertEqual(None, globster.match('static/bar/versionable.html'))
 
335
 
 
336
class TestOrderedGlobster(TestCase):
 
337
 
 
338
    def test_ordered_globs(self):
 
339
        """test that the first match in a list is the one found"""
 
340
        patterns = [ u'*.foo', u'bar.*']
 
341
        globster = _OrderedGlobster(patterns)
 
342
        self.assertEqual(u'*.foo', globster.match('bar.foo'))
 
343
        self.assertEqual(None, globster.match('foo.bar'))
 
344
        globster = _OrderedGlobster(reversed(patterns))
 
345
        self.assertEqual(u'bar.*', globster.match('bar.foo'))
 
346
        self.assertEqual(None, globster.match('foo.bar'))
 
347
 
 
348
 
 
349
class TestNormalizePattern(TestCase):
 
350
 
 
351
    def test_backslashes(self):
 
352
        """tests that backslashes are converted to forward slashes, multiple
 
353
        backslashes are collapsed to single forward slashes and trailing
 
354
        backslashes are removed"""
 
355
        self.assertEqual(u'/', normalize_pattern(u'\\'))
 
356
        self.assertEqual(u'/', normalize_pattern(u'\\\\'))
 
357
        self.assertEqual(u'/foo/bar', normalize_pattern(u'\\foo\\bar'))
 
358
        self.assertEqual(u'foo/bar', normalize_pattern(u'foo\\bar\\'))
 
359
        self.assertEqual(u'/foo/bar', normalize_pattern(u'\\\\foo\\\\bar\\\\'))
 
360
 
 
361
    def test_forward_slashes(self):
 
362
        """tests that multiple foward slashes are collapsed to single forward
 
363
        slashes and trailing forward slashes are removed"""
 
364
        self.assertEqual(u'/', normalize_pattern(u'/'))
 
365
        self.assertEqual(u'/', normalize_pattern(u'//'))
 
366
        self.assertEqual(u'/foo/bar', normalize_pattern(u'/foo/bar'))
 
367
        self.assertEqual(u'foo/bar', normalize_pattern(u'foo/bar/'))
 
368
        self.assertEqual(u'/foo/bar', normalize_pattern(u'//foo//bar//'))
 
369
 
 
370
    def test_mixed_slashes(self):
 
371
        """tests that multiple mixed slashes are collapsed to single forward
 
372
        slashes and trailing mixed slashes are removed"""
 
373
        self.assertEqual(u'/foo/bar', normalize_pattern(u'\\/\\foo//\\///bar/\\\\/'))