|
1 import pymake.data, pymake.parser, pymake.parserdata, pymake.functions |
|
2 import unittest |
|
3 import logging |
|
4 |
|
5 from cStringIO import StringIO |
|
6 |
|
7 def multitest(cls): |
|
8 for name in cls.testdata.iterkeys(): |
|
9 def m(self, name=name): |
|
10 return self.runSingle(*self.testdata[name]) |
|
11 |
|
12 setattr(cls, 'test_%s' % name, m) |
|
13 return cls |
|
14 |
|
15 class TestBase(unittest.TestCase): |
|
16 def assertEqual(self, a, b, msg=""): |
|
17 """Actually print the values which weren't equal, if things don't work out!""" |
|
18 unittest.TestCase.assertEqual(self, a, b, "%s got %r expected %r" % (msg, a, b)) |
|
19 |
|
20 class DataTest(TestBase): |
|
21 testdata = { |
|
22 'oneline': |
|
23 ("He\tllo", "f", 1, 0, |
|
24 ((0, "f", 1, 0), (2, "f", 1, 2), (3, "f", 1, 4))), |
|
25 'twoline': |
|
26 ("line1 \n\tl\tine2", "f", 1, 4, |
|
27 ((0, "f", 1, 4), (5, "f", 1, 9), (6, "f", 1, 10), (7, "f", 2, 0), (8, "f", 2, 4), (10, "f", 2, 8), (13, "f", 2, 11))), |
|
28 } |
|
29 |
|
30 def runSingle(self, data, filename, line, col, results): |
|
31 d = pymake.parser.Data(data, 0, len(data), pymake.parserdata.Location(filename, line, col)) |
|
32 for pos, file, lineno, col in results: |
|
33 loc = d.getloc(pos) |
|
34 self.assertEqual(loc.path, file, "data file offset %i" % pos) |
|
35 self.assertEqual(loc.line, lineno, "data line offset %i" % pos) |
|
36 self.assertEqual(loc.column, col, "data col offset %i" % pos) |
|
37 multitest(DataTest) |
|
38 |
|
39 class LineEnumeratorTest(TestBase): |
|
40 testdata = { |
|
41 'simple': ( |
|
42 'Hello, world', [ |
|
43 ('Hello, world', 1), |
|
44 ] |
|
45 ), |
|
46 'multi': ( |
|
47 'Hello\nhappy \n\nworld\n', [ |
|
48 ('Hello', 1), |
|
49 ('happy ', 2), |
|
50 ('', 3), |
|
51 ('world', 4), |
|
52 ('', 5), |
|
53 ] |
|
54 ), |
|
55 'continuation': ( |
|
56 'Hello, \\\n world\nJellybeans!', [ |
|
57 ('Hello, \\\n world', 1), |
|
58 ('Jellybeans!', 3), |
|
59 ] |
|
60 ), |
|
61 'multislash': ( |
|
62 'Hello, \\\\\n world', [ |
|
63 ('Hello, \\\\', 1), |
|
64 (' world', 2), |
|
65 ] |
|
66 ) |
|
67 } |
|
68 |
|
69 def runSingle(self, s, lines): |
|
70 gotlines = [(d.s[d.lstart:d.lend], d.loc.line) for d in pymake.parser.enumeratelines(s, 'path')] |
|
71 self.assertEqual(gotlines, lines) |
|
72 |
|
73 multitest(LineEnumeratorTest) |
|
74 |
|
75 class IterTest(TestBase): |
|
76 testdata = { |
|
77 'plaindata': ( |
|
78 pymake.parser.iterdata, |
|
79 "plaindata # test\n", |
|
80 "plaindata # test\n" |
|
81 ), |
|
82 'makecomment': ( |
|
83 pymake.parser.itermakefilechars, |
|
84 "VAR = val # comment", |
|
85 "VAR = val " |
|
86 ), |
|
87 'makeescapedcomment': ( |
|
88 pymake.parser.itermakefilechars, |
|
89 "VAR = val \# escaped hash", |
|
90 "VAR = val # escaped hash" |
|
91 ), |
|
92 'makeescapedslash': ( |
|
93 pymake.parser.itermakefilechars, |
|
94 "VAR = val\\\\", |
|
95 "VAR = val\\\\", |
|
96 ), |
|
97 'makecontinuation': ( |
|
98 pymake.parser.itermakefilechars, |
|
99 "VAR = VAL \\\n continuation # comment \\\n continuation", |
|
100 "VAR = VAL continuation " |
|
101 ), |
|
102 'makecontinuation2': ( |
|
103 pymake.parser.itermakefilechars, |
|
104 "VAR = VAL \\ \\\n continuation", |
|
105 "VAR = VAL \\ continuation" |
|
106 ), |
|
107 'makeawful': ( |
|
108 pymake.parser.itermakefilechars, |
|
109 "VAR = VAL \\\\# comment\n", |
|
110 "VAR = VAL \\" |
|
111 ), |
|
112 'command': ( |
|
113 pymake.parser.itercommandchars, |
|
114 "echo boo # comment", |
|
115 "echo boo # comment", |
|
116 ), |
|
117 'commandcomment': ( |
|
118 pymake.parser.itercommandchars, |
|
119 "echo boo \# comment", |
|
120 "echo boo \# comment", |
|
121 ), |
|
122 'commandcontinue': ( |
|
123 pymake.parser.itercommandchars, |
|
124 "echo boo # \\\n\t command 2", |
|
125 "echo boo # \\\n command 2" |
|
126 ), |
|
127 } |
|
128 |
|
129 def runSingle(self, ifunc, idata, expected): |
|
130 d = pymake.parser.Data.fromstring(idata, 'IterTest data') |
|
131 |
|
132 it = pymake.parser._alltokens.finditer(d.s, 0, d.lend) |
|
133 actual = ''.join( [c for c, t, o, oo in ifunc(d, 0, ('dummy-token',), it)] ) |
|
134 self.assertEqual(actual, expected) |
|
135 |
|
136 if ifunc == pymake.parser.itermakefilechars: |
|
137 print "testing %r" % expected |
|
138 self.assertEqual(pymake.parser.flattenmakesyntax(d, 0), expected) |
|
139 |
|
140 multitest(IterTest) |
|
141 |
|
142 |
|
143 # 'define': ( |
|
144 # pymake.parser.iterdefinechars, |
|
145 # "endef", |
|
146 # "" |
|
147 # ), |
|
148 # 'definenesting': ( |
|
149 # pymake.parser.iterdefinechars, |
|
150 # """define BAR # comment |
|
151 #random text |
|
152 #endef not what you think! |
|
153 #endef # comment is ok\n""", |
|
154 # """define BAR # comment |
|
155 #random text |
|
156 #endef not what you think!""" |
|
157 # ), |
|
158 # 'defineescaped': ( |
|
159 # pymake.parser.iterdefinechars, |
|
160 # """value \\ |
|
161 #endef |
|
162 #endef\n""", |
|
163 # "value endef" |
|
164 # ), |
|
165 |
|
166 class MakeSyntaxTest(TestBase): |
|
167 # (string, startat, stopat, stopoffset, expansion |
|
168 testdata = { |
|
169 'text': ('hello world', 0, (), None, ['hello world']), |
|
170 'singlechar': ('hello $W', 0, (), None, |
|
171 ['hello ', |
|
172 {'type': 'VariableRef', |
|
173 '.vname': ['W']} |
|
174 ]), |
|
175 'stopat': ('hello: world', 0, (':', '='), 6, ['hello']), |
|
176 'funccall': ('h $(flavor FOO)', 0, (), None, |
|
177 ['h ', |
|
178 {'type': 'FlavorFunction', |
|
179 '[0]': ['FOO']} |
|
180 ]), |
|
181 'escapedollar': ('hello$$world', 0, (), None, ['hello$world']), |
|
182 'varref': ('echo $(VAR)', 0, (), None, |
|
183 ['echo ', |
|
184 {'type': 'VariableRef', |
|
185 '.vname': ['VAR']} |
|
186 ]), |
|
187 'dynamicvarname': ('echo $($(VARNAME):.c=.o)', 0, (':',), None, |
|
188 ['echo ', |
|
189 {'type': 'SubstitutionRef', |
|
190 '.vname': [{'type': 'VariableRef', |
|
191 '.vname': ['VARNAME']} |
|
192 ], |
|
193 '.substfrom': ['.c'], |
|
194 '.substto': ['.o']} |
|
195 ]), |
|
196 'substref': (' $(VAR:VAL) := $(VAL)', 0, (':=', '+=', '=', ':'), 15, |
|
197 [' ', |
|
198 {'type': 'VariableRef', |
|
199 '.vname': ['VAR:VAL']}, |
|
200 ' ']), |
|
201 'vadsubstref': (' $(VAR:VAL) = $(VAL)', 15, (), None, |
|
202 [{'type': 'VariableRef', |
|
203 '.vname': ['VAL']}, |
|
204 ]), |
|
205 } |
|
206 |
|
207 def compareRecursive(self, actual, expected, path): |
|
208 self.assertEqual(len(actual), len(expected), |
|
209 "compareRecursive: %s %r" % (path, actual)) |
|
210 for i in xrange(0, len(actual)): |
|
211 ipath = path + [i] |
|
212 |
|
213 a, isfunc = actual[i] |
|
214 e = expected[i] |
|
215 if isinstance(e, str): |
|
216 self.assertEqual(a, e, "compareRecursive: %s" % (ipath,)) |
|
217 else: |
|
218 self.assertEqual(type(a), getattr(pymake.functions, e['type']), |
|
219 "compareRecursive: %s" % (ipath,)) |
|
220 for k, v in e.iteritems(): |
|
221 if k == 'type': |
|
222 pass |
|
223 elif k[0] == '[': |
|
224 item = int(k[1:-1]) |
|
225 proppath = ipath + [item] |
|
226 self.compareRecursive(a[item], v, proppath) |
|
227 elif k[0] == '.': |
|
228 item = k[1:] |
|
229 proppath = ipath + [item] |
|
230 self.compareRecursive(getattr(a, item), v, proppath) |
|
231 else: |
|
232 raise Exception("Unexpected property at %s: %s" % (ipath, k)) |
|
233 |
|
234 def runSingle(self, s, startat, stopat, stopoffset, expansion): |
|
235 d = pymake.parser.Data.fromstring(s, pymake.parserdata.Location('testdata', 1, 0)) |
|
236 |
|
237 a, t, offset = pymake.parser.parsemakesyntax(d, startat, stopat, pymake.parser.itermakefilechars) |
|
238 self.compareRecursive(a, expansion, []) |
|
239 self.assertEqual(offset, stopoffset) |
|
240 |
|
241 multitest(MakeSyntaxTest) |
|
242 |
|
243 class VariableTest(TestBase): |
|
244 testdata = """ |
|
245 VAR = value |
|
246 VARNAME = TESTVAR |
|
247 $(VARNAME) = testvalue |
|
248 $(VARNAME:VAR=VAL) = moretesting |
|
249 IMM := $(VARNAME) # this is a comment |
|
250 MULTIVAR = val1 \\ |
|
251 val2 |
|
252 VARNAME = newname |
|
253 """ |
|
254 expected = {'VAR': 'value', |
|
255 'VARNAME': 'newname', |
|
256 'TESTVAR': 'testvalue', |
|
257 'TESTVAL': 'moretesting', |
|
258 'IMM': 'TESTVAR ', |
|
259 'MULTIVAR': 'val1 val2', |
|
260 'UNDEF': None} |
|
261 |
|
262 def runTest(self): |
|
263 stmts = pymake.parser.parsestring(self.testdata, 'VariableTest') |
|
264 |
|
265 m = pymake.data.Makefile() |
|
266 stmts.execute(m) |
|
267 for k, v in self.expected.iteritems(): |
|
268 flavor, source, val = m.variables.get(k) |
|
269 if val is None: |
|
270 self.assertEqual(val, v, 'variable named %s' % k) |
|
271 else: |
|
272 self.assertEqual(val.resolvestr(m, m.variables), v, 'variable named %s' % k) |
|
273 |
|
274 class SimpleRuleTest(TestBase): |
|
275 testdata = """ |
|
276 VAR = value |
|
277 TSPEC = dummy |
|
278 all: TSPEC = myrule |
|
279 all:: test test2 $(VAR) |
|
280 echo "Hello, $(TSPEC)" |
|
281 |
|
282 %.o: %.c |
|
283 $(CC) -o $@ $< |
|
284 """ |
|
285 |
|
286 def runTest(self): |
|
287 stmts = pymake.parser.parsestring(self.testdata, 'SimpleRuleTest') |
|
288 |
|
289 m = pymake.data.Makefile() |
|
290 stmts.execute(m) |
|
291 self.assertEqual(m.defaulttarget, 'all', "Default target") |
|
292 |
|
293 self.assertTrue(m.hastarget('all'), "Has 'all' target") |
|
294 target = m.gettarget('all') |
|
295 rules = target.rules |
|
296 self.assertEqual(len(rules), 1, "Number of rules") |
|
297 prereqs = rules[0].prerequisites |
|
298 self.assertEqual(prereqs, ['test', 'test2', 'value'], "Prerequisites") |
|
299 commands = rules[0].commands |
|
300 self.assertEqual(len(commands), 1, "Number of commands") |
|
301 expanded = commands[0].resolvestr(m, target.variables) |
|
302 self.assertEqual(expanded, 'echo "Hello, myrule"') |
|
303 |
|
304 irules = m.implicitrules |
|
305 self.assertEqual(len(irules), 1, "Number of implicit rules") |
|
306 |
|
307 irule = irules[0] |
|
308 self.assertEqual(len(irule.targetpatterns), 1, "%.o target pattern count") |
|
309 self.assertEqual(len(irule.prerequisites), 1, "%.o prerequisite count") |
|
310 self.assertEqual(irule.targetpatterns[0].match('foo.o'), 'foo', "%.o stem") |
|
311 |
|
312 if __name__ == '__main__': |
|
313 logging.basicConfig(level=logging.DEBUG) |
|
314 unittest.main() |