1"""Test cases for the fnmatch module.""" 2 3import unittest 4import os 5import string 6import warnings 7 8from fnmatch import fnmatch, fnmatchcase, translate, filter 9 10class FnmatchTestCase(unittest.TestCase): 11 12 def check_match(self, filename, pattern, should_match=True, fn=fnmatch): 13 if should_match: 14 self.assertTrue(fn(filename, pattern), 15 "expected %r to match pattern %r" 16 % (filename, pattern)) 17 else: 18 self.assertFalse(fn(filename, pattern), 19 "expected %r not to match pattern %r" 20 % (filename, pattern)) 21 22 def test_fnmatch(self): 23 check = self.check_match 24 check('abc', 'abc') 25 check('abc', '?*?') 26 check('abc', '???*') 27 check('abc', '*???') 28 check('abc', '???') 29 check('abc', '*') 30 check('abc', 'ab[cd]') 31 check('abc', 'ab[!de]') 32 check('abc', 'ab[de]', False) 33 check('a', '??', False) 34 check('a', 'b', False) 35 36 # these test that '\' is handled correctly in character sets; 37 # see SF bug #409651 38 check('\\', r'[\]') 39 check('a', r'[!\]') 40 check('\\', r'[!\]', False) 41 42 # test that filenames with newlines in them are handled correctly. 43 # http://bugs.python.org/issue6665 44 check('foo\nbar', 'foo*') 45 check('foo\nbar\n', 'foo*') 46 check('\nfoo', 'foo*', False) 47 check('\n', '*') 48 49 def test_slow_fnmatch(self): 50 check = self.check_match 51 check('a' * 50, '*a*a*a*a*a*a*a*a*a*a') 52 # The next "takes forever" if the regexp translation is 53 # straightforward. See bpo-40480. 54 check('a' * 50 + 'b', '*a*a*a*a*a*a*a*a*a*a', False) 55 56 def test_mix_bytes_str(self): 57 self.assertRaises(TypeError, fnmatch, 'test', b'*') 58 self.assertRaises(TypeError, fnmatch, b'test', '*') 59 self.assertRaises(TypeError, fnmatchcase, 'test', b'*') 60 self.assertRaises(TypeError, fnmatchcase, b'test', '*') 61 62 def test_fnmatchcase(self): 63 check = self.check_match 64 check('abc', 'abc', True, fnmatchcase) 65 check('AbC', 'abc', False, fnmatchcase) 66 check('abc', 'AbC', False, fnmatchcase) 67 check('AbC', 'AbC', True, fnmatchcase) 68 69 check('usr/bin', 'usr/bin', True, fnmatchcase) 70 check('usr\\bin', 'usr/bin', False, fnmatchcase) 71 check('usr/bin', 'usr\\bin', False, fnmatchcase) 72 check('usr\\bin', 'usr\\bin', True, fnmatchcase) 73 74 def test_bytes(self): 75 self.check_match(b'test', b'te*') 76 self.check_match(b'test\xff', b'te*\xff') 77 self.check_match(b'foo\nbar', b'foo*') 78 79 def test_case(self): 80 ignorecase = os.path.normcase('ABC') == os.path.normcase('abc') 81 check = self.check_match 82 check('abc', 'abc') 83 check('AbC', 'abc', ignorecase) 84 check('abc', 'AbC', ignorecase) 85 check('AbC', 'AbC') 86 87 def test_sep(self): 88 normsep = os.path.normcase('\\') == os.path.normcase('/') 89 check = self.check_match 90 check('usr/bin', 'usr/bin') 91 check('usr\\bin', 'usr/bin', normsep) 92 check('usr/bin', 'usr\\bin', normsep) 93 check('usr\\bin', 'usr\\bin') 94 95 def test_char_set(self): 96 ignorecase = os.path.normcase('ABC') == os.path.normcase('abc') 97 check = self.check_match 98 tescases = string.ascii_lowercase + string.digits + string.punctuation 99 for c in tescases: 100 check(c, '[az]', c in 'az') 101 check(c, '[!az]', c not in 'az') 102 # Case insensitive. 103 for c in tescases: 104 check(c, '[AZ]', (c in 'az') and ignorecase) 105 check(c, '[!AZ]', (c not in 'az') or not ignorecase) 106 for c in string.ascii_uppercase: 107 check(c, '[az]', (c in 'AZ') and ignorecase) 108 check(c, '[!az]', (c not in 'AZ') or not ignorecase) 109 # Repeated same character. 110 for c in tescases: 111 check(c, '[aa]', c == 'a') 112 # Special cases. 113 for c in tescases: 114 check(c, '[^az]', c in '^az') 115 check(c, '[[az]', c in '[az') 116 check(c, r'[!]]', c != ']') 117 check('[', '[') 118 check('[]', '[]') 119 check('[!', '[!') 120 check('[!]', '[!]') 121 122 def test_range(self): 123 ignorecase = os.path.normcase('ABC') == os.path.normcase('abc') 124 normsep = os.path.normcase('\\') == os.path.normcase('/') 125 check = self.check_match 126 tescases = string.ascii_lowercase + string.digits + string.punctuation 127 for c in tescases: 128 check(c, '[b-d]', c in 'bcd') 129 check(c, '[!b-d]', c not in 'bcd') 130 check(c, '[b-dx-z]', c in 'bcdxyz') 131 check(c, '[!b-dx-z]', c not in 'bcdxyz') 132 # Case insensitive. 133 for c in tescases: 134 check(c, '[B-D]', (c in 'bcd') and ignorecase) 135 check(c, '[!B-D]', (c not in 'bcd') or not ignorecase) 136 for c in string.ascii_uppercase: 137 check(c, '[b-d]', (c in 'BCD') and ignorecase) 138 check(c, '[!b-d]', (c not in 'BCD') or not ignorecase) 139 # Upper bound == lower bound. 140 for c in tescases: 141 check(c, '[b-b]', c == 'b') 142 # Special cases. 143 for c in tescases: 144 check(c, '[!-#]', c not in '-#') 145 check(c, '[!--.]', c not in '-.') 146 check(c, '[^-`]', c in '^_`') 147 if not (normsep and c == '/'): 148 check(c, '[[-^]', c in r'[\]^') 149 check(c, r'[\-^]', c in r'\]^') 150 check(c, '[b-]', c in '-b') 151 check(c, '[!b-]', c not in '-b') 152 check(c, '[-b]', c in '-b') 153 check(c, '[!-b]', c not in '-b') 154 check(c, '[-]', c in '-') 155 check(c, '[!-]', c not in '-') 156 # Upper bound is less that lower bound: error in RE. 157 for c in tescases: 158 check(c, '[d-b]', False) 159 check(c, '[!d-b]', True) 160 check(c, '[d-bx-z]', c in 'xyz') 161 check(c, '[!d-bx-z]', c not in 'xyz') 162 check(c, '[d-b^-`]', c in '^_`') 163 if not (normsep and c == '/'): 164 check(c, '[d-b[-^]', c in r'[\]^') 165 166 def test_sep_in_char_set(self): 167 normsep = os.path.normcase('\\') == os.path.normcase('/') 168 check = self.check_match 169 check('/', r'[/]') 170 check('\\', r'[\]') 171 check('/', r'[\]', normsep) 172 check('\\', r'[/]', normsep) 173 check('[/]', r'[/]', False) 174 check(r'[\\]', r'[/]', False) 175 check('\\', r'[\t]') 176 check('/', r'[\t]', normsep) 177 check('t', r'[\t]') 178 check('\t', r'[\t]', False) 179 180 def test_sep_in_range(self): 181 normsep = os.path.normcase('\\') == os.path.normcase('/') 182 check = self.check_match 183 check('a/b', 'a[.-0]b', not normsep) 184 check('a\\b', 'a[.-0]b', False) 185 check('a\\b', 'a[Z-^]b', not normsep) 186 check('a/b', 'a[Z-^]b', False) 187 188 check('a/b', 'a[/-0]b', not normsep) 189 check(r'a\b', 'a[/-0]b', False) 190 check('a[/-0]b', 'a[/-0]b', False) 191 check(r'a[\-0]b', 'a[/-0]b', False) 192 193 check('a/b', 'a[.-/]b') 194 check(r'a\b', 'a[.-/]b', normsep) 195 check('a[.-/]b', 'a[.-/]b', False) 196 check(r'a[.-\]b', 'a[.-/]b', False) 197 198 check(r'a\b', r'a[\-^]b') 199 check('a/b', r'a[\-^]b', normsep) 200 check(r'a[\-^]b', r'a[\-^]b', False) 201 check('a[/-^]b', r'a[\-^]b', False) 202 203 check(r'a\b', r'a[Z-\]b', not normsep) 204 check('a/b', r'a[Z-\]b', False) 205 check(r'a[Z-\]b', r'a[Z-\]b', False) 206 check('a[Z-/]b', r'a[Z-\]b', False) 207 208 def test_warnings(self): 209 with warnings.catch_warnings(): 210 warnings.simplefilter('error', Warning) 211 check = self.check_match 212 check('[', '[[]') 213 check('&', '[a&&b]') 214 check('|', '[a||b]') 215 check('~', '[a~~b]') 216 check(',', '[a-z+--A-Z]') 217 check('.', '[a-z--/A-Z]') 218 219 220class TranslateTestCase(unittest.TestCase): 221 222 def test_translate(self): 223 import re 224 self.assertEqual(translate('*'), r'(?s:.*)\Z') 225 self.assertEqual(translate('?'), r'(?s:.)\Z') 226 self.assertEqual(translate('a?b*'), r'(?s:a.b.*)\Z') 227 self.assertEqual(translate('[abc]'), r'(?s:[abc])\Z') 228 self.assertEqual(translate('[]]'), r'(?s:[]])\Z') 229 self.assertEqual(translate('[!x]'), r'(?s:[^x])\Z') 230 self.assertEqual(translate('[^x]'), r'(?s:[\^x])\Z') 231 self.assertEqual(translate('[x'), r'(?s:\[x)\Z') 232 # from the docs 233 self.assertEqual(translate('*.txt'), r'(?s:.*\.txt)\Z') 234 # squash consecutive stars 235 self.assertEqual(translate('*********'), r'(?s:.*)\Z') 236 self.assertEqual(translate('A*********'), r'(?s:A.*)\Z') 237 self.assertEqual(translate('*********A'), r'(?s:.*A)\Z') 238 self.assertEqual(translate('A*********?[?]?'), r'(?s:A.*.[?].)\Z') 239 # fancy translation to prevent exponential-time match failure 240 t = translate('**a*a****a') 241 self.assertEqual(t, r'(?s:(?>.*?a)(?>.*?a).*a)\Z') 242 # and try pasting multiple translate results - it's an undocumented 243 # feature that this works 244 r1 = translate('**a**a**a*') 245 r2 = translate('**b**b**b*') 246 r3 = translate('*c*c*c*') 247 fatre = "|".join([r1, r2, r3]) 248 self.assertTrue(re.match(fatre, 'abaccad')) 249 self.assertTrue(re.match(fatre, 'abxbcab')) 250 self.assertTrue(re.match(fatre, 'cbabcaxc')) 251 self.assertFalse(re.match(fatre, 'dabccbad')) 252 253class FilterTestCase(unittest.TestCase): 254 255 def test_filter(self): 256 self.assertEqual(filter(['Python', 'Ruby', 'Perl', 'Tcl'], 'P*'), 257 ['Python', 'Perl']) 258 self.assertEqual(filter([b'Python', b'Ruby', b'Perl', b'Tcl'], b'P*'), 259 [b'Python', b'Perl']) 260 261 def test_mix_bytes_str(self): 262 self.assertRaises(TypeError, filter, ['test'], b'*') 263 self.assertRaises(TypeError, filter, [b'test'], '*') 264 265 def test_case(self): 266 ignorecase = os.path.normcase('P') == os.path.normcase('p') 267 self.assertEqual(filter(['Test.py', 'Test.rb', 'Test.PL'], '*.p*'), 268 ['Test.py', 'Test.PL'] if ignorecase else ['Test.py']) 269 self.assertEqual(filter(['Test.py', 'Test.rb', 'Test.PL'], '*.P*'), 270 ['Test.py', 'Test.PL'] if ignorecase else ['Test.PL']) 271 272 def test_sep(self): 273 normsep = os.path.normcase('\\') == os.path.normcase('/') 274 self.assertEqual(filter(['usr/bin', 'usr', 'usr\\lib'], 'usr/*'), 275 ['usr/bin', 'usr\\lib'] if normsep else ['usr/bin']) 276 self.assertEqual(filter(['usr/bin', 'usr', 'usr\\lib'], 'usr\\*'), 277 ['usr/bin', 'usr\\lib'] if normsep else ['usr\\lib']) 278 279 280if __name__ == "__main__": 281 unittest.main() 282