michael@0: # This file contains test code for the formatting of parsed statements back to michael@0: # make file "source." It essentially verifies to to_source() functions michael@0: # scattered across the tree. michael@0: michael@0: import glob michael@0: import logging michael@0: import os.path michael@0: import unittest michael@0: michael@0: from pymake.data import Expansion michael@0: from pymake.data import StringExpansion michael@0: from pymake.functions import BasenameFunction michael@0: from pymake.functions import SubstitutionRef michael@0: from pymake.functions import VariableRef michael@0: from pymake.functions import WordlistFunction michael@0: from pymake.parserdata import Include michael@0: from pymake.parserdata import SetVariable michael@0: from pymake.parser import parsestring michael@0: from pymake.parser import SyntaxError michael@0: michael@0: class TestBase(unittest.TestCase): michael@0: pass michael@0: michael@0: class VariableRefTest(TestBase): michael@0: def test_string_name(self): michael@0: e = StringExpansion('foo', None) michael@0: v = VariableRef(None, e) michael@0: michael@0: self.assertEqual(v.to_source(), '$(foo)') michael@0: michael@0: def test_special_variable(self): michael@0: e = StringExpansion('<', None) michael@0: v = VariableRef(None, e) michael@0: michael@0: self.assertEqual(v.to_source(), '$<') michael@0: michael@0: def test_expansion_simple(self): michael@0: e = Expansion() michael@0: e.appendstr('foo') michael@0: e.appendstr('bar') michael@0: michael@0: v = VariableRef(None, e) michael@0: michael@0: self.assertEqual(v.to_source(), '$(foobar)') michael@0: michael@0: class StandardFunctionTest(TestBase): michael@0: def test_basename(self): michael@0: e1 = StringExpansion('foo', None) michael@0: v = VariableRef(None, e1) michael@0: e2 = Expansion(None) michael@0: e2.appendfunc(v) michael@0: michael@0: b = BasenameFunction(None) michael@0: b.append(e2) michael@0: michael@0: self.assertEqual(b.to_source(), '$(basename $(foo))') michael@0: michael@0: def test_wordlist(self): michael@0: e1 = StringExpansion('foo', None) michael@0: e2 = StringExpansion('bar ', None) michael@0: e3 = StringExpansion(' baz', None) michael@0: michael@0: w = WordlistFunction(None) michael@0: w.append(e1) michael@0: w.append(e2) michael@0: w.append(e3) michael@0: michael@0: self.assertEqual(w.to_source(), '$(wordlist foo,bar , baz)') michael@0: michael@0: def test_curly_brackets(self): michael@0: e1 = Expansion(None) michael@0: e1.appendstr('foo') michael@0: michael@0: e2 = Expansion(None) michael@0: e2.appendstr('foo ( bar') michael@0: michael@0: f = WordlistFunction(None) michael@0: f.append(e1) michael@0: f.append(e2) michael@0: michael@0: self.assertEqual(f.to_source(), '${wordlist foo,foo ( bar}') michael@0: michael@0: class StringExpansionTest(TestBase): michael@0: def test_simple(self): michael@0: e = StringExpansion('foobar', None) michael@0: self.assertEqual(e.to_source(), 'foobar') michael@0: michael@0: e = StringExpansion('$var', None) michael@0: self.assertEqual(e.to_source(), '$var') michael@0: michael@0: def test_escaping(self): michael@0: e = StringExpansion('$var', None) michael@0: self.assertEqual(e.to_source(escape_variables=True), '$$var') michael@0: michael@0: e = StringExpansion('this is # not a comment', None) michael@0: self.assertEqual(e.to_source(escape_comments=True), michael@0: 'this is \# not a comment') michael@0: michael@0: def test_empty(self): michael@0: e = StringExpansion('', None) michael@0: self.assertEqual(e.to_source(), '') michael@0: michael@0: e = StringExpansion(' ', None) michael@0: self.assertEqual(e.to_source(), ' ') michael@0: michael@0: class ExpansionTest(TestBase): michael@0: def test_single_string(self): michael@0: e = Expansion() michael@0: e.appendstr('foo') michael@0: michael@0: self.assertEqual(e.to_source(), 'foo') michael@0: michael@0: def test_multiple_strings(self): michael@0: e = Expansion() michael@0: e.appendstr('hello') michael@0: e.appendstr('world') michael@0: michael@0: self.assertEqual(e.to_source(), 'helloworld') michael@0: michael@0: def test_string_escape(self): michael@0: e = Expansion() michael@0: e.appendstr('$var') michael@0: self.assertEqual(e.to_source(), '$var') michael@0: self.assertEqual(e.to_source(escape_variables=True), '$$var') michael@0: michael@0: e = Expansion() michael@0: e.appendstr('foo') michael@0: e.appendstr(' $bar') michael@0: self.assertEqual(e.to_source(escape_variables=True), 'foo $$bar') michael@0: michael@0: class SubstitutionRefTest(TestBase): michael@0: def test_simple(self): michael@0: name = StringExpansion('foo', None) michael@0: c = StringExpansion('%.c', None) michael@0: o = StringExpansion('%.o', None) michael@0: s = SubstitutionRef(None, name, c, o) michael@0: michael@0: self.assertEqual(s.to_source(), '$(foo:%.c=%.o)') michael@0: michael@0: class SetVariableTest(TestBase): michael@0: def test_simple(self): michael@0: v = SetVariable(StringExpansion('foo', None), '=', 'bar', None, None) michael@0: self.assertEqual(v.to_source(), 'foo = bar') michael@0: michael@0: def test_multiline(self): michael@0: s = 'hello\nworld' michael@0: foo = StringExpansion('FOO', None) michael@0: michael@0: v = SetVariable(foo, '=', s, None, None) michael@0: michael@0: self.assertEqual(v.to_source(), 'define FOO\nhello\nworld\nendef') michael@0: michael@0: def test_multiline_immediate(self): michael@0: source = 'define FOO :=\nhello\nworld\nendef' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements.to_source(), source) michael@0: michael@0: def test_target_specific(self): michael@0: foo = StringExpansion('FOO', None) michael@0: bar = StringExpansion('BAR', None) michael@0: michael@0: v = SetVariable(foo, '+=', 'value', None, bar) michael@0: michael@0: self.assertEqual(v.to_source(), 'BAR: FOO += value') michael@0: michael@0: class IncludeTest(TestBase): michael@0: def test_include(self): michael@0: e = StringExpansion('rules.mk', None) michael@0: i = Include(e, True, False) michael@0: self.assertEqual(i.to_source(), 'include rules.mk') michael@0: michael@0: i = Include(e, False, False) michael@0: self.assertEqual(i.to_source(), '-include rules.mk') michael@0: michael@0: class IfdefTest(TestBase): michael@0: def test_simple(self): michael@0: source = 'ifdef FOO\nbar := $(value)\nendif' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements[0].to_source(), source) michael@0: michael@0: def test_nested(self): michael@0: source = 'ifdef FOO\nifdef BAR\nhello = world\nendif\nendif' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements[0].to_source(), source) michael@0: michael@0: def test_negation(self): michael@0: source = 'ifndef FOO\nbar += value\nendif' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements[0].to_source(), source) michael@0: michael@0: class IfeqTest(TestBase): michael@0: def test_simple(self): michael@0: source = 'ifeq ($(foo),bar)\nhello = $(world)\nendif' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements[0].to_source(), source) michael@0: michael@0: def test_negation(self): michael@0: source = 'ifneq (foo,bar)\nhello = world\nendif' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements.to_source(), source) michael@0: michael@0: class ConditionBlocksTest(TestBase): michael@0: def test_mixed_conditions(self): michael@0: source = 'ifdef FOO\nifeq ($(FOO),bar)\nvar += $(value)\nendif\nendif' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements.to_source(), source) michael@0: michael@0: def test_extra_statements(self): michael@0: source = 'ifdef FOO\nF := 1\nifdef BAR\nB += 1\nendif\nC = 1\nendif' michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements.to_source(), source) michael@0: michael@0: def test_whitespace_preservation(self): michael@0: source = "ifeq ' x' 'x '\n$(error stripping)\nendif" michael@0: michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements.to_source(), source) michael@0: michael@0: source = 'ifneq (x , x)\n$(error stripping)\nendif' michael@0: statements = parsestring(source, 'foo.mk') michael@0: self.assertEqual(statements.to_source(), michael@0: 'ifneq (x,x)\n$(error stripping)\nendif') michael@0: michael@0: class MakefileCorupusTest(TestBase): michael@0: """Runs the make files from the pymake corpus through the formatter. michael@0: michael@0: All the above tests are child's play compared to this. michael@0: """ michael@0: michael@0: # Our reformatting isn't perfect. We ignore files with known failures until michael@0: # we make them work. michael@0: # TODO Address these formatting corner cases. michael@0: _IGNORE_FILES = [ michael@0: # We are thrown off by backslashes at end of lines. michael@0: 'comment-parsing.mk', michael@0: 'escape-chars.mk', michael@0: 'include-notfound.mk', michael@0: ] michael@0: michael@0: def _get_test_files(self): michael@0: ourdir = os.path.dirname(os.path.abspath(__file__)) michael@0: michael@0: for makefile in glob.glob(os.path.join(ourdir, '*.mk')): michael@0: if os.path.basename(makefile) in self._IGNORE_FILES: michael@0: continue michael@0: michael@0: source = None michael@0: with open(makefile, 'rU') as fh: michael@0: source = fh.read() michael@0: michael@0: try: michael@0: yield (makefile, source, parsestring(source, makefile)) michael@0: except SyntaxError: michael@0: continue michael@0: michael@0: def test_reparse_consistency(self): michael@0: for filename, source, statements in self._get_test_files(): michael@0: reformatted = statements.to_source() michael@0: michael@0: # We should be able to parse the reformatted source fine. michael@0: new_statements = parsestring(reformatted, filename) michael@0: michael@0: # If we do the formatting again, the representation shouldn't michael@0: # change. i.e. the only lossy change should be the original michael@0: # (whitespace and some semantics aren't preserved). michael@0: reformatted_again = new_statements.to_source() michael@0: self.assertEqual(reformatted, reformatted_again, michael@0: '%s has lossless reformat.' % filename) michael@0: michael@0: self.assertEqual(len(statements), len(new_statements)) michael@0: michael@0: for i in xrange(0, len(statements)): michael@0: original = statements[i] michael@0: formatted = new_statements[i] michael@0: michael@0: self.assertEqual(original, formatted, '%s %d: %s != %s' % (filename, michael@0: i, original, formatted)) michael@0: michael@0: if __name__ == '__main__': michael@0: logging.basicConfig(level=logging.DEBUG) michael@0: unittest.main()