~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_globbing.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

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