| # 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 unicode_literals |
| |
| import os |
| import unittest |
| |
| from shutil import rmtree |
| |
| from tempfile import ( |
| gettempdir, |
| mkdtemp, |
| ) |
| |
| from mozfile.mozfile import NamedTemporaryFile |
| |
| from mozunit import main |
| |
| from mozbuild.mozconfig import ( |
| MozconfigFindException, |
| MozconfigLoadException, |
| MozconfigLoader, |
| ) |
| |
| |
| class TestMozconfigLoader(unittest.TestCase): |
| def setUp(self): |
| self._old_env = dict(os.environ) |
| os.environ.pop('MOZCONFIG', None) |
| os.environ.pop('CC', None) |
| os.environ.pop('CXX', None) |
| self._temp_dirs = set() |
| |
| def tearDown(self): |
| os.environ.clear() |
| os.environ.update(self._old_env) |
| |
| for d in self._temp_dirs: |
| rmtree(d) |
| |
| def get_loader(self): |
| return MozconfigLoader(self.get_temp_dir()) |
| |
| def get_temp_dir(self): |
| d = mkdtemp() |
| self._temp_dirs.add(d) |
| |
| return d |
| |
| def test_find_legacy_env(self): |
| """Ensure legacy mozconfig path definitions result in error.""" |
| |
| os.environ[b'MOZ_MYCONFIG'] = '/foo' |
| |
| with self.assertRaises(MozconfigFindException) as e: |
| self.get_loader().find_mozconfig() |
| |
| self.assertTrue(e.exception.message.startswith('The MOZ_MYCONFIG')) |
| |
| def test_find_path_not_exist(self): |
| """Ensure missing files are detected.""" |
| os.environ[b'MOZCONFIG'] = '/foo/bar/does/not/exist' |
| |
| with self.assertRaises(MozconfigFindException) as e: |
| self.get_loader().find_mozconfig() |
| |
| self.assertIn('path that does not exist', e.exception.message) |
| self.assertTrue(e.exception.message.endswith('/foo/bar/does/not/exist')) |
| |
| def test_find_path_not_file(self): |
| """Ensure non-file paths are detected.""" |
| |
| os.environ[b'MOZCONFIG'] = gettempdir() |
| |
| with self.assertRaises(MozconfigFindException) as e: |
| self.get_loader().find_mozconfig() |
| |
| self.assertIn('refers to a non-file', e.exception.message) |
| self.assertTrue(e.exception.message.endswith(gettempdir())) |
| |
| def test_find_default_files(self): |
| """Ensure default paths are used when present.""" |
| for p in MozconfigLoader.DEFAULT_TOPSRCDIR_PATHS: |
| d = self.get_temp_dir() |
| path = os.path.join(d, p) |
| |
| with open(path, 'w'): |
| pass |
| |
| self.assertEqual(MozconfigLoader(d).find_mozconfig(), path) |
| |
| def test_find_multiple_defaults(self): |
| """Ensure we error when multiple default files are present.""" |
| self.assertGreater(len(MozconfigLoader.DEFAULT_TOPSRCDIR_PATHS), 1) |
| |
| d = self.get_temp_dir() |
| for p in MozconfigLoader.DEFAULT_TOPSRCDIR_PATHS: |
| with open(os.path.join(d, p), 'w'): |
| pass |
| |
| with self.assertRaises(MozconfigFindException) as e: |
| MozconfigLoader(d).find_mozconfig() |
| |
| self.assertIn('Multiple default mozconfig files present', |
| e.exception.message) |
| |
| def test_find_deprecated_path_srcdir(self): |
| """Ensure we error when deprecated path locations are present.""" |
| for p in MozconfigLoader.DEPRECATED_TOPSRCDIR_PATHS: |
| d = self.get_temp_dir() |
| with open(os.path.join(d, p), 'w'): |
| pass |
| |
| with self.assertRaises(MozconfigFindException) as e: |
| MozconfigLoader(d).find_mozconfig() |
| |
| self.assertIn('This implicit location is no longer', |
| e.exception.message) |
| self.assertIn(d, e.exception.message) |
| |
| def test_find_deprecated_home_paths(self): |
| """Ensure we error when deprecated home directory paths are present.""" |
| |
| for p in MozconfigLoader.DEPRECATED_HOME_PATHS: |
| home = self.get_temp_dir() |
| os.environ[b'HOME'] = home |
| path = os.path.join(home, p) |
| |
| with open(path, 'w'): |
| pass |
| |
| with self.assertRaises(MozconfigFindException) as e: |
| self.get_loader().find_mozconfig() |
| |
| self.assertIn('This implicit location is no longer', |
| e.exception.message) |
| self.assertIn(path, e.exception.message) |
| |
| def test_read_no_mozconfig(self): |
| # This is basically to ensure changes to defaults incur a test failure. |
| result = self.get_loader().read_mozconfig() |
| |
| self.assertEqual(result, { |
| 'path': None, |
| 'topobjdir': None, |
| 'configure_args': None, |
| 'make_flags': None, |
| 'make_extra': None, |
| 'env': None, |
| }) |
| |
| def test_read_empty_mozconfig(self): |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['path'], mozconfig.name) |
| self.assertIsNone(result['topobjdir']) |
| self.assertEqual(result['configure_args'], []) |
| self.assertEqual(result['make_flags'], []) |
| self.assertEqual(result['make_extra'], []) |
| |
| for f in ('added', 'removed', 'modified'): |
| self.assertEqual(len(result['env'][f]), 0) |
| |
| self.assertGreater(len(result['env']['unmodified']), 0) |
| |
| def test_read_capture_ac_options(self): |
| """Ensures ac_add_options calls are captured.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('ac_add_options --enable-debug\n') |
| mozconfig.write('ac_add_options --disable-tests --enable-foo\n') |
| mozconfig.write('ac_add_options --foo="bar"\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| self.assertEqual(result['configure_args'], [ |
| '--enable-debug', '--disable-tests', '--enable-foo', |
| '--foo=bar']) |
| |
| def test_read_ac_options_substitution(self): |
| """Ensure ac_add_options values are substituted.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('ac_add_options --foo=@TOPSRCDIR@\n') |
| mozconfig.flush() |
| |
| loader = self.get_loader() |
| result = loader.read_mozconfig(mozconfig.name) |
| self.assertEqual(result['configure_args'], [ |
| '--foo=%s' % loader.topsrcdir]) |
| |
| def test_read_ac_app_options(self): |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('ac_add_options --foo=@TOPSRCDIR@\n') |
| mozconfig.write('ac_add_app_options app1 --bar=@TOPSRCDIR@\n') |
| mozconfig.write('ac_add_app_options app2 --bar=x\n') |
| mozconfig.flush() |
| |
| loader = self.get_loader() |
| result = loader.read_mozconfig(mozconfig.name, moz_build_app='app1') |
| self.assertEqual(result['configure_args'], [ |
| '--foo=%s' % loader.topsrcdir, |
| '--bar=%s' % loader.topsrcdir]) |
| |
| result = loader.read_mozconfig(mozconfig.name, moz_build_app='app2') |
| self.assertEqual(result['configure_args'], [ |
| '--foo=%s' % loader.topsrcdir, |
| '--bar=x']) |
| |
| def test_read_capture_mk_options(self): |
| """Ensures mk_add_options calls are captured.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('mk_add_options MOZ_OBJDIR=/foo/bar\n') |
| mozconfig.write('mk_add_options MOZ_MAKE_FLAGS=-j8\n') |
| mozconfig.write('mk_add_options FOO=BAR\n') |
| mozconfig.write('mk_add_options BIZ=1\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| self.assertEqual(result['topobjdir'], '/foo/bar') |
| self.assertEqual(result['make_flags'], '-j8') |
| self.assertEqual(result['make_extra'], ['FOO=BAR', 'BIZ=1']) |
| |
| def test_read_moz_objdir_substitution(self): |
| """Ensure @TOPSRCDIR@ substitution is recognized in MOZ_OBJDIR.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/some-objdir') |
| mozconfig.flush() |
| |
| loader = self.get_loader() |
| result = loader.read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['topobjdir'], '%s/some-objdir' % |
| loader.topsrcdir) |
| |
| def test_read_new_variables(self): |
| """New variables declared in mozconfig file are detected.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('CC=/usr/local/bin/clang\n') |
| mozconfig.write('CXX=/usr/local/bin/clang++\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['env']['added'], { |
| 'CC': '/usr/local/bin/clang', |
| 'CXX': '/usr/local/bin/clang++'}) |
| |
| def test_read_exported_variables(self): |
| """Exported variables are caught as new variables.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('export MY_EXPORTED=woot\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['env']['added'], { |
| 'MY_EXPORTED': 'woot'}) |
| |
| def test_read_modify_variables(self): |
| """Variables modified by mozconfig are detected.""" |
| os.environ[b'CC'] = b'/usr/bin/gcc' |
| |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('CC=/usr/local/bin/clang\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['env']['modified'], { |
| 'CC': ('/usr/bin/gcc', '/usr/local/bin/clang') |
| }) |
| |
| def test_read_removed_variables(self): |
| """Variables unset by the mozconfig are detected.""" |
| os.environ[b'CC'] = b'/usr/bin/clang' |
| |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('unset CC\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['env']['removed'], { |
| 'CC': '/usr/bin/clang'}) |
| |
| def test_read_multiline_variables(self): |
| """Ensure multi-line variables are captured properly.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('multi="foo\nbar"\n') |
| mozconfig.write('single=1\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['env']['added'], { |
| 'multi': 'foo\nbar', |
| 'single': '1' |
| }) |
| |
| def test_read_topsrcdir_defined(self): |
| """Ensure $topsrcdir references work as expected.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('TEST=$topsrcdir') |
| mozconfig.flush() |
| |
| loader = self.get_loader() |
| result = loader.read_mozconfig(mozconfig.name) |
| |
| self.assertEqual(result['env']['added']['TEST'], |
| loader.topsrcdir.replace(os.sep, '/')) |
| |
| def test_read_empty_variable_value(self): |
| """Ensure empty variable values are parsed properly.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('EMPTY=\n') |
| mozconfig.flush() |
| |
| result = self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertIn('EMPTY', result['env']['added']) |
| self.assertEqual(result['env']['added']['EMPTY'], '') |
| |
| def test_read_load_exception(self): |
| """Ensure non-0 exit codes in mozconfigs are handled properly.""" |
| with NamedTemporaryFile(mode='w') as mozconfig: |
| mozconfig.write('echo "hello world"\n') |
| mozconfig.write('exit 1\n') |
| mozconfig.flush() |
| |
| with self.assertRaises(MozconfigLoadException) as e: |
| self.get_loader().read_mozconfig(mozconfig.name) |
| |
| self.assertTrue(e.exception.message.startswith( |
| 'Evaluation of your mozconfig exited with an error')) |
| self.assertEquals(e.exception.path, |
| mozconfig.name.replace(os.sep, '/')) |
| self.assertEquals(e.exception.output, ['hello world']) |
| |
| |
| if __name__ == '__main__': |
| main() |