michael@0: import pymake.data, pymake.parser, pymake.parserdata, pymake.functions michael@0: import unittest michael@0: import logging michael@0: michael@0: from cStringIO import StringIO michael@0: michael@0: def multitest(cls): michael@0: for name in cls.testdata.iterkeys(): michael@0: def m(self, name=name): michael@0: return self.runSingle(*self.testdata[name]) michael@0: michael@0: setattr(cls, 'test_%s' % name, m) michael@0: return cls michael@0: michael@0: class TestBase(unittest.TestCase): michael@0: def assertEqual(self, a, b, msg=""): michael@0: """Actually print the values which weren't equal, if things don't work out!""" michael@0: unittest.TestCase.assertEqual(self, a, b, "%s got %r expected %r" % (msg, a, b)) michael@0: michael@0: class DataTest(TestBase): michael@0: testdata = { michael@0: 'oneline': michael@0: ("He\tllo", "f", 1, 0, michael@0: ((0, "f", 1, 0), (2, "f", 1, 2), (3, "f", 1, 4))), michael@0: 'twoline': michael@0: ("line1 \n\tl\tine2", "f", 1, 4, michael@0: ((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))), michael@0: } michael@0: michael@0: def runSingle(self, data, filename, line, col, results): michael@0: d = pymake.parser.Data(data, 0, len(data), pymake.parserdata.Location(filename, line, col)) michael@0: for pos, file, lineno, col in results: michael@0: loc = d.getloc(pos) michael@0: self.assertEqual(loc.path, file, "data file offset %i" % pos) michael@0: self.assertEqual(loc.line, lineno, "data line offset %i" % pos) michael@0: self.assertEqual(loc.column, col, "data col offset %i" % pos) michael@0: multitest(DataTest) michael@0: michael@0: class LineEnumeratorTest(TestBase): michael@0: testdata = { michael@0: 'simple': ( michael@0: 'Hello, world', [ michael@0: ('Hello, world', 1), michael@0: ] michael@0: ), michael@0: 'multi': ( michael@0: 'Hello\nhappy \n\nworld\n', [ michael@0: ('Hello', 1), michael@0: ('happy ', 2), michael@0: ('', 3), michael@0: ('world', 4), michael@0: ('', 5), michael@0: ] michael@0: ), michael@0: 'continuation': ( michael@0: 'Hello, \\\n world\nJellybeans!', [ michael@0: ('Hello, \\\n world', 1), michael@0: ('Jellybeans!', 3), michael@0: ] michael@0: ), michael@0: 'multislash': ( michael@0: 'Hello, \\\\\n world', [ michael@0: ('Hello, \\\\', 1), michael@0: (' world', 2), michael@0: ] michael@0: ) michael@0: } michael@0: michael@0: def runSingle(self, s, lines): michael@0: gotlines = [(d.s[d.lstart:d.lend], d.loc.line) for d in pymake.parser.enumeratelines(s, 'path')] michael@0: self.assertEqual(gotlines, lines) michael@0: michael@0: multitest(LineEnumeratorTest) michael@0: michael@0: class IterTest(TestBase): michael@0: testdata = { michael@0: 'plaindata': ( michael@0: pymake.parser.iterdata, michael@0: "plaindata # test\n", michael@0: "plaindata # test\n" michael@0: ), michael@0: 'makecomment': ( michael@0: pymake.parser.itermakefilechars, michael@0: "VAR = val # comment", michael@0: "VAR = val " michael@0: ), michael@0: 'makeescapedcomment': ( michael@0: pymake.parser.itermakefilechars, michael@0: "VAR = val \# escaped hash", michael@0: "VAR = val # escaped hash" michael@0: ), michael@0: 'makeescapedslash': ( michael@0: pymake.parser.itermakefilechars, michael@0: "VAR = val\\\\", michael@0: "VAR = val\\\\", michael@0: ), michael@0: 'makecontinuation': ( michael@0: pymake.parser.itermakefilechars, michael@0: "VAR = VAL \\\n continuation # comment \\\n continuation", michael@0: "VAR = VAL continuation " michael@0: ), michael@0: 'makecontinuation2': ( michael@0: pymake.parser.itermakefilechars, michael@0: "VAR = VAL \\ \\\n continuation", michael@0: "VAR = VAL \\ continuation" michael@0: ), michael@0: 'makeawful': ( michael@0: pymake.parser.itermakefilechars, michael@0: "VAR = VAL \\\\# comment\n", michael@0: "VAR = VAL \\" michael@0: ), michael@0: 'command': ( michael@0: pymake.parser.itercommandchars, michael@0: "echo boo # comment", michael@0: "echo boo # comment", michael@0: ), michael@0: 'commandcomment': ( michael@0: pymake.parser.itercommandchars, michael@0: "echo boo \# comment", michael@0: "echo boo \# comment", michael@0: ), michael@0: 'commandcontinue': ( michael@0: pymake.parser.itercommandchars, michael@0: "echo boo # \\\n\t command 2", michael@0: "echo boo # \\\n command 2" michael@0: ), michael@0: } michael@0: michael@0: def runSingle(self, ifunc, idata, expected): michael@0: d = pymake.parser.Data.fromstring(idata, 'IterTest data') michael@0: michael@0: it = pymake.parser._alltokens.finditer(d.s, 0, d.lend) michael@0: actual = ''.join( [c for c, t, o, oo in ifunc(d, 0, ('dummy-token',), it)] ) michael@0: self.assertEqual(actual, expected) michael@0: michael@0: if ifunc == pymake.parser.itermakefilechars: michael@0: print "testing %r" % expected michael@0: self.assertEqual(pymake.parser.flattenmakesyntax(d, 0), expected) michael@0: michael@0: multitest(IterTest) michael@0: michael@0: michael@0: # 'define': ( michael@0: # pymake.parser.iterdefinechars, michael@0: # "endef", michael@0: # "" michael@0: # ), michael@0: # 'definenesting': ( michael@0: # pymake.parser.iterdefinechars, michael@0: # """define BAR # comment michael@0: #random text michael@0: #endef not what you think! michael@0: #endef # comment is ok\n""", michael@0: # """define BAR # comment michael@0: #random text michael@0: #endef not what you think!""" michael@0: # ), michael@0: # 'defineescaped': ( michael@0: # pymake.parser.iterdefinechars, michael@0: # """value \\ michael@0: #endef michael@0: #endef\n""", michael@0: # "value endef" michael@0: # ), michael@0: michael@0: class MakeSyntaxTest(TestBase): michael@0: # (string, startat, stopat, stopoffset, expansion michael@0: testdata = { michael@0: 'text': ('hello world', 0, (), None, ['hello world']), michael@0: 'singlechar': ('hello $W', 0, (), None, michael@0: ['hello ', michael@0: {'type': 'VariableRef', michael@0: '.vname': ['W']} michael@0: ]), michael@0: 'stopat': ('hello: world', 0, (':', '='), 6, ['hello']), michael@0: 'funccall': ('h $(flavor FOO)', 0, (), None, michael@0: ['h ', michael@0: {'type': 'FlavorFunction', michael@0: '[0]': ['FOO']} michael@0: ]), michael@0: 'escapedollar': ('hello$$world', 0, (), None, ['hello$world']), michael@0: 'varref': ('echo $(VAR)', 0, (), None, michael@0: ['echo ', michael@0: {'type': 'VariableRef', michael@0: '.vname': ['VAR']} michael@0: ]), michael@0: 'dynamicvarname': ('echo $($(VARNAME):.c=.o)', 0, (':',), None, michael@0: ['echo ', michael@0: {'type': 'SubstitutionRef', michael@0: '.vname': [{'type': 'VariableRef', michael@0: '.vname': ['VARNAME']} michael@0: ], michael@0: '.substfrom': ['.c'], michael@0: '.substto': ['.o']} michael@0: ]), michael@0: 'substref': (' $(VAR:VAL) := $(VAL)', 0, (':=', '+=', '=', ':'), 15, michael@0: [' ', michael@0: {'type': 'VariableRef', michael@0: '.vname': ['VAR:VAL']}, michael@0: ' ']), michael@0: 'vadsubstref': (' $(VAR:VAL) = $(VAL)', 15, (), None, michael@0: [{'type': 'VariableRef', michael@0: '.vname': ['VAL']}, michael@0: ]), michael@0: } michael@0: michael@0: def compareRecursive(self, actual, expected, path): michael@0: self.assertEqual(len(actual), len(expected), michael@0: "compareRecursive: %s %r" % (path, actual)) michael@0: for i in xrange(0, len(actual)): michael@0: ipath = path + [i] michael@0: michael@0: a, isfunc = actual[i] michael@0: e = expected[i] michael@0: if isinstance(e, str): michael@0: self.assertEqual(a, e, "compareRecursive: %s" % (ipath,)) michael@0: else: michael@0: self.assertEqual(type(a), getattr(pymake.functions, e['type']), michael@0: "compareRecursive: %s" % (ipath,)) michael@0: for k, v in e.iteritems(): michael@0: if k == 'type': michael@0: pass michael@0: elif k[0] == '[': michael@0: item = int(k[1:-1]) michael@0: proppath = ipath + [item] michael@0: self.compareRecursive(a[item], v, proppath) michael@0: elif k[0] == '.': michael@0: item = k[1:] michael@0: proppath = ipath + [item] michael@0: self.compareRecursive(getattr(a, item), v, proppath) michael@0: else: michael@0: raise Exception("Unexpected property at %s: %s" % (ipath, k)) michael@0: michael@0: def runSingle(self, s, startat, stopat, stopoffset, expansion): michael@0: d = pymake.parser.Data.fromstring(s, pymake.parserdata.Location('testdata', 1, 0)) michael@0: michael@0: a, t, offset = pymake.parser.parsemakesyntax(d, startat, stopat, pymake.parser.itermakefilechars) michael@0: self.compareRecursive(a, expansion, []) michael@0: self.assertEqual(offset, stopoffset) michael@0: michael@0: multitest(MakeSyntaxTest) michael@0: michael@0: class VariableTest(TestBase): michael@0: testdata = """ michael@0: VAR = value michael@0: VARNAME = TESTVAR michael@0: $(VARNAME) = testvalue michael@0: $(VARNAME:VAR=VAL) = moretesting michael@0: IMM := $(VARNAME) # this is a comment michael@0: MULTIVAR = val1 \\ michael@0: val2 michael@0: VARNAME = newname michael@0: """ michael@0: expected = {'VAR': 'value', michael@0: 'VARNAME': 'newname', michael@0: 'TESTVAR': 'testvalue', michael@0: 'TESTVAL': 'moretesting', michael@0: 'IMM': 'TESTVAR ', michael@0: 'MULTIVAR': 'val1 val2', michael@0: 'UNDEF': None} michael@0: michael@0: def runTest(self): michael@0: stmts = pymake.parser.parsestring(self.testdata, 'VariableTest') michael@0: michael@0: m = pymake.data.Makefile() michael@0: stmts.execute(m) michael@0: for k, v in self.expected.iteritems(): michael@0: flavor, source, val = m.variables.get(k) michael@0: if val is None: michael@0: self.assertEqual(val, v, 'variable named %s' % k) michael@0: else: michael@0: self.assertEqual(val.resolvestr(m, m.variables), v, 'variable named %s' % k) michael@0: michael@0: class SimpleRuleTest(TestBase): michael@0: testdata = """ michael@0: VAR = value michael@0: TSPEC = dummy michael@0: all: TSPEC = myrule michael@0: all:: test test2 $(VAR) michael@0: echo "Hello, $(TSPEC)" michael@0: michael@0: %.o: %.c michael@0: $(CC) -o $@ $< michael@0: """ michael@0: michael@0: def runTest(self): michael@0: stmts = pymake.parser.parsestring(self.testdata, 'SimpleRuleTest') michael@0: michael@0: m = pymake.data.Makefile() michael@0: stmts.execute(m) michael@0: self.assertEqual(m.defaulttarget, 'all', "Default target") michael@0: michael@0: self.assertTrue(m.hastarget('all'), "Has 'all' target") michael@0: target = m.gettarget('all') michael@0: rules = target.rules michael@0: self.assertEqual(len(rules), 1, "Number of rules") michael@0: prereqs = rules[0].prerequisites michael@0: self.assertEqual(prereqs, ['test', 'test2', 'value'], "Prerequisites") michael@0: commands = rules[0].commands michael@0: self.assertEqual(len(commands), 1, "Number of commands") michael@0: expanded = commands[0].resolvestr(m, target.variables) michael@0: self.assertEqual(expanded, 'echo "Hello, myrule"') michael@0: michael@0: irules = m.implicitrules michael@0: self.assertEqual(len(irules), 1, "Number of implicit rules") michael@0: michael@0: irule = irules[0] michael@0: self.assertEqual(len(irule.targetpatterns), 1, "%.o target pattern count") michael@0: self.assertEqual(len(irule.prerequisites), 1, "%.o prerequisite count") michael@0: self.assertEqual(irule.targetpatterns[0].match('foo.o'), 'foo', "%.o stem") michael@0: michael@0: if __name__ == '__main__': michael@0: logging.basicConfig(level=logging.DEBUG) michael@0: unittest.main()