1"""Tests for distutils.filelist."""
2import os
3import re
4import unittest
5from distutils import debug
6from distutils.log import WARN
7from distutils.errors import DistutilsTemplateError
8from distutils.filelist import glob_to_re, translate_pattern, FileList
9
10from test.test_support import captured_stdout, run_unittest
11from distutils.tests import support
12
13MANIFEST_IN = """\
14include ok
15include xo
16exclude xo
17include foo.tmp
18include buildout.cfg
19global-include *.x
20global-include *.txt
21global-exclude *.tmp
22recursive-include f *.oo
23recursive-exclude global *.x
24graft dir
25prune dir3
26"""
27
28
29def make_local_path(s):
30    """Converts '/' in a string to os.sep"""
31    return s.replace('/', os.sep)
32
33
34class FileListTestCase(support.LoggingSilencer,
35                       unittest.TestCase):
36
37    def assertNoWarnings(self):
38        self.assertEqual(self.get_logs(WARN), [])
39        self.clear_logs()
40
41    def assertWarnings(self):
42        self.assertGreater(len(self.get_logs(WARN)), 0)
43        self.clear_logs()
44
45    def test_glob_to_re(self):
46        sep = os.sep
47        if os.sep == '\\':
48            sep = re.escape(os.sep)
49
50        for glob, regex in (
51            # simple cases
52            ('foo*', r'foo[^%(sep)s]*\Z(?ms)'),
53            ('foo?', r'foo[^%(sep)s]\Z(?ms)'),
54            ('foo??', r'foo[^%(sep)s][^%(sep)s]\Z(?ms)'),
55            # special cases
56            (r'foo\\*', r'foo\\\\[^%(sep)s]*\Z(?ms)'),
57            (r'foo\\\*', r'foo\\\\\\[^%(sep)s]*\Z(?ms)'),
58            ('foo????', r'foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s]\Z(?ms)'),
59            (r'foo\\??', r'foo\\\\[^%(sep)s][^%(sep)s]\Z(?ms)')):
60            regex = regex % {'sep': sep}
61            self.assertEqual(glob_to_re(glob), regex)
62
63    def test_process_template_line(self):
64        # testing  all MANIFEST.in template patterns
65        file_list = FileList()
66        l = make_local_path
67
68        # simulated file list
69        file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt',
70                              'buildout.cfg',
71                              # filelist does not filter out VCS directories,
72                              # it's sdist that does
73                              l('.hg/last-message.txt'),
74                              l('global/one.txt'),
75                              l('global/two.txt'),
76                              l('global/files.x'),
77                              l('global/here.tmp'),
78                              l('f/o/f.oo'),
79                              l('dir/graft-one'),
80                              l('dir/dir2/graft2'),
81                              l('dir3/ok'),
82                              l('dir3/sub/ok.txt'),
83                             ]
84
85        for line in MANIFEST_IN.split('\n'):
86            if line.strip() == '':
87                continue
88            file_list.process_template_line(line)
89
90        wanted = ['ok',
91                  'buildout.cfg',
92                  'four.txt',
93                  l('.hg/last-message.txt'),
94                  l('global/one.txt'),
95                  l('global/two.txt'),
96                  l('f/o/f.oo'),
97                  l('dir/graft-one'),
98                  l('dir/dir2/graft2'),
99                 ]
100
101        self.assertEqual(file_list.files, wanted)
102
103    def test_debug_print(self):
104        file_list = FileList()
105        with captured_stdout() as stdout:
106            file_list.debug_print('xxx')
107        self.assertEqual(stdout.getvalue(), '')
108
109        debug.DEBUG = True
110        try:
111            with captured_stdout() as stdout:
112                file_list.debug_print('xxx')
113            self.assertEqual(stdout.getvalue(), 'xxx\n')
114        finally:
115            debug.DEBUG = False
116
117    def test_set_allfiles(self):
118        file_list = FileList()
119        files = ['a', 'b', 'c']
120        file_list.set_allfiles(files)
121        self.assertEqual(file_list.allfiles, files)
122
123    def test_remove_duplicates(self):
124        file_list = FileList()
125        file_list.files = ['a', 'b', 'a', 'g', 'c', 'g']
126        # files must be sorted beforehand (sdist does it)
127        file_list.sort()
128        file_list.remove_duplicates()
129        self.assertEqual(file_list.files, ['a', 'b', 'c', 'g'])
130
131    def test_translate_pattern(self):
132        # not regex
133        self.assertTrue(hasattr(
134            translate_pattern('a', anchor=True, is_regex=False),
135            'search'))
136
137        # is a regex
138        regex = re.compile('a')
139        self.assertEqual(
140            translate_pattern(regex, anchor=True, is_regex=True),
141            regex)
142
143        # plain string flagged as regex
144        self.assertTrue(hasattr(
145            translate_pattern('a', anchor=True, is_regex=True),
146            'search'))
147
148        # glob support
149        self.assertTrue(translate_pattern(
150            '*.py', anchor=True, is_regex=False).search('filelist.py'))
151
152    def test_exclude_pattern(self):
153        # return False if no match
154        file_list = FileList()
155        self.assertFalse(file_list.exclude_pattern('*.py'))
156
157        # return True if files match
158        file_list = FileList()
159        file_list.files = ['a.py', 'b.py']
160        self.assertTrue(file_list.exclude_pattern('*.py'))
161
162        # test excludes
163        file_list = FileList()
164        file_list.files = ['a.py', 'a.txt']
165        file_list.exclude_pattern('*.py')
166        self.assertEqual(file_list.files, ['a.txt'])
167
168    def test_include_pattern(self):
169        # return False if no match
170        file_list = FileList()
171        file_list.set_allfiles([])
172        self.assertFalse(file_list.include_pattern('*.py'))
173
174        # return True if files match
175        file_list = FileList()
176        file_list.set_allfiles(['a.py', 'b.txt'])
177        self.assertTrue(file_list.include_pattern('*.py'))
178
179        # test * matches all files
180        file_list = FileList()
181        self.assertIsNone(file_list.allfiles)
182        file_list.set_allfiles(['a.py', 'b.txt'])
183        file_list.include_pattern('*')
184        self.assertEqual(file_list.allfiles, ['a.py', 'b.txt'])
185
186    def test_process_template(self):
187        l = make_local_path
188        # invalid lines
189        file_list = FileList()
190        for action in ('include', 'exclude', 'global-include',
191                       'global-exclude', 'recursive-include',
192                       'recursive-exclude', 'graft', 'prune', 'blarg'):
193            self.assertRaises(DistutilsTemplateError,
194                              file_list.process_template_line, action)
195
196        # include
197        file_list = FileList()
198        file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')])
199
200        file_list.process_template_line('include *.py')
201        self.assertEqual(file_list.files, ['a.py'])
202        self.assertNoWarnings()
203
204        file_list.process_template_line('include *.rb')
205        self.assertEqual(file_list.files, ['a.py'])
206        self.assertWarnings()
207
208        # exclude
209        file_list = FileList()
210        file_list.files = ['a.py', 'b.txt', l('d/c.py')]
211
212        file_list.process_template_line('exclude *.py')
213        self.assertEqual(file_list.files, ['b.txt', l('d/c.py')])
214        self.assertNoWarnings()
215
216        file_list.process_template_line('exclude *.rb')
217        self.assertEqual(file_list.files, ['b.txt', l('d/c.py')])
218        self.assertWarnings()
219
220        # global-include
221        file_list = FileList()
222        file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')])
223
224        file_list.process_template_line('global-include *.py')
225        self.assertEqual(file_list.files, ['a.py', l('d/c.py')])
226        self.assertNoWarnings()
227
228        file_list.process_template_line('global-include *.rb')
229        self.assertEqual(file_list.files, ['a.py', l('d/c.py')])
230        self.assertWarnings()
231
232        # global-exclude
233        file_list = FileList()
234        file_list.files = ['a.py', 'b.txt', l('d/c.py')]
235
236        file_list.process_template_line('global-exclude *.py')
237        self.assertEqual(file_list.files, ['b.txt'])
238        self.assertNoWarnings()
239
240        file_list.process_template_line('global-exclude *.rb')
241        self.assertEqual(file_list.files, ['b.txt'])
242        self.assertWarnings()
243
244        # recursive-include
245        file_list = FileList()
246        file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'),
247                                l('d/d/e.py')])
248
249        file_list.process_template_line('recursive-include d *.py')
250        self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])
251        self.assertNoWarnings()
252
253        file_list.process_template_line('recursive-include e *.py')
254        self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])
255        self.assertWarnings()
256
257        # recursive-exclude
258        file_list = FileList()
259        file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')]
260
261        file_list.process_template_line('recursive-exclude d *.py')
262        self.assertEqual(file_list.files, ['a.py', l('d/c.txt')])
263        self.assertNoWarnings()
264
265        file_list.process_template_line('recursive-exclude e *.py')
266        self.assertEqual(file_list.files, ['a.py', l('d/c.txt')])
267        self.assertWarnings()
268
269        # graft
270        file_list = FileList()
271        file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'),
272                                l('f/f.py')])
273
274        file_list.process_template_line('graft d')
275        self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])
276        self.assertNoWarnings()
277
278        file_list.process_template_line('graft e')
279        self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])
280        self.assertWarnings()
281
282        # prune
283        file_list = FileList()
284        file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')]
285
286        file_list.process_template_line('prune d')
287        self.assertEqual(file_list.files, ['a.py', l('f/f.py')])
288        self.assertNoWarnings()
289
290        file_list.process_template_line('prune e')
291        self.assertEqual(file_list.files, ['a.py', l('f/f.py')])
292        self.assertWarnings()
293
294
295def test_suite():
296    return unittest.makeSuite(FileListTestCase)
297
298if __name__ == "__main__":
299    run_unittest(test_suite())
300