~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: 2006-05-19 06:14:38 UTC
  • mfrom: (1704.2.23 bzr.mbp.integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060519061438-6300caf3926c3cff
(mbp) small fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 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/\\\\/'))