1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/build/pymake/tests/parsertests.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,314 @@ 1.4 +import pymake.data, pymake.parser, pymake.parserdata, pymake.functions 1.5 +import unittest 1.6 +import logging 1.7 + 1.8 +from cStringIO import StringIO 1.9 + 1.10 +def multitest(cls): 1.11 + for name in cls.testdata.iterkeys(): 1.12 + def m(self, name=name): 1.13 + return self.runSingle(*self.testdata[name]) 1.14 + 1.15 + setattr(cls, 'test_%s' % name, m) 1.16 + return cls 1.17 + 1.18 +class TestBase(unittest.TestCase): 1.19 + def assertEqual(self, a, b, msg=""): 1.20 + """Actually print the values which weren't equal, if things don't work out!""" 1.21 + unittest.TestCase.assertEqual(self, a, b, "%s got %r expected %r" % (msg, a, b)) 1.22 + 1.23 +class DataTest(TestBase): 1.24 + testdata = { 1.25 + 'oneline': 1.26 + ("He\tllo", "f", 1, 0, 1.27 + ((0, "f", 1, 0), (2, "f", 1, 2), (3, "f", 1, 4))), 1.28 + 'twoline': 1.29 + ("line1 \n\tl\tine2", "f", 1, 4, 1.30 + ((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))), 1.31 + } 1.32 + 1.33 + def runSingle(self, data, filename, line, col, results): 1.34 + d = pymake.parser.Data(data, 0, len(data), pymake.parserdata.Location(filename, line, col)) 1.35 + for pos, file, lineno, col in results: 1.36 + loc = d.getloc(pos) 1.37 + self.assertEqual(loc.path, file, "data file offset %i" % pos) 1.38 + self.assertEqual(loc.line, lineno, "data line offset %i" % pos) 1.39 + self.assertEqual(loc.column, col, "data col offset %i" % pos) 1.40 +multitest(DataTest) 1.41 + 1.42 +class LineEnumeratorTest(TestBase): 1.43 + testdata = { 1.44 + 'simple': ( 1.45 + 'Hello, world', [ 1.46 + ('Hello, world', 1), 1.47 + ] 1.48 + ), 1.49 + 'multi': ( 1.50 + 'Hello\nhappy \n\nworld\n', [ 1.51 + ('Hello', 1), 1.52 + ('happy ', 2), 1.53 + ('', 3), 1.54 + ('world', 4), 1.55 + ('', 5), 1.56 + ] 1.57 + ), 1.58 + 'continuation': ( 1.59 + 'Hello, \\\n world\nJellybeans!', [ 1.60 + ('Hello, \\\n world', 1), 1.61 + ('Jellybeans!', 3), 1.62 + ] 1.63 + ), 1.64 + 'multislash': ( 1.65 + 'Hello, \\\\\n world', [ 1.66 + ('Hello, \\\\', 1), 1.67 + (' world', 2), 1.68 + ] 1.69 + ) 1.70 + } 1.71 + 1.72 + def runSingle(self, s, lines): 1.73 + gotlines = [(d.s[d.lstart:d.lend], d.loc.line) for d in pymake.parser.enumeratelines(s, 'path')] 1.74 + self.assertEqual(gotlines, lines) 1.75 + 1.76 +multitest(LineEnumeratorTest) 1.77 + 1.78 +class IterTest(TestBase): 1.79 + testdata = { 1.80 + 'plaindata': ( 1.81 + pymake.parser.iterdata, 1.82 + "plaindata # test\n", 1.83 + "plaindata # test\n" 1.84 + ), 1.85 + 'makecomment': ( 1.86 + pymake.parser.itermakefilechars, 1.87 + "VAR = val # comment", 1.88 + "VAR = val " 1.89 + ), 1.90 + 'makeescapedcomment': ( 1.91 + pymake.parser.itermakefilechars, 1.92 + "VAR = val \# escaped hash", 1.93 + "VAR = val # escaped hash" 1.94 + ), 1.95 + 'makeescapedslash': ( 1.96 + pymake.parser.itermakefilechars, 1.97 + "VAR = val\\\\", 1.98 + "VAR = val\\\\", 1.99 + ), 1.100 + 'makecontinuation': ( 1.101 + pymake.parser.itermakefilechars, 1.102 + "VAR = VAL \\\n continuation # comment \\\n continuation", 1.103 + "VAR = VAL continuation " 1.104 + ), 1.105 + 'makecontinuation2': ( 1.106 + pymake.parser.itermakefilechars, 1.107 + "VAR = VAL \\ \\\n continuation", 1.108 + "VAR = VAL \\ continuation" 1.109 + ), 1.110 + 'makeawful': ( 1.111 + pymake.parser.itermakefilechars, 1.112 + "VAR = VAL \\\\# comment\n", 1.113 + "VAR = VAL \\" 1.114 + ), 1.115 + 'command': ( 1.116 + pymake.parser.itercommandchars, 1.117 + "echo boo # comment", 1.118 + "echo boo # comment", 1.119 + ), 1.120 + 'commandcomment': ( 1.121 + pymake.parser.itercommandchars, 1.122 + "echo boo \# comment", 1.123 + "echo boo \# comment", 1.124 + ), 1.125 + 'commandcontinue': ( 1.126 + pymake.parser.itercommandchars, 1.127 + "echo boo # \\\n\t command 2", 1.128 + "echo boo # \\\n command 2" 1.129 + ), 1.130 + } 1.131 + 1.132 + def runSingle(self, ifunc, idata, expected): 1.133 + d = pymake.parser.Data.fromstring(idata, 'IterTest data') 1.134 + 1.135 + it = pymake.parser._alltokens.finditer(d.s, 0, d.lend) 1.136 + actual = ''.join( [c for c, t, o, oo in ifunc(d, 0, ('dummy-token',), it)] ) 1.137 + self.assertEqual(actual, expected) 1.138 + 1.139 + if ifunc == pymake.parser.itermakefilechars: 1.140 + print "testing %r" % expected 1.141 + self.assertEqual(pymake.parser.flattenmakesyntax(d, 0), expected) 1.142 + 1.143 +multitest(IterTest) 1.144 + 1.145 + 1.146 +# 'define': ( 1.147 +# pymake.parser.iterdefinechars, 1.148 +# "endef", 1.149 +# "" 1.150 +# ), 1.151 +# 'definenesting': ( 1.152 +# pymake.parser.iterdefinechars, 1.153 +# """define BAR # comment 1.154 +#random text 1.155 +#endef not what you think! 1.156 +#endef # comment is ok\n""", 1.157 +# """define BAR # comment 1.158 +#random text 1.159 +#endef not what you think!""" 1.160 +# ), 1.161 +# 'defineescaped': ( 1.162 +# pymake.parser.iterdefinechars, 1.163 +# """value \\ 1.164 +#endef 1.165 +#endef\n""", 1.166 +# "value endef" 1.167 +# ), 1.168 + 1.169 +class MakeSyntaxTest(TestBase): 1.170 + # (string, startat, stopat, stopoffset, expansion 1.171 + testdata = { 1.172 + 'text': ('hello world', 0, (), None, ['hello world']), 1.173 + 'singlechar': ('hello $W', 0, (), None, 1.174 + ['hello ', 1.175 + {'type': 'VariableRef', 1.176 + '.vname': ['W']} 1.177 + ]), 1.178 + 'stopat': ('hello: world', 0, (':', '='), 6, ['hello']), 1.179 + 'funccall': ('h $(flavor FOO)', 0, (), None, 1.180 + ['h ', 1.181 + {'type': 'FlavorFunction', 1.182 + '[0]': ['FOO']} 1.183 + ]), 1.184 + 'escapedollar': ('hello$$world', 0, (), None, ['hello$world']), 1.185 + 'varref': ('echo $(VAR)', 0, (), None, 1.186 + ['echo ', 1.187 + {'type': 'VariableRef', 1.188 + '.vname': ['VAR']} 1.189 + ]), 1.190 + 'dynamicvarname': ('echo $($(VARNAME):.c=.o)', 0, (':',), None, 1.191 + ['echo ', 1.192 + {'type': 'SubstitutionRef', 1.193 + '.vname': [{'type': 'VariableRef', 1.194 + '.vname': ['VARNAME']} 1.195 + ], 1.196 + '.substfrom': ['.c'], 1.197 + '.substto': ['.o']} 1.198 + ]), 1.199 + 'substref': (' $(VAR:VAL) := $(VAL)', 0, (':=', '+=', '=', ':'), 15, 1.200 + [' ', 1.201 + {'type': 'VariableRef', 1.202 + '.vname': ['VAR:VAL']}, 1.203 + ' ']), 1.204 + 'vadsubstref': (' $(VAR:VAL) = $(VAL)', 15, (), None, 1.205 + [{'type': 'VariableRef', 1.206 + '.vname': ['VAL']}, 1.207 + ]), 1.208 + } 1.209 + 1.210 + def compareRecursive(self, actual, expected, path): 1.211 + self.assertEqual(len(actual), len(expected), 1.212 + "compareRecursive: %s %r" % (path, actual)) 1.213 + for i in xrange(0, len(actual)): 1.214 + ipath = path + [i] 1.215 + 1.216 + a, isfunc = actual[i] 1.217 + e = expected[i] 1.218 + if isinstance(e, str): 1.219 + self.assertEqual(a, e, "compareRecursive: %s" % (ipath,)) 1.220 + else: 1.221 + self.assertEqual(type(a), getattr(pymake.functions, e['type']), 1.222 + "compareRecursive: %s" % (ipath,)) 1.223 + for k, v in e.iteritems(): 1.224 + if k == 'type': 1.225 + pass 1.226 + elif k[0] == '[': 1.227 + item = int(k[1:-1]) 1.228 + proppath = ipath + [item] 1.229 + self.compareRecursive(a[item], v, proppath) 1.230 + elif k[0] == '.': 1.231 + item = k[1:] 1.232 + proppath = ipath + [item] 1.233 + self.compareRecursive(getattr(a, item), v, proppath) 1.234 + else: 1.235 + raise Exception("Unexpected property at %s: %s" % (ipath, k)) 1.236 + 1.237 + def runSingle(self, s, startat, stopat, stopoffset, expansion): 1.238 + d = pymake.parser.Data.fromstring(s, pymake.parserdata.Location('testdata', 1, 0)) 1.239 + 1.240 + a, t, offset = pymake.parser.parsemakesyntax(d, startat, stopat, pymake.parser.itermakefilechars) 1.241 + self.compareRecursive(a, expansion, []) 1.242 + self.assertEqual(offset, stopoffset) 1.243 + 1.244 +multitest(MakeSyntaxTest) 1.245 + 1.246 +class VariableTest(TestBase): 1.247 + testdata = """ 1.248 + VAR = value 1.249 + VARNAME = TESTVAR 1.250 + $(VARNAME) = testvalue 1.251 + $(VARNAME:VAR=VAL) = moretesting 1.252 + IMM := $(VARNAME) # this is a comment 1.253 + MULTIVAR = val1 \\ 1.254 + val2 1.255 + VARNAME = newname 1.256 + """ 1.257 + expected = {'VAR': 'value', 1.258 + 'VARNAME': 'newname', 1.259 + 'TESTVAR': 'testvalue', 1.260 + 'TESTVAL': 'moretesting', 1.261 + 'IMM': 'TESTVAR ', 1.262 + 'MULTIVAR': 'val1 val2', 1.263 + 'UNDEF': None} 1.264 + 1.265 + def runTest(self): 1.266 + stmts = pymake.parser.parsestring(self.testdata, 'VariableTest') 1.267 + 1.268 + m = pymake.data.Makefile() 1.269 + stmts.execute(m) 1.270 + for k, v in self.expected.iteritems(): 1.271 + flavor, source, val = m.variables.get(k) 1.272 + if val is None: 1.273 + self.assertEqual(val, v, 'variable named %s' % k) 1.274 + else: 1.275 + self.assertEqual(val.resolvestr(m, m.variables), v, 'variable named %s' % k) 1.276 + 1.277 +class SimpleRuleTest(TestBase): 1.278 + testdata = """ 1.279 + VAR = value 1.280 +TSPEC = dummy 1.281 +all: TSPEC = myrule 1.282 +all:: test test2 $(VAR) 1.283 + echo "Hello, $(TSPEC)" 1.284 + 1.285 +%.o: %.c 1.286 + $(CC) -o $@ $< 1.287 +""" 1.288 + 1.289 + def runTest(self): 1.290 + stmts = pymake.parser.parsestring(self.testdata, 'SimpleRuleTest') 1.291 + 1.292 + m = pymake.data.Makefile() 1.293 + stmts.execute(m) 1.294 + self.assertEqual(m.defaulttarget, 'all', "Default target") 1.295 + 1.296 + self.assertTrue(m.hastarget('all'), "Has 'all' target") 1.297 + target = m.gettarget('all') 1.298 + rules = target.rules 1.299 + self.assertEqual(len(rules), 1, "Number of rules") 1.300 + prereqs = rules[0].prerequisites 1.301 + self.assertEqual(prereqs, ['test', 'test2', 'value'], "Prerequisites") 1.302 + commands = rules[0].commands 1.303 + self.assertEqual(len(commands), 1, "Number of commands") 1.304 + expanded = commands[0].resolvestr(m, target.variables) 1.305 + self.assertEqual(expanded, 'echo "Hello, myrule"') 1.306 + 1.307 + irules = m.implicitrules 1.308 + self.assertEqual(len(irules), 1, "Number of implicit rules") 1.309 + 1.310 + irule = irules[0] 1.311 + self.assertEqual(len(irule.targetpatterns), 1, "%.o target pattern count") 1.312 + self.assertEqual(len(irule.prerequisites), 1, "%.o prerequisite count") 1.313 + self.assertEqual(irule.targetpatterns[0].match('foo.o'), 'foo', "%.o stem") 1.314 + 1.315 +if __name__ == '__main__': 1.316 + logging.basicConfig(level=logging.DEBUG) 1.317 + unittest.main()