1 #!/usr/bin/env python2
2 """
3 glob_test.py: Tests for glob.py
4 """
5 from __future__ import print_function
6
7 import re
8 import unittest
9
10 from frontend import match
11 from osh import glob_
12
13
14 class GlobEscapeTest(unittest.TestCase):
15 def testEscapeUnescape(self):
16 esc = glob_.GlobEscape
17 unesc = glob_.GlobUnescape
18
19 pairs = [
20 (r'\*.py', '*.py'),
21 (r'\?.py', '?.py'),
22 (r'\[a\-z\]\[\[\:punct\:\]\]', '[a-z][[:punct:]]'),
23 (r'\\n', r'\n'),
24 ]
25 for e, u in pairs:
26 self.assertEqual(e, esc(u))
27 self.assertEqual(u, unesc(e))
28
29 def testLooksLikeGlob(self):
30 # The way to test bash behavior is:
31 # $ shopt -s nullglob; argv [ # not a glob
32 # $ shopt -s nullglob; argv [] # is a glob
33 # $ shopt -s nullglob; argv [][ # is a glob
34 CASES = [
35 (r'[]', True),
36 (r'[a]', True),
37 (r'[][', True),
38 (r'][', False), # no balanced pair
39 (r'\[]', False), # no balanced pair
40 (r'[', False), # no balanced pair
41 (r']', False), # no balanced pair
42 (r'echo', False),
43 (r'status=0', False),
44 (r'*', True),
45 (r'\*', False),
46 (r'\*.sh', False),
47 ('\\', False),
48 ('*\\', True),
49 ('?', True),
50 ]
51 for pat, expected in CASES:
52 self.assertEqual(expected, glob_.LooksLikeGlob(pat),
53 '%s: expected %r' % (pat, expected))
54
55 def testGlobStripRegexes(self):
56 s = 'aabbccdd'
57
58 # ${v%c*} # shortest suffix
59 m = re.match('^(.*)c.*$', s)
60 self.assertEqual('aabbc', m.group(1))
61
62 # ${v%%c*} # longest suffix
63 m = re.match('^(.*?)c.*$', s)
64 self.assertEqual('aabb', m.group(1))
65
66 # ${v#*b} # shortest prefix
67 m = re.match('^.*?b(.*)$', s)
68 self.assertEqual('bccdd', m.group(1))
69
70 # ${v##*b} # longest prefix
71 m = re.match('^.*b(.*)$', s)
72 self.assertEqual('ccdd', m.group(1))
73
74 def testPatSubRegexes(self):
75 # x=~/git/oil
76 # ${x//git*/X/}
77
78 # git*
79 r1 = re.compile('git.*')
80 result = r1.sub('X', '~/git/oil')
81 self.assertEqual('~/X', result)
82
83 r2 = re.compile('[a-z]')
84 result = r2.sub('X', 'a-b-c')
85 self.assertEqual('X-X-X', result)
86
87 # Substitute the first one only
88 r2 = re.compile('[a-z]')
89 result = r2.sub('X', 'a-b-c', count=1)
90 self.assertEqual('X-b-c', result)
91
92
93 def _ReadTokens(s):
94 lex = match.GlobLexer(s)
95 return list(lex.Tokens())
96
97
98 class GlobParserTest(unittest.TestCase):
99 def testGlobLexer(self):
100 print(_ReadTokens(''))
101 print(_ReadTokens('*.py'))
102 print(_ReadTokens(r'\*.py'))
103 print(_ReadTokens('[abc]'))
104 print(_ReadTokens('\\')) # Enf
105 print(_ReadTokens('\\x'))
106 print(_ReadTokens(r'\\'))
107 print(_ReadTokens(r'[[:alpha:]]'))
108 print(_ReadTokens(r'[?]'))
109
110 def testGlobParser(self):
111 CASES = [
112 # (glob input, expected AST, expected extended regexp, has error)
113 ('*.py', r'.*\.py', False),
114 ('*.?', r'.*\..', False),
115 ('<*>', r'<.*>', False),
116 ('\**+', r'\*.*\+', False),
117 ('\**', r'\*.*', False),
118 ('*.[ch]pp', r'.*\.[ch]pp', False),
119
120 # not globs
121 ('abc', 'abc', False),
122 ('\\*', '\\*', False),
123 ('c:\\foo', 'c:foo', False),
124 ('strange]one', 'strange\\]one', False),
125
126 # character class globs
127 ('[[:space:]abc]', '[[:space:]abc]', False),
128 ('[abc]', '[abc]', False),
129 (r'[\a\b\c]', r'[\a\b\c]', False),
130 ('[abc\[]', r'[abc\[]', False),
131 ('[!not]', '[^not]', False),
132 ('[^also_not]', '[^also_not]', False),
133 ('[!*?!\\[]', '[^*?!\\[]', False),
134 ('[!\]foo]', r'[^]foo]', False),
135
136 # invalid globs
137 ('not_closed[a-z', 'not_closed\\[a-z', True),
138 ('[[:spa[ce:]]', '\\[\\[:spa\\[ce:\\]\\]', True),
139
140 # Regression test for IndexError.
141 ('[', '\\[', True),
142 ('\\', '\\\\', True),
143 (']', '\\]', False),
144 ]
145 for glob, expected_ere, expected_err in CASES:
146 print('===')
147 print(glob)
148 regex, warnings = glob_.GlobToERE(glob)
149 self.assertEqual(
150 expected_ere, regex, 'Expected %r to translate to %r, got %r' %
151 (glob, expected_ere, regex))
152
153 print('regex : %s' % regex)
154 print('warnings: %s' % warnings)
155
156
157 if __name__ == '__main__':
158 unittest.main()