Wed, 31 Dec 2014 07:16:47 +0100
Revert simplistic fix pending revisit of Mozilla integration attempt.
michael@0 | 1 | # This file contains test code for the formatting of parsed statements back to |
michael@0 | 2 | # make file "source." It essentially verifies to to_source() functions |
michael@0 | 3 | # scattered across the tree. |
michael@0 | 4 | |
michael@0 | 5 | import glob |
michael@0 | 6 | import logging |
michael@0 | 7 | import os.path |
michael@0 | 8 | import unittest |
michael@0 | 9 | |
michael@0 | 10 | from pymake.data import Expansion |
michael@0 | 11 | from pymake.data import StringExpansion |
michael@0 | 12 | from pymake.functions import BasenameFunction |
michael@0 | 13 | from pymake.functions import SubstitutionRef |
michael@0 | 14 | from pymake.functions import VariableRef |
michael@0 | 15 | from pymake.functions import WordlistFunction |
michael@0 | 16 | from pymake.parserdata import Include |
michael@0 | 17 | from pymake.parserdata import SetVariable |
michael@0 | 18 | from pymake.parser import parsestring |
michael@0 | 19 | from pymake.parser import SyntaxError |
michael@0 | 20 | |
michael@0 | 21 | class TestBase(unittest.TestCase): |
michael@0 | 22 | pass |
michael@0 | 23 | |
michael@0 | 24 | class VariableRefTest(TestBase): |
michael@0 | 25 | def test_string_name(self): |
michael@0 | 26 | e = StringExpansion('foo', None) |
michael@0 | 27 | v = VariableRef(None, e) |
michael@0 | 28 | |
michael@0 | 29 | self.assertEqual(v.to_source(), '$(foo)') |
michael@0 | 30 | |
michael@0 | 31 | def test_special_variable(self): |
michael@0 | 32 | e = StringExpansion('<', None) |
michael@0 | 33 | v = VariableRef(None, e) |
michael@0 | 34 | |
michael@0 | 35 | self.assertEqual(v.to_source(), '$<') |
michael@0 | 36 | |
michael@0 | 37 | def test_expansion_simple(self): |
michael@0 | 38 | e = Expansion() |
michael@0 | 39 | e.appendstr('foo') |
michael@0 | 40 | e.appendstr('bar') |
michael@0 | 41 | |
michael@0 | 42 | v = VariableRef(None, e) |
michael@0 | 43 | |
michael@0 | 44 | self.assertEqual(v.to_source(), '$(foobar)') |
michael@0 | 45 | |
michael@0 | 46 | class StandardFunctionTest(TestBase): |
michael@0 | 47 | def test_basename(self): |
michael@0 | 48 | e1 = StringExpansion('foo', None) |
michael@0 | 49 | v = VariableRef(None, e1) |
michael@0 | 50 | e2 = Expansion(None) |
michael@0 | 51 | e2.appendfunc(v) |
michael@0 | 52 | |
michael@0 | 53 | b = BasenameFunction(None) |
michael@0 | 54 | b.append(e2) |
michael@0 | 55 | |
michael@0 | 56 | self.assertEqual(b.to_source(), '$(basename $(foo))') |
michael@0 | 57 | |
michael@0 | 58 | def test_wordlist(self): |
michael@0 | 59 | e1 = StringExpansion('foo', None) |
michael@0 | 60 | e2 = StringExpansion('bar ', None) |
michael@0 | 61 | e3 = StringExpansion(' baz', None) |
michael@0 | 62 | |
michael@0 | 63 | w = WordlistFunction(None) |
michael@0 | 64 | w.append(e1) |
michael@0 | 65 | w.append(e2) |
michael@0 | 66 | w.append(e3) |
michael@0 | 67 | |
michael@0 | 68 | self.assertEqual(w.to_source(), '$(wordlist foo,bar , baz)') |
michael@0 | 69 | |
michael@0 | 70 | def test_curly_brackets(self): |
michael@0 | 71 | e1 = Expansion(None) |
michael@0 | 72 | e1.appendstr('foo') |
michael@0 | 73 | |
michael@0 | 74 | e2 = Expansion(None) |
michael@0 | 75 | e2.appendstr('foo ( bar') |
michael@0 | 76 | |
michael@0 | 77 | f = WordlistFunction(None) |
michael@0 | 78 | f.append(e1) |
michael@0 | 79 | f.append(e2) |
michael@0 | 80 | |
michael@0 | 81 | self.assertEqual(f.to_source(), '${wordlist foo,foo ( bar}') |
michael@0 | 82 | |
michael@0 | 83 | class StringExpansionTest(TestBase): |
michael@0 | 84 | def test_simple(self): |
michael@0 | 85 | e = StringExpansion('foobar', None) |
michael@0 | 86 | self.assertEqual(e.to_source(), 'foobar') |
michael@0 | 87 | |
michael@0 | 88 | e = StringExpansion('$var', None) |
michael@0 | 89 | self.assertEqual(e.to_source(), '$var') |
michael@0 | 90 | |
michael@0 | 91 | def test_escaping(self): |
michael@0 | 92 | e = StringExpansion('$var', None) |
michael@0 | 93 | self.assertEqual(e.to_source(escape_variables=True), '$$var') |
michael@0 | 94 | |
michael@0 | 95 | e = StringExpansion('this is # not a comment', None) |
michael@0 | 96 | self.assertEqual(e.to_source(escape_comments=True), |
michael@0 | 97 | 'this is \# not a comment') |
michael@0 | 98 | |
michael@0 | 99 | def test_empty(self): |
michael@0 | 100 | e = StringExpansion('', None) |
michael@0 | 101 | self.assertEqual(e.to_source(), '') |
michael@0 | 102 | |
michael@0 | 103 | e = StringExpansion(' ', None) |
michael@0 | 104 | self.assertEqual(e.to_source(), ' ') |
michael@0 | 105 | |
michael@0 | 106 | class ExpansionTest(TestBase): |
michael@0 | 107 | def test_single_string(self): |
michael@0 | 108 | e = Expansion() |
michael@0 | 109 | e.appendstr('foo') |
michael@0 | 110 | |
michael@0 | 111 | self.assertEqual(e.to_source(), 'foo') |
michael@0 | 112 | |
michael@0 | 113 | def test_multiple_strings(self): |
michael@0 | 114 | e = Expansion() |
michael@0 | 115 | e.appendstr('hello') |
michael@0 | 116 | e.appendstr('world') |
michael@0 | 117 | |
michael@0 | 118 | self.assertEqual(e.to_source(), 'helloworld') |
michael@0 | 119 | |
michael@0 | 120 | def test_string_escape(self): |
michael@0 | 121 | e = Expansion() |
michael@0 | 122 | e.appendstr('$var') |
michael@0 | 123 | self.assertEqual(e.to_source(), '$var') |
michael@0 | 124 | self.assertEqual(e.to_source(escape_variables=True), '$$var') |
michael@0 | 125 | |
michael@0 | 126 | e = Expansion() |
michael@0 | 127 | e.appendstr('foo') |
michael@0 | 128 | e.appendstr(' $bar') |
michael@0 | 129 | self.assertEqual(e.to_source(escape_variables=True), 'foo $$bar') |
michael@0 | 130 | |
michael@0 | 131 | class SubstitutionRefTest(TestBase): |
michael@0 | 132 | def test_simple(self): |
michael@0 | 133 | name = StringExpansion('foo', None) |
michael@0 | 134 | c = StringExpansion('%.c', None) |
michael@0 | 135 | o = StringExpansion('%.o', None) |
michael@0 | 136 | s = SubstitutionRef(None, name, c, o) |
michael@0 | 137 | |
michael@0 | 138 | self.assertEqual(s.to_source(), '$(foo:%.c=%.o)') |
michael@0 | 139 | |
michael@0 | 140 | class SetVariableTest(TestBase): |
michael@0 | 141 | def test_simple(self): |
michael@0 | 142 | v = SetVariable(StringExpansion('foo', None), '=', 'bar', None, None) |
michael@0 | 143 | self.assertEqual(v.to_source(), 'foo = bar') |
michael@0 | 144 | |
michael@0 | 145 | def test_multiline(self): |
michael@0 | 146 | s = 'hello\nworld' |
michael@0 | 147 | foo = StringExpansion('FOO', None) |
michael@0 | 148 | |
michael@0 | 149 | v = SetVariable(foo, '=', s, None, None) |
michael@0 | 150 | |
michael@0 | 151 | self.assertEqual(v.to_source(), 'define FOO\nhello\nworld\nendef') |
michael@0 | 152 | |
michael@0 | 153 | def test_multiline_immediate(self): |
michael@0 | 154 | source = 'define FOO :=\nhello\nworld\nendef' |
michael@0 | 155 | |
michael@0 | 156 | statements = parsestring(source, 'foo.mk') |
michael@0 | 157 | self.assertEqual(statements.to_source(), source) |
michael@0 | 158 | |
michael@0 | 159 | def test_target_specific(self): |
michael@0 | 160 | foo = StringExpansion('FOO', None) |
michael@0 | 161 | bar = StringExpansion('BAR', None) |
michael@0 | 162 | |
michael@0 | 163 | v = SetVariable(foo, '+=', 'value', None, bar) |
michael@0 | 164 | |
michael@0 | 165 | self.assertEqual(v.to_source(), 'BAR: FOO += value') |
michael@0 | 166 | |
michael@0 | 167 | class IncludeTest(TestBase): |
michael@0 | 168 | def test_include(self): |
michael@0 | 169 | e = StringExpansion('rules.mk', None) |
michael@0 | 170 | i = Include(e, True, False) |
michael@0 | 171 | self.assertEqual(i.to_source(), 'include rules.mk') |
michael@0 | 172 | |
michael@0 | 173 | i = Include(e, False, False) |
michael@0 | 174 | self.assertEqual(i.to_source(), '-include rules.mk') |
michael@0 | 175 | |
michael@0 | 176 | class IfdefTest(TestBase): |
michael@0 | 177 | def test_simple(self): |
michael@0 | 178 | source = 'ifdef FOO\nbar := $(value)\nendif' |
michael@0 | 179 | |
michael@0 | 180 | statements = parsestring(source, 'foo.mk') |
michael@0 | 181 | self.assertEqual(statements[0].to_source(), source) |
michael@0 | 182 | |
michael@0 | 183 | def test_nested(self): |
michael@0 | 184 | source = 'ifdef FOO\nifdef BAR\nhello = world\nendif\nendif' |
michael@0 | 185 | |
michael@0 | 186 | statements = parsestring(source, 'foo.mk') |
michael@0 | 187 | self.assertEqual(statements[0].to_source(), source) |
michael@0 | 188 | |
michael@0 | 189 | def test_negation(self): |
michael@0 | 190 | source = 'ifndef FOO\nbar += value\nendif' |
michael@0 | 191 | |
michael@0 | 192 | statements = parsestring(source, 'foo.mk') |
michael@0 | 193 | self.assertEqual(statements[0].to_source(), source) |
michael@0 | 194 | |
michael@0 | 195 | class IfeqTest(TestBase): |
michael@0 | 196 | def test_simple(self): |
michael@0 | 197 | source = 'ifeq ($(foo),bar)\nhello = $(world)\nendif' |
michael@0 | 198 | |
michael@0 | 199 | statements = parsestring(source, 'foo.mk') |
michael@0 | 200 | self.assertEqual(statements[0].to_source(), source) |
michael@0 | 201 | |
michael@0 | 202 | def test_negation(self): |
michael@0 | 203 | source = 'ifneq (foo,bar)\nhello = world\nendif' |
michael@0 | 204 | |
michael@0 | 205 | statements = parsestring(source, 'foo.mk') |
michael@0 | 206 | self.assertEqual(statements.to_source(), source) |
michael@0 | 207 | |
michael@0 | 208 | class ConditionBlocksTest(TestBase): |
michael@0 | 209 | def test_mixed_conditions(self): |
michael@0 | 210 | source = 'ifdef FOO\nifeq ($(FOO),bar)\nvar += $(value)\nendif\nendif' |
michael@0 | 211 | |
michael@0 | 212 | statements = parsestring(source, 'foo.mk') |
michael@0 | 213 | self.assertEqual(statements.to_source(), source) |
michael@0 | 214 | |
michael@0 | 215 | def test_extra_statements(self): |
michael@0 | 216 | source = 'ifdef FOO\nF := 1\nifdef BAR\nB += 1\nendif\nC = 1\nendif' |
michael@0 | 217 | |
michael@0 | 218 | statements = parsestring(source, 'foo.mk') |
michael@0 | 219 | self.assertEqual(statements.to_source(), source) |
michael@0 | 220 | |
michael@0 | 221 | def test_whitespace_preservation(self): |
michael@0 | 222 | source = "ifeq ' x' 'x '\n$(error stripping)\nendif" |
michael@0 | 223 | |
michael@0 | 224 | statements = parsestring(source, 'foo.mk') |
michael@0 | 225 | self.assertEqual(statements.to_source(), source) |
michael@0 | 226 | |
michael@0 | 227 | source = 'ifneq (x , x)\n$(error stripping)\nendif' |
michael@0 | 228 | statements = parsestring(source, 'foo.mk') |
michael@0 | 229 | self.assertEqual(statements.to_source(), |
michael@0 | 230 | 'ifneq (x,x)\n$(error stripping)\nendif') |
michael@0 | 231 | |
michael@0 | 232 | class MakefileCorupusTest(TestBase): |
michael@0 | 233 | """Runs the make files from the pymake corpus through the formatter. |
michael@0 | 234 | |
michael@0 | 235 | All the above tests are child's play compared to this. |
michael@0 | 236 | """ |
michael@0 | 237 | |
michael@0 | 238 | # Our reformatting isn't perfect. We ignore files with known failures until |
michael@0 | 239 | # we make them work. |
michael@0 | 240 | # TODO Address these formatting corner cases. |
michael@0 | 241 | _IGNORE_FILES = [ |
michael@0 | 242 | # We are thrown off by backslashes at end of lines. |
michael@0 | 243 | 'comment-parsing.mk', |
michael@0 | 244 | 'escape-chars.mk', |
michael@0 | 245 | 'include-notfound.mk', |
michael@0 | 246 | ] |
michael@0 | 247 | |
michael@0 | 248 | def _get_test_files(self): |
michael@0 | 249 | ourdir = os.path.dirname(os.path.abspath(__file__)) |
michael@0 | 250 | |
michael@0 | 251 | for makefile in glob.glob(os.path.join(ourdir, '*.mk')): |
michael@0 | 252 | if os.path.basename(makefile) in self._IGNORE_FILES: |
michael@0 | 253 | continue |
michael@0 | 254 | |
michael@0 | 255 | source = None |
michael@0 | 256 | with open(makefile, 'rU') as fh: |
michael@0 | 257 | source = fh.read() |
michael@0 | 258 | |
michael@0 | 259 | try: |
michael@0 | 260 | yield (makefile, source, parsestring(source, makefile)) |
michael@0 | 261 | except SyntaxError: |
michael@0 | 262 | continue |
michael@0 | 263 | |
michael@0 | 264 | def test_reparse_consistency(self): |
michael@0 | 265 | for filename, source, statements in self._get_test_files(): |
michael@0 | 266 | reformatted = statements.to_source() |
michael@0 | 267 | |
michael@0 | 268 | # We should be able to parse the reformatted source fine. |
michael@0 | 269 | new_statements = parsestring(reformatted, filename) |
michael@0 | 270 | |
michael@0 | 271 | # If we do the formatting again, the representation shouldn't |
michael@0 | 272 | # change. i.e. the only lossy change should be the original |
michael@0 | 273 | # (whitespace and some semantics aren't preserved). |
michael@0 | 274 | reformatted_again = new_statements.to_source() |
michael@0 | 275 | self.assertEqual(reformatted, reformatted_again, |
michael@0 | 276 | '%s has lossless reformat.' % filename) |
michael@0 | 277 | |
michael@0 | 278 | self.assertEqual(len(statements), len(new_statements)) |
michael@0 | 279 | |
michael@0 | 280 | for i in xrange(0, len(statements)): |
michael@0 | 281 | original = statements[i] |
michael@0 | 282 | formatted = new_statements[i] |
michael@0 | 283 | |
michael@0 | 284 | self.assertEqual(original, formatted, '%s %d: %s != %s' % (filename, |
michael@0 | 285 | i, original, formatted)) |
michael@0 | 286 | |
michael@0 | 287 | if __name__ == '__main__': |
michael@0 | 288 | logging.basicConfig(level=logging.DEBUG) |
michael@0 | 289 | unittest.main() |