Bug 1266343 - Change FindProgramSandbox to override which.which instead of replacing find_program. r=chmanchester
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 19 Apr 2016 16:32:38 +0900
changeset 332288 5919b9e90fc829b590800434016498f2c840d810
parent 332287 ad7344ced6c7956040cb09ba1840bf3296d4125a
child 332289 1c806c5bf368aebbd4af87ab2ee98b992b190e76
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschmanchester
bugs1266343
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1266343 - Change FindProgramSandbox to override which.which instead of replacing find_program. r=chmanchester Also make it more generic so that it can be reused for other tests.
python/mozbuild/mozbuild/test/configure/test_checks_configure.py
--- a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
@@ -10,57 +10,76 @@ import textwrap
 import unittest
 
 from mozunit import main
 
 from mozbuild.configure import (
     ConfigureError,
     ConfigureSandbox,
 )
-from mozbuild.util import exec_
+from mozbuild.util import (
+    exec_,
+    ReadOnlyNamespace,
+)
+from mozpack import path as mozpath
 
 from buildconfig import topsrcdir
+from which import WhichError
+
+
+class ConfigureTestVFS(object):
+    def __init__(self, paths):
+        self._paths = set(mozpath.abspath(p) for p in paths)
+
+    def exists(self, path):
+        return mozpath.abspath(path) in self._paths
+
+    def isfile(self, path):
+        return mozpath.abspath(path) in self._paths
 
 
-class FindProgramSandbox(ConfigureSandbox):
-    def __init__(self, *args, **kwargs):
-        super(FindProgramSandbox, self).__init__(*args, **kwargs)
+class ConfigureTestSandbox(ConfigureSandbox):
+    def __init__(self, paths, config, environ, *args, **kwargs):
+        self._search_path = environ.get('PATH', '').split(os.pathsep)
+
+        vfs = ConfigureTestVFS(paths)
 
-        # We could define self.find_program_impl and have it automatically
-        # declared, but then it wouldn't be available in the tested templates.
-        # We also need to use super().__setitem__ because ours would do
-        # nothing.
-        super(FindProgramSandbox, self).__setitem__(
-            'find_program', lambda x: self.find_program(x))
+        self.OS = ReadOnlyNamespace(path=ReadOnlyNamespace(**{
+            k: v if k not in ('exists', 'isfile')
+            else getattr(vfs, k)
+            for k, v in ConfigureSandbox.OS.path.__dict__.iteritems()
+        }))
+
+        super(ConfigureTestSandbox, self).__init__(config, environ, *args,
+                                                   **kwargs)
 
-    PROGRAMS = {
-        'known-a': '/usr/bin/known-a',
-        'known-b': '/usr/local/bin/known-b',
-        'known c': '/home/user/bin/known c',
-    }
-
-    for p in PROGRAMS.values():
-        PROGRAMS[p] = p
+    def _get_one_import(self, what):
+        if what == 'which.which':
+            return self.which
 
-    @staticmethod
-    def find_program(prog):
-        return FindProgramSandbox.PROGRAMS.get(prog)
+        if what == 'which':
+            return ReadOnlyNamespace(
+                which=self.which,
+                WhichError=WhichError,
+            )
 
-    def __setitem__(self, key, value):
-        # Avoid util.configure overwriting our mock find_program
-        if key == 'find_program':
-            return
+        return super(ConfigureTestSandbox, self)._get_one_import(what)
 
-        super(FindProgramSandbox, self).__setitem__(key, value)
+    def which(self, command):
+        for parent in self._search_path:
+            path = mozpath.join(parent, command)
+            if self.OS.path.exists(path):
+                return path
+        raise WhichError()
 
 
 class TestChecksConfigure(unittest.TestCase):
     def test_checking(self):
         out = StringIO()
-        sandbox = FindProgramSandbox({}, stdout=out, stderr=out)
+        sandbox = ConfigureSandbox({}, stdout=out, stderr=out)
         base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
         sandbox.include_file(os.path.join(base_dir, 'checks.configure'))
 
         exec_(textwrap.dedent('''
             @checking('for a thing')
             def foo(value):
                 return value
         '''), sandbox)
@@ -148,21 +167,33 @@ class TestChecksConfigure(unittest.TestC
         out.truncate(0)
         foo('foo')
         self.assertEqual(out.getvalue(), 'checking for a thing... foo\n')
 
         out.truncate(0)
         foo(['foo', 'bar'])
         self.assertEqual(out.getvalue(), 'checking for a thing... foo bar\n')
 
+    KNOWN_A = mozpath.abspath('/usr/bin/known-a')
+    KNOWN_B = mozpath.abspath('/usr/local/bin/known-b')
+    KNOWN_C = mozpath.abspath('/home/user/bin/known c')
+
     def get_result(self, command='', args=[], environ={},
                    prog='/bin/configure'):
         config = {}
         out = StringIO()
-        sandbox = FindProgramSandbox(config, environ, [prog] + args, out, out)
+        paths = (
+            self.KNOWN_A,
+            self.KNOWN_B,
+            self.KNOWN_C,
+        )
+        environ = dict(environ)
+        environ['PATH'] = os.pathsep.join(os.path.dirname(p) for p in paths)
+        sandbox = ConfigureTestSandbox(paths, config, environ, [prog] + args,
+                                       out, out)
         base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
         sandbox.include_file(os.path.join(base_dir, 'util.configure'))
         sandbox.include_file(os.path.join(base_dir, 'checks.configure'))
 
         status = 0
         try:
             exec_(command, sandbox)
             sandbox.run()
@@ -170,30 +201,30 @@ class TestChecksConfigure(unittest.TestC
             status = e.code
 
         return config, out.getvalue(), status
 
     def test_check_prog(self):
         config, out, status = self.get_result(
             'check_prog("FOO", ("known-a",))')
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'FOO': '/usr/bin/known-a'})
-        self.assertEqual(out, 'checking for foo... /usr/bin/known-a\n')
+        self.assertEqual(config, {'FOO': self.KNOWN_A})
+        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)
 
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown", "known-b", "known c"))')
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'FOO': '/usr/local/bin/known-b'})
-        self.assertEqual(out, 'checking for foo... /usr/local/bin/known-b\n')
+        self.assertEqual(config, {'FOO': self.KNOWN_B})
+        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_B)
 
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown", "unknown-2", "known c"))')
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'FOO': '/home/user/bin/known c'})
-        self.assertEqual(out, "checking for foo... '/home/user/bin/known c'\n")
+        self.assertEqual(config, {'FOO': self.KNOWN_C})
+        self.assertEqual(out, "checking for foo... '%s'\n" % self.KNOWN_C)
 
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown",))')
         self.assertEqual(status, 1)
         self.assertEqual(config, {})
         self.assertEqual(out, textwrap.dedent('''\
             checking for foo... not found
             DEBUG: foo: Trying unknown
@@ -219,62 +250,63 @@ class TestChecksConfigure(unittest.TestC
         self.assertEqual(config, {'FOO': ':'})
         self.assertEqual(out, 'checking for foo... not found\n')
 
     def test_check_prog_with_args(self):
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown", "known-b", "known c"))',
             ['FOO=known-a'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'FOO': '/usr/bin/known-a'})
-        self.assertEqual(out, 'checking for foo... /usr/bin/known-a\n')
+        self.assertEqual(config, {'FOO': self.KNOWN_A})
+        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)
 
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown", "known-b", "known c"))',
-            ['FOO=/usr/bin/known-a'])
+            ['FOO=%s' % self.KNOWN_A])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'FOO': '/usr/bin/known-a'})
-        self.assertEqual(out, 'checking for foo... /usr/bin/known-a\n')
+        self.assertEqual(config, {'FOO': self.KNOWN_A})
+        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)
 
+        path = self.KNOWN_B.replace('known-b', 'known-a')
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown", "known-b", "known c"))',
-            ['FOO=/usr/local/bin/known-a'])
+            ['FOO=%s' % path])
         self.assertEqual(status, 1)
         self.assertEqual(config, {})
         self.assertEqual(out, textwrap.dedent('''\
             checking for foo... not found
-            DEBUG: foo: Trying /usr/local/bin/known-a
+            DEBUG: foo: Trying %s
             ERROR: Cannot find foo
-        '''))
+        ''') % path)
 
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown",))',
             ['FOO=known c'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'FOO': '/home/user/bin/known c'})
-        self.assertEqual(out, "checking for foo... '/home/user/bin/known c'\n")
+        self.assertEqual(config, {'FOO': self.KNOWN_C})
+        self.assertEqual(out, "checking for foo... '%s'\n" % self.KNOWN_C)
 
         config, out, status = self.get_result(
             'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
             'allow_missing=True)', ['FOO=unknown'])
         self.assertEqual(status, 1)
         self.assertEqual(config, {})
         self.assertEqual(out, textwrap.dedent('''\
             checking for foo... not found
             DEBUG: foo: Trying unknown
             ERROR: Cannot find foo
         '''))
 
     def test_check_prog_what(self):
         config, out, status = self.get_result(
             'check_prog("CC", ("known-a",), what="the target C compiler")')
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'CC': '/usr/bin/known-a'})
+        self.assertEqual(config, {'CC': self.KNOWN_A})
         self.assertEqual(
-            out, 'checking for the target C compiler... /usr/bin/known-a\n')
+            out, 'checking for the target C compiler... %s\n' % self.KNOWN_A)
 
         config, out, status = self.get_result(
             'check_prog("CC", ("unknown", "unknown-2", "unknown 3"),'
             '           what="the target C compiler")')
         self.assertEqual(status, 1)
         self.assertEqual(config, {})
         self.assertEqual(out, textwrap.dedent('''\
             checking for the target C compiler... not found
@@ -285,54 +317,53 @@ class TestChecksConfigure(unittest.TestC
         '''))
 
     def test_check_prog_input(self):
         config, out, status = self.get_result(textwrap.dedent('''
             option("--with-ccache", nargs=1, help="ccache")
             check_prog("CCACHE", ("known-a",), input="--with-ccache")
         '''), ['--with-ccache=known-b'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'CCACHE': '/usr/local/bin/known-b'})
-        self.assertEqual(
-            out, 'checking for ccache... /usr/local/bin/known-b\n')
+        self.assertEqual(config, {'CCACHE': self.KNOWN_B})
+        self.assertEqual(out, 'checking for ccache... %s\n' % self.KNOWN_B)
 
         script = textwrap.dedent('''
             option(env="CC", nargs=1, help="compiler")
             @depends("CC")
             def compiler(value):
                 return value[0].split()[0] if value else None
             check_prog("CC", ("known-a",), input=compiler)
         ''')
         config, out, status = self.get_result(script)
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'CC': '/usr/bin/known-a'})
-        self.assertEqual(out, 'checking for cc... /usr/bin/known-a\n')
+        self.assertEqual(config, {'CC': self.KNOWN_A})
+        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_A)
 
         config, out, status = self.get_result(script, ['CC=known-b'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'CC': '/usr/local/bin/known-b'})
-        self.assertEqual(out, 'checking for cc... /usr/local/bin/known-b\n')
+        self.assertEqual(config, {'CC': self.KNOWN_B})
+        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_B)
 
         config, out, status = self.get_result(script, ['CC=known-b -m32'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'CC': '/usr/local/bin/known-b'})
-        self.assertEqual(out, 'checking for cc... /usr/local/bin/known-b\n')
+        self.assertEqual(config, {'CC': self.KNOWN_B})
+        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_B)
 
     def test_check_prog_progs(self):
         config, out, status = self.get_result(
             'check_prog("FOO", ())')
         self.assertEqual(status, 0)
         self.assertEqual(config, {})
         self.assertEqual(out, '')
 
         config, out, status = self.get_result(
             'check_prog("FOO", ())', ['FOO=known-a'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'FOO': '/usr/bin/known-a'})
-        self.assertEqual(out, 'checking for foo... /usr/bin/known-a\n')
+        self.assertEqual(config, {'FOO': self.KNOWN_A})
+        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)
 
         script = textwrap.dedent('''
             option(env="TARGET", nargs=1, default="linux", help="target")
             @depends("TARGET")
             def compiler(value):
                 if value:
                     if value[0] == "linux":
                         return ("gcc", "clang")
@@ -373,24 +404,24 @@ class TestChecksConfigure(unittest.TestC
         config, out, status = self.get_result(script, ['TARGET=none'])
         self.assertEqual(status, 0)
         self.assertEqual(config, {})
         self.assertEqual(out, '')
 
         config, out, status = self.get_result(script, ['TARGET=winnt',
                                                        'CC=known-a'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'CC': '/usr/bin/known-a'})
-        self.assertEqual(out, 'checking for cc... /usr/bin/known-a\n')
+        self.assertEqual(config, {'CC': self.KNOWN_A})
+        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_A)
 
         config, out, status = self.get_result(script, ['TARGET=none',
                                                        'CC=known-a'])
         self.assertEqual(status, 0)
-        self.assertEqual(config, {'CC': '/usr/bin/known-a'})
-        self.assertEqual(out, 'checking for cc... /usr/bin/known-a\n')
+        self.assertEqual(config, {'CC': self.KNOWN_A})
+        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_A)
 
     def test_check_prog_configure_error(self):
         with self.assertRaises(ConfigureError) as e:
             self.get_result('check_prog("FOO", "foo")')
 
         self.assertEqual(e.exception.message,
                          'progs must resolve to a list or tuple!')