build/pymake/tests/formattingtests.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/build/pymake/tests/formattingtests.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,289 @@
     1.4 +# This file contains test code for the formatting of parsed statements back to
     1.5 +# make file "source." It essentially verifies to to_source() functions
     1.6 +# scattered across the tree.
     1.7 +
     1.8 +import glob
     1.9 +import logging
    1.10 +import os.path
    1.11 +import unittest
    1.12 +
    1.13 +from pymake.data import Expansion
    1.14 +from pymake.data import StringExpansion
    1.15 +from pymake.functions import BasenameFunction
    1.16 +from pymake.functions import SubstitutionRef
    1.17 +from pymake.functions import VariableRef
    1.18 +from pymake.functions import WordlistFunction
    1.19 +from pymake.parserdata import Include
    1.20 +from pymake.parserdata import SetVariable
    1.21 +from pymake.parser import parsestring
    1.22 +from pymake.parser import SyntaxError
    1.23 +
    1.24 +class TestBase(unittest.TestCase):
    1.25 +    pass
    1.26 +
    1.27 +class VariableRefTest(TestBase):
    1.28 +    def test_string_name(self):
    1.29 +        e = StringExpansion('foo', None)
    1.30 +        v = VariableRef(None, e)
    1.31 +
    1.32 +        self.assertEqual(v.to_source(), '$(foo)')
    1.33 +
    1.34 +    def test_special_variable(self):
    1.35 +        e = StringExpansion('<', None)
    1.36 +        v = VariableRef(None, e)
    1.37 +
    1.38 +        self.assertEqual(v.to_source(), '$<')
    1.39 +
    1.40 +    def test_expansion_simple(self):
    1.41 +        e = Expansion()
    1.42 +        e.appendstr('foo')
    1.43 +        e.appendstr('bar')
    1.44 +
    1.45 +        v = VariableRef(None, e)
    1.46 +
    1.47 +        self.assertEqual(v.to_source(), '$(foobar)')
    1.48 +
    1.49 +class StandardFunctionTest(TestBase):
    1.50 +    def test_basename(self):
    1.51 +        e1 = StringExpansion('foo', None)
    1.52 +        v = VariableRef(None, e1)
    1.53 +        e2 = Expansion(None)
    1.54 +        e2.appendfunc(v)
    1.55 +
    1.56 +        b = BasenameFunction(None)
    1.57 +        b.append(e2)
    1.58 +
    1.59 +        self.assertEqual(b.to_source(), '$(basename $(foo))')
    1.60 +
    1.61 +    def test_wordlist(self):
    1.62 +        e1 = StringExpansion('foo', None)
    1.63 +        e2 = StringExpansion('bar ', None)
    1.64 +        e3 = StringExpansion(' baz', None)
    1.65 +
    1.66 +        w = WordlistFunction(None)
    1.67 +        w.append(e1)
    1.68 +        w.append(e2)
    1.69 +        w.append(e3)
    1.70 +
    1.71 +        self.assertEqual(w.to_source(), '$(wordlist foo,bar , baz)')
    1.72 +
    1.73 +    def test_curly_brackets(self):
    1.74 +        e1 = Expansion(None)
    1.75 +        e1.appendstr('foo')
    1.76 +
    1.77 +        e2 = Expansion(None)
    1.78 +        e2.appendstr('foo ( bar')
    1.79 +
    1.80 +        f = WordlistFunction(None)
    1.81 +        f.append(e1)
    1.82 +        f.append(e2)
    1.83 +
    1.84 +        self.assertEqual(f.to_source(), '${wordlist foo,foo ( bar}')
    1.85 +
    1.86 +class StringExpansionTest(TestBase):
    1.87 +    def test_simple(self):
    1.88 +        e = StringExpansion('foobar', None)
    1.89 +        self.assertEqual(e.to_source(), 'foobar')
    1.90 +
    1.91 +        e = StringExpansion('$var', None)
    1.92 +        self.assertEqual(e.to_source(), '$var')
    1.93 +
    1.94 +    def test_escaping(self):
    1.95 +        e = StringExpansion('$var', None)
    1.96 +        self.assertEqual(e.to_source(escape_variables=True), '$$var')
    1.97 +
    1.98 +        e = StringExpansion('this is # not a comment', None)
    1.99 +        self.assertEqual(e.to_source(escape_comments=True),
   1.100 +                         'this is \# not a comment')
   1.101 +
   1.102 +    def test_empty(self):
   1.103 +        e = StringExpansion('', None)
   1.104 +        self.assertEqual(e.to_source(), '')
   1.105 +
   1.106 +        e = StringExpansion(' ', None)
   1.107 +        self.assertEqual(e.to_source(), ' ')
   1.108 +
   1.109 +class ExpansionTest(TestBase):
   1.110 +    def test_single_string(self):
   1.111 +        e = Expansion()
   1.112 +        e.appendstr('foo')
   1.113 +
   1.114 +        self.assertEqual(e.to_source(), 'foo')
   1.115 +
   1.116 +    def test_multiple_strings(self):
   1.117 +        e = Expansion()
   1.118 +        e.appendstr('hello')
   1.119 +        e.appendstr('world')
   1.120 +
   1.121 +        self.assertEqual(e.to_source(), 'helloworld')
   1.122 +
   1.123 +    def test_string_escape(self):
   1.124 +        e = Expansion()
   1.125 +        e.appendstr('$var')
   1.126 +        self.assertEqual(e.to_source(), '$var')
   1.127 +        self.assertEqual(e.to_source(escape_variables=True), '$$var')
   1.128 +
   1.129 +        e = Expansion()
   1.130 +        e.appendstr('foo')
   1.131 +        e.appendstr(' $bar')
   1.132 +        self.assertEqual(e.to_source(escape_variables=True), 'foo $$bar')
   1.133 +
   1.134 +class SubstitutionRefTest(TestBase):
   1.135 +    def test_simple(self):
   1.136 +        name = StringExpansion('foo', None)
   1.137 +        c = StringExpansion('%.c', None)
   1.138 +        o = StringExpansion('%.o', None)
   1.139 +        s = SubstitutionRef(None, name, c, o)
   1.140 +
   1.141 +        self.assertEqual(s.to_source(), '$(foo:%.c=%.o)')
   1.142 +
   1.143 +class SetVariableTest(TestBase):
   1.144 +    def test_simple(self):
   1.145 +        v = SetVariable(StringExpansion('foo', None), '=', 'bar', None, None)
   1.146 +        self.assertEqual(v.to_source(), 'foo = bar')
   1.147 +
   1.148 +    def test_multiline(self):
   1.149 +        s = 'hello\nworld'
   1.150 +        foo = StringExpansion('FOO', None)
   1.151 +
   1.152 +        v = SetVariable(foo, '=', s, None, None)
   1.153 +
   1.154 +        self.assertEqual(v.to_source(), 'define FOO\nhello\nworld\nendef')
   1.155 +
   1.156 +    def test_multiline_immediate(self):
   1.157 +        source = 'define FOO :=\nhello\nworld\nendef'
   1.158 +
   1.159 +        statements = parsestring(source, 'foo.mk')
   1.160 +        self.assertEqual(statements.to_source(), source)
   1.161 +
   1.162 +    def test_target_specific(self):
   1.163 +        foo = StringExpansion('FOO', None)
   1.164 +        bar = StringExpansion('BAR', None)
   1.165 +
   1.166 +        v = SetVariable(foo, '+=', 'value', None, bar)
   1.167 +
   1.168 +        self.assertEqual(v.to_source(), 'BAR: FOO += value')
   1.169 +
   1.170 +class IncludeTest(TestBase):
   1.171 +    def test_include(self):
   1.172 +        e = StringExpansion('rules.mk', None)
   1.173 +        i = Include(e, True, False)
   1.174 +        self.assertEqual(i.to_source(), 'include rules.mk')
   1.175 +
   1.176 +        i = Include(e, False, False)
   1.177 +        self.assertEqual(i.to_source(), '-include rules.mk')
   1.178 +
   1.179 +class IfdefTest(TestBase):
   1.180 +    def test_simple(self):
   1.181 +        source = 'ifdef FOO\nbar := $(value)\nendif'
   1.182 +
   1.183 +        statements = parsestring(source, 'foo.mk')
   1.184 +        self.assertEqual(statements[0].to_source(), source)
   1.185 +
   1.186 +    def test_nested(self):
   1.187 +        source = 'ifdef FOO\nifdef BAR\nhello = world\nendif\nendif'
   1.188 +
   1.189 +        statements = parsestring(source, 'foo.mk')
   1.190 +        self.assertEqual(statements[0].to_source(), source)
   1.191 +
   1.192 +    def test_negation(self):
   1.193 +        source = 'ifndef FOO\nbar += value\nendif'
   1.194 +
   1.195 +        statements = parsestring(source, 'foo.mk')
   1.196 +        self.assertEqual(statements[0].to_source(), source)
   1.197 +
   1.198 +class IfeqTest(TestBase):
   1.199 +    def test_simple(self):
   1.200 +        source = 'ifeq ($(foo),bar)\nhello = $(world)\nendif'
   1.201 +
   1.202 +        statements = parsestring(source, 'foo.mk')
   1.203 +        self.assertEqual(statements[0].to_source(), source)
   1.204 +
   1.205 +    def test_negation(self):
   1.206 +        source = 'ifneq (foo,bar)\nhello = world\nendif'
   1.207 +
   1.208 +        statements = parsestring(source, 'foo.mk')
   1.209 +        self.assertEqual(statements.to_source(), source)
   1.210 +
   1.211 +class ConditionBlocksTest(TestBase):
   1.212 +    def test_mixed_conditions(self):
   1.213 +        source = 'ifdef FOO\nifeq ($(FOO),bar)\nvar += $(value)\nendif\nendif'
   1.214 +
   1.215 +        statements = parsestring(source, 'foo.mk')
   1.216 +        self.assertEqual(statements.to_source(), source)
   1.217 +
   1.218 +    def test_extra_statements(self):
   1.219 +        source = 'ifdef FOO\nF := 1\nifdef BAR\nB += 1\nendif\nC = 1\nendif'
   1.220 +
   1.221 +        statements = parsestring(source, 'foo.mk')
   1.222 +        self.assertEqual(statements.to_source(), source)
   1.223 +
   1.224 +    def test_whitespace_preservation(self):
   1.225 +        source = "ifeq ' x' 'x '\n$(error stripping)\nendif"
   1.226 +
   1.227 +        statements = parsestring(source, 'foo.mk')
   1.228 +        self.assertEqual(statements.to_source(), source)
   1.229 +
   1.230 +        source = 'ifneq (x , x)\n$(error stripping)\nendif'
   1.231 +        statements = parsestring(source, 'foo.mk')
   1.232 +        self.assertEqual(statements.to_source(),
   1.233 +                'ifneq (x,x)\n$(error stripping)\nendif')
   1.234 +
   1.235 +class MakefileCorupusTest(TestBase):
   1.236 +    """Runs the make files from the pymake corpus through the formatter.
   1.237 +
   1.238 +    All the above tests are child's play compared to this.
   1.239 +    """
   1.240 +
   1.241 +    # Our reformatting isn't perfect. We ignore files with known failures until
   1.242 +    # we make them work.
   1.243 +    # TODO Address these formatting corner cases.
   1.244 +    _IGNORE_FILES = [
   1.245 +        # We are thrown off by backslashes at end of lines.
   1.246 +        'comment-parsing.mk',
   1.247 +        'escape-chars.mk',
   1.248 +        'include-notfound.mk',
   1.249 +    ]
   1.250 +
   1.251 +    def _get_test_files(self):
   1.252 +        ourdir = os.path.dirname(os.path.abspath(__file__))
   1.253 +
   1.254 +        for makefile in glob.glob(os.path.join(ourdir, '*.mk')):
   1.255 +            if os.path.basename(makefile) in self._IGNORE_FILES:
   1.256 +                continue
   1.257 +
   1.258 +            source = None
   1.259 +            with open(makefile, 'rU') as fh:
   1.260 +                source = fh.read()
   1.261 +
   1.262 +            try:
   1.263 +                yield (makefile, source, parsestring(source, makefile))
   1.264 +            except SyntaxError:
   1.265 +                continue
   1.266 +
   1.267 +    def test_reparse_consistency(self):
   1.268 +        for filename, source, statements in self._get_test_files():
   1.269 +            reformatted = statements.to_source()
   1.270 +
   1.271 +            # We should be able to parse the reformatted source fine.
   1.272 +            new_statements = parsestring(reformatted, filename)
   1.273 +
   1.274 +            # If we do the formatting again, the representation shouldn't
   1.275 +            # change. i.e. the only lossy change should be the original
   1.276 +            # (whitespace and some semantics aren't preserved).
   1.277 +            reformatted_again = new_statements.to_source()
   1.278 +            self.assertEqual(reformatted, reformatted_again,
   1.279 +                '%s has lossless reformat.' % filename)
   1.280 +
   1.281 +            self.assertEqual(len(statements), len(new_statements))
   1.282 +
   1.283 +            for i in xrange(0, len(statements)):
   1.284 +                original = statements[i]
   1.285 +                formatted = new_statements[i]
   1.286 +
   1.287 +                self.assertEqual(original, formatted, '%s %d: %s != %s' % (filename,
   1.288 +                    i, original, formatted))
   1.289 +
   1.290 +if __name__ == '__main__':
   1.291 +    logging.basicConfig(level=logging.DEBUG)
   1.292 +    unittest.main()

mercurial