/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_globbing.py

  • Committer: Jelmer Vernooij
  • Date: 2020-05-24 00:39:50 UTC
  • mto: This revision was merged to the branch mainline in revision 7504.
  • Revision ID: jelmer@jelmer.uk-20200524003950-bbc545r76vc5yajg
Add github action.

Show diffs side-by-side

added added

removed removed

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