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