Bug 1257516 - Add a unit test for check_prog(). r?ted draft
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 25 Mar 2016 17:15:47 +0900
changeset 344696 6ed8521425345d88a64dfea34ccf399a07d69803
parent 344667 9aadd62b13084ca16a2f5854af464c7cc97db49a
child 344697 b3af64933052e4f61c878e467101c65901d2f71f
push id13908
push userbmo:mh+mozilla@glandium.org
push dateFri, 25 Mar 2016 11:36:41 +0000
reviewersted
bugs1257516
milestone48.0a1
Bug 1257516 - Add a unit test for check_prog(). r?ted At the same time, shell quote the result it prints if it needs to be.
build/moz.configure/checks.configure
python/moz.build
python/mozbuild/mozbuild/configure/__init__.py
python/mozbuild/mozbuild/test/configure/test_checks_configure.py
--- a/build/moz.configure/checks.configure
+++ b/build/moz.configure/checks.configure
@@ -43,38 +43,39 @@ def checking(what, callback=None):
 
 
 # Template to check for programs in $PATH.
 #   check('PROG', ('a', 'b'))
 # will look for 'a' or 'b' in $PATH, and set_config PROG to the one
 # it can find. If PROG is already set from the environment or command line,
 # use that value instead.
 @template
+@advanced
 def check_prog(var, progs, allow_missing=False):
+    from mozbuild.shellutil import quote
+
     option(env=var, nargs=1, help='Path to the %s program' % var.lower())
 
     if not (isinstance(progs, tuple) or isinstance(progs, list)):
         configure_error('progs should be a list or tuple!')
     progs = list(progs)
 
     @depends(var)
-    @checking('for %s' % var.lower(), lambda x: x or 'not found')
+    @checking('for %s' % var.lower(), lambda x: quote(x) if x else 'not found')
     def check(value):
         if value:
             progs[:] = value
         for prog in progs:
             result = find_program(prog)
             if result:
                 return result
 
     @depends(check, var)
-    @advanced
     def postcheck(value, raw_value):
         if value is None and (not allow_missing or raw_value):
-            from mozbuild.shellutil import quote
             die('Cannot find %s (tried: %s)', var.lower(),
                 ', '.join(quote(p) for p in progs))
 
     @depends(check)
     def normalized_for_config(value):
         return ':' if value is None else value
 
     set_config(var, normalized_for_config)
--- a/python/moz.build
+++ b/python/moz.build
@@ -29,16 +29,17 @@ PYTHON_UNIT_TESTS += [
     'mozbuild/mozbuild/test/action/test_buildlist.py',
     'mozbuild/mozbuild/test/action/test_generate_browsersearch.py',
     'mozbuild/mozbuild/test/backend/test_android_eclipse.py',
     'mozbuild/mozbuild/test/backend/test_build.py',
     'mozbuild/mozbuild/test/backend/test_configenvironment.py',
     'mozbuild/mozbuild/test/backend/test_recursivemake.py',
     'mozbuild/mozbuild/test/backend/test_visualstudio.py',
     'mozbuild/mozbuild/test/compilation/test_warnings.py',
+    'mozbuild/mozbuild/test/configure/test_checks_configure.py',
     'mozbuild/mozbuild/test/configure/test_configure.py',
     'mozbuild/mozbuild/test/configure/test_options.py',
     'mozbuild/mozbuild/test/configure/test_util.py',
     'mozbuild/mozbuild/test/controller/test_ccachestats.py',
     'mozbuild/mozbuild/test/controller/test_clobber.py',
     'mozbuild/mozbuild/test/frontend/test_context.py',
     'mozbuild/mozbuild/test/frontend/test_emitter.py',
     'mozbuild/mozbuild/test/frontend/test_namespaces.py',
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -523,17 +523,17 @@ class ConfigureSandbox(dict):
         if not inspect.isfunction(func):
             raise TypeError("Unexpected type: '%s'" % type(func))
         if func in self._prepared_functions:
             return func, func.func_globals
 
         glob = SandboxedGlobal(func.func_globals)
         glob.update(
             __builtins__=self.BUILTINS,
-            __file__=self._paths[-1],
+            __file__=self._paths[-1] if self._paths else '',
             os=self.OS,
             log=self.log_impl,
         )
         func = wraps(func)(types.FunctionType(
             func.func_code,
             glob,
             func.__name__,
             func.func_defaults,
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
@@ -0,0 +1,160 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from StringIO import StringIO
+import os
+import unittest
+
+from mozunit import main
+
+from mozbuild.configure import (
+    ConfigureError,
+    ConfigureSandbox,
+)
+
+from buildconfig import topsrcdir
+
+
+class FindProgramSandbox(ConfigureSandbox):
+    def __init__(self, *args, **kwargs):
+        super(FindProgramSandbox, self).__init__(*args, **kwargs)
+
+        # 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', self.template_impl(self.find_program))
+
+    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
+
+    @staticmethod
+    def find_program(prog):
+        return FindProgramSandbox.PROGRAMS.get(prog)
+
+    def __setitem__(self, key, value):
+        # Avoid util.configure overwriting our mock find_program
+        if key == 'find_program':
+            return
+
+        super(FindProgramSandbox, self).__setitem__(key, value)
+
+
+class TestChecksConfigure(unittest.TestCase):
+    def get_result(self, command='', args=[], environ={},
+                   prog='/bin/configure'):
+        config = {}
+        out = StringIO()
+        sandbox = FindProgramSandbox(config, environ, [prog] + args, out, out)
+        base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
+        sandbox.exec_file(os.path.join(base_dir, 'util.configure'))
+        sandbox.exec_file(os.path.join(base_dir, 'checks.configure'))
+
+        status = 0
+        try:
+            exec(command, sandbox)
+        except SystemExit as e:
+            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')
+
+        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')
+
+        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")
+
+        config, out, status = self.get_result(
+            'check_prog("FOO", ("unknown",))')
+        self.assertEqual(status, 1)
+        self.assertEqual(config, {})
+        self.assertEqual(out, 'checking for foo... not found\n'
+                              'ERROR: Cannot find foo (tried: unknown)\n')
+
+        config, out, status = self.get_result(
+            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"))')
+        self.assertEqual(status, 1)
+        self.assertEqual(config, {})
+        self.assertEqual(out, 'checking for foo... not found\n'
+                              'ERROR: Cannot find foo '
+                              "(tried: unknown, unknown-2, 'unknown 3')\n")
+
+        config, out, status = self.get_result(
+            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
+            'allow_missing=True)')
+        self.assertEqual(status, 0)
+        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')
+
+        config, out, status = self.get_result(
+            'check_prog("FOO", ("unknown", "known-b", "known c"))',
+            ['FOO=/usr/bin/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')
+
+        config, out, status = self.get_result(
+            'check_prog("FOO", ("unknown", "known-b", "known c"))',
+            ['FOO=/usr/local/bin/known-a'])
+        self.assertEqual(status, 1)
+        self.assertEqual(config, {})
+        self.assertEqual(out, 'checking for foo... not found\n'
+                              'ERROR: Cannot find foo '
+                              '(tried: /usr/local/bin/known-a)\n')
+
+        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")
+
+        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, 'checking for foo... not found\n'
+                              'ERROR: Cannot find foo (tried: unknown)\n')
+
+    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 should be a list or tuple!')
+
+
+if __name__ == '__main__':
+    main()