~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_globbing.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-29 22:03:03 UTC
  • mfrom: (5416.2.6 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20100929220303-cr95h8iwtggco721
(mbp) Add 'break-lock --force'

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