1  """Tests for distutils.filelist."""
       2  import os
       3  import re
       4  import unittest
       5  from distutils import debug
       6  from distutils.log import WARN
       7  from distutils.errors import DistutilsTemplateError
       8  from distutils.filelist import glob_to_re, translate_pattern, FileList
       9  from distutils import filelist
      10  
      11  from test.support import os_helper
      12  from test.support import captured_stdout
      13  from distutils.tests import support
      14  
      15  MANIFEST_IN = """\
      16  include ok
      17  include xo
      18  exclude xo
      19  include foo.tmp
      20  include buildout.cfg
      21  global-include *.x
      22  global-include *.txt
      23  global-exclude *.tmp
      24  recursive-include f *.oo
      25  recursive-exclude global *.x
      26  graft dir
      27  prune dir3
      28  """
      29  
      30  
      31  def make_local_path(s):
      32      """Converts '/' in a string to os.sep"""
      33      return s.replace('/', os.sep)
      34  
      35  
      36  class ESC[4;38;5;81mFileListTestCase(ESC[4;38;5;149msupportESC[4;38;5;149m.ESC[4;38;5;149mLoggingSilencer,
      37                         ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      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  
     297  class ESC[4;38;5;81mFindAllTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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  
     332  if __name__ == "__main__":
     333      unittest.main()