Bug 1270446 - Allow to combine multiple FakeCompiler definitions. r?chmanchester draft
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 22 Apr 2016 11:34:02 +0900
changeset 363767 10990b6a1f4a0260716068fe64ef198fb650b2eb
parent 363766 c8289570b312a8794676c268dffb4ea81c47d947
child 363768 8cc149414b965c38f0c0785bd11d5dcceb85cd8a
push id17288
push userbmo:mh+mozilla@glandium.org
push dateThu, 05 May 2016 11:06:21 +0000
reviewerschmanchester
bugs1270446
milestone49.0a1
Bug 1270446 - Allow to combine multiple FakeCompiler definitions. r?chmanchester
python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py
--- a/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py
+++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py
@@ -108,20 +108,20 @@ class TestCompilerPreprocessor(unittest.
             #endif
         '''))
         input.name = 'foo'
         pp.do_include(input)
 
         self.assertEquals('IFDEF_A\nIF_A\nIF_B\nIF_NOT_C\n', pp.out.getvalue())
 
 
-class FakeCompiler(object):
+class FakeCompiler(dict):
     '''Defines a fake compiler for use in toolchain tests below.
 
-    The definition given when creating an instance can have one of two
+    The definitions given when creating an instance can have one of two
     forms:
     - a dict giving preprocessor symbols and their respective value, e.g.
         { '__GNUC__': 4, '__STDC__': 1 }
     - a dict associating flags to preprocessor symbols. An entry for `None`
       is required in this case. Those are the baseline preprocessor symbols.
       Additional entries describe additional flags to set or existing flags
       to unset (with a value of `False`).
         {
@@ -134,51 +134,63 @@ class FakeCompiler(object):
       __GNUC__, __STDC__, and __STDC_VERSION__ (__STRICT_ANSI__ would be
       unset).
       It is also possible to have different symbols depending on the source
       file extension. In this case, the key is '*.ext'. e.g.
         {
           '*.c': { '__STDC__': 1 },
           '*.cpp': { '__cplusplus': '199711L' },
         }
+
+    All the given definitions are merged together.
+
+    A FakeCompiler instance itself can be used as a definition to create
+    another FakeCompiler.
+
+    For convenience, FakeCompiler instances can be added (+) to one another.
     '''
-    def __init__(self, definition):
-        if definition.get(None) is None:
-            definition = {None: definition}
-        self._definition = definition
+    def __init__(self, *definitions):
+        for definition in definitions:
+            if all(not isinstance(d, dict) for d in definition.itervalues()):
+                definition = {None: definition}
+            for key, value in definition.iteritems():
+                self.setdefault(key, {}).update(value)
 
     def __call__(self, stdin, args):
         files = [arg for arg in args if not arg.startswith('-')]
         flags = [arg for arg in args if arg.startswith('-')]
         if '-E' in flags:
             assert len(files) == 1
             file = files[0]
-            pp = CompilerPreprocessor(self._definition[None])
+            pp = CompilerPreprocessor(self[None])
 
             def apply_defn(defn):
                 for k, v in defn.iteritems():
                     if v is False:
                         if k in pp.context:
                             del pp.context[k]
                     else:
                         pp.context[k] = v
 
-            for glob, defn in self._definition.iteritems():
+            for glob, defn in self.iteritems():
                 if glob and not glob.startswith('-') and fnmatch(file, glob):
                     apply_defn(defn)
 
             for flag in flags:
-                apply_defn(self._definition.get(flag, {}))
+                apply_defn(self.get(flag, {}))
 
             pp.out = StringIO()
             pp.do_include(file)
             return 0, pp.out.getvalue(), ''
 
         return 1, '', ''
 
+    def __add__(self, other):
+        return FakeCompiler(self, other)
+
 
 class TestFakeCompiler(unittest.TestCase):
     def test_fake_compiler(self):
         with MockedOpen({
             'file': 'A B C',
             'file.c': 'A B C',
         }):
             compiler = FakeCompiler({
@@ -223,11 +235,95 @@ class TestFakeCompiler(unittest.TestCase
                               (0, '1 B bar', ''))
             self.assertEquals(compiler(None, ['-E', '-qux', '-bar', 'file']),
                               (0, '1 bar bar', ''))
             self.assertEquals(compiler(None, ['-E', 'file.c']),
                               (0, '1 42 C', ''))
             self.assertEquals(compiler(None, ['-E', '-bar', 'file.c']),
                               (0, '1 bar bar', ''))
 
+    def test_multiple_definitions(self):
+        compiler = FakeCompiler({
+            'A': 1,
+            'B': 2,
+        }, {
+            'C': 3,
+        })
+
+        self.assertEquals(compiler, {
+            None: {
+                'A': 1,
+                'B': 2,
+                'C': 3,
+            },
+        })
+        compiler = FakeCompiler({
+            'A': 1,
+            'B': 2,
+        }, {
+            'B': 4,
+            'C': 3,
+        })
+
+        self.assertEquals(compiler, {
+            None: {
+                'A': 1,
+                'B': 4,
+                'C': 3,
+            },
+        })
+        compiler = FakeCompiler({
+            'A': 1,
+            'B': 2,
+        }, {
+            None: {
+                'B': 4,
+                'C': 3,
+            },
+            '-foo': {
+                'D': 5,
+            },
+        })
+
+        self.assertEquals(compiler, {
+            None: {
+                'A': 1,
+                'B': 4,
+                'C': 3,
+            },
+            '-foo': {
+                'D': 5,
+            },
+        })
+
+        compiler = FakeCompiler({
+            None: {
+                'A': 1,
+                'B': 2,
+            },
+            '-foo': {
+                'D': 5,
+            },
+        }, {
+            '-foo': {
+                'D': 5,
+            },
+            '-bar': {
+                'E': 6,
+            },
+        })
+
+        self.assertEquals(compiler, {
+            None: {
+                'A': 1,
+                'B': 2,
+            },
+            '-foo': {
+                'D': 5,
+            },
+            '-bar': {
+                'E': 6,
+            },
+        })
+
 
 if __name__ == '__main__':
     main()