1
# This file contains test code for the formatting of parsed statements back to
2
# make file "source." It essentially verifies to to_source() functions
3
# scattered across the tree.
10
from pymake.data import Expansion
11
from pymake.data import StringExpansion
12
from pymake.functions import BasenameFunction
13
from pymake.functions import SubstitutionRef
14
from pymake.functions import VariableRef
15
from pymake.functions import WordlistFunction
16
from pymake.parserdata import Include
17
from pymake.parserdata import SetVariable
18
from pymake.parser import parsestring
19
from pymake.parser import SyntaxError
21
class TestBase(unittest.TestCase):
24
class VariableRefTest(TestBase):
25
def test_string_name(self):
26
e = StringExpansion('foo', None)
27
v = VariableRef(None, e)
29
self.assertEqual(v.to_source(), '$(foo)')
31
def test_special_variable(self):
32
e = StringExpansion('<', None)
33
v = VariableRef(None, e)
35
self.assertEqual(v.to_source(), '$<')
37
def test_expansion_simple(self):
42
v = VariableRef(None, e)
44
self.assertEqual(v.to_source(), '$(foobar)')
46
class StandardFunctionTest(TestBase):
47
def test_basename(self):
48
e1 = StringExpansion('foo', None)
49
v = VariableRef(None, e1)
53
b = BasenameFunction(None)
56
self.assertEqual(b.to_source(), '$(basename $(foo))')
58
def test_wordlist(self):
59
e1 = StringExpansion('foo', None)
60
e2 = StringExpansion('bar ', None)
61
e3 = StringExpansion(' baz', None)
63
w = WordlistFunction(None)
68
self.assertEqual(w.to_source(), '$(wordlist foo,bar , baz)')
70
def test_curly_brackets(self):
75
e2.appendstr('foo ( bar')
77
f = WordlistFunction(None)
81
self.assertEqual(f.to_source(), '${wordlist foo,foo ( bar}')
83
class StringExpansionTest(TestBase):
84
def test_simple(self):
85
e = StringExpansion('foobar', None)
86
self.assertEqual(e.to_source(), 'foobar')
88
e = StringExpansion('$var', None)
89
self.assertEqual(e.to_source(), '$var')
91
def test_escaping(self):
92
e = StringExpansion('$var', None)
93
self.assertEqual(e.to_source(escape_variables=True), '$$var')
95
e = StringExpansion('this is # not a comment', None)
96
self.assertEqual(e.to_source(escape_comments=True),
97
'this is \# not a comment')
100
e = StringExpansion('', None)
101
self.assertEqual(e.to_source(), '')
103
e = StringExpansion(' ', None)
104
self.assertEqual(e.to_source(), ' ')
106
class ExpansionTest(TestBase):
107
def test_single_string(self):
111
self.assertEqual(e.to_source(), 'foo')
113
def test_multiple_strings(self):
118
self.assertEqual(e.to_source(), 'helloworld')
120
def test_string_escape(self):
123
self.assertEqual(e.to_source(), '$var')
124
self.assertEqual(e.to_source(escape_variables=True), '$$var')
129
self.assertEqual(e.to_source(escape_variables=True), 'foo $$bar')
131
class SubstitutionRefTest(TestBase):
132
def test_simple(self):
133
name = StringExpansion('foo', None)
134
c = StringExpansion('%.c', None)
135
o = StringExpansion('%.o', None)
136
s = SubstitutionRef(None, name, c, o)
138
self.assertEqual(s.to_source(), '$(foo:%.c=%.o)')
140
class SetVariableTest(TestBase):
141
def test_simple(self):
142
v = SetVariable(StringExpansion('foo', None), '=', 'bar', None, None)
143
self.assertEqual(v.to_source(), 'foo = bar')
145
def test_multiline(self):
147
foo = StringExpansion('FOO', None)
149
v = SetVariable(foo, '=', s, None, None)
151
self.assertEqual(v.to_source(), 'define FOO\nhello\nworld\nendef')
153
def test_multiline_immediate(self):
154
source = 'define FOO :=\nhello\nworld\nendef'
156
statements = parsestring(source, 'foo.mk')
157
self.assertEqual(statements.to_source(), source)
159
def test_target_specific(self):
160
foo = StringExpansion('FOO', None)
161
bar = StringExpansion('BAR', None)
163
v = SetVariable(foo, '+=', 'value', None, bar)
165
self.assertEqual(v.to_source(), 'BAR: FOO += value')
167
class IncludeTest(TestBase):
168
def test_include(self):
169
e = StringExpansion('rules.mk', None)
170
i = Include(e, True, False)
171
self.assertEqual(i.to_source(), 'include rules.mk')
173
i = Include(e, False, False)
174
self.assertEqual(i.to_source(), '-include rules.mk')
176
class IfdefTest(TestBase):
177
def test_simple(self):
178
source = 'ifdef FOO\nbar := $(value)\nendif'
180
statements = parsestring(source, 'foo.mk')
181
self.assertEqual(statements[0].to_source(), source)
183
def test_nested(self):
184
source = 'ifdef FOO\nifdef BAR\nhello = world\nendif\nendif'
186
statements = parsestring(source, 'foo.mk')
187
self.assertEqual(statements[0].to_source(), source)
189
def test_negation(self):
190
source = 'ifndef FOO\nbar += value\nendif'
192
statements = parsestring(source, 'foo.mk')
193
self.assertEqual(statements[0].to_source(), source)
195
class IfeqTest(TestBase):
196
def test_simple(self):
197
source = 'ifeq ($(foo),bar)\nhello = $(world)\nendif'
199
statements = parsestring(source, 'foo.mk')
200
self.assertEqual(statements[0].to_source(), source)
202
def test_negation(self):
203
source = 'ifneq (foo,bar)\nhello = world\nendif'
205
statements = parsestring(source, 'foo.mk')
206
self.assertEqual(statements.to_source(), source)
208
class ConditionBlocksTest(TestBase):
209
def test_mixed_conditions(self):
210
source = 'ifdef FOO\nifeq ($(FOO),bar)\nvar += $(value)\nendif\nendif'
212
statements = parsestring(source, 'foo.mk')
213
self.assertEqual(statements.to_source(), source)
215
def test_extra_statements(self):
216
source = 'ifdef FOO\nF := 1\nifdef BAR\nB += 1\nendif\nC = 1\nendif'
218
statements = parsestring(source, 'foo.mk')
219
self.assertEqual(statements.to_source(), source)
221
def test_whitespace_preservation(self):
222
source = "ifeq ' x' 'x '\n$(error stripping)\nendif"
224
statements = parsestring(source, 'foo.mk')
225
self.assertEqual(statements.to_source(), source)
227
source = 'ifneq (x , x)\n$(error stripping)\nendif'
228
statements = parsestring(source, 'foo.mk')
229
self.assertEqual(statements.to_source(),
230
'ifneq (x,x)\n$(error stripping)\nendif')
232
class MakefileCorupusTest(TestBase):
233
"""Runs the make files from the pymake corpus through the formatter.
235
All the above tests are child's play compared to this.
238
# Our reformatting isn't perfect. We ignore files with known failures until
240
# TODO Address these formatting corner cases.
242
# We are thrown off by backslashes at end of lines.
243
'comment-parsing.mk',
245
'include-notfound.mk',
248
def _get_test_files(self):
249
ourdir = os.path.dirname(os.path.abspath(__file__))
251
for makefile in glob.glob(os.path.join(ourdir, '*.mk')):
252
if os.path.basename(makefile) in self._IGNORE_FILES:
256
with open(makefile, 'rU') as fh:
260
yield (makefile, source, parsestring(source, makefile))
264
def test_reparse_consistency(self):
265
for filename, source, statements in self._get_test_files():
266
reformatted = statements.to_source()
268
# We should be able to parse the reformatted source fine.
269
new_statements = parsestring(reformatted, filename)
271
# If we do the formatting again, the representation shouldn't
272
# change. i.e. the only lossy change should be the original
273
# (whitespace and some semantics aren't preserved).
274
reformatted_again = new_statements.to_source()
275
self.assertEqual(reformatted, reformatted_again,
276
'%s has lossless reformat.' % filename)
278
self.assertEqual(len(statements), len(new_statements))
280
for i in xrange(0, len(statements)):
281
original = statements[i]
282
formatted = new_statements[i]
284
self.assertEqual(original, formatted, '%s %d: %s != %s' % (filename,
285
i, original, formatted))
287
if __name__ == '__main__':
288
logging.basicConfig(level=logging.DEBUG)