blob: 8833c450fd9984fabbf4d0b59198018f4de02833 [file] [log] [blame]
# 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()