build/pymake/tests/parsertests.py

changeset 0
6474c204b198
     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()

mercurial