diff options
Diffstat (limited to 'python/mozbuild/mozbuild/test')
592 files changed, 18920 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/test/__init__.py b/python/mozbuild/mozbuild/test/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/__init__.py diff --git a/python/mozbuild/mozbuild/test/action/data/invalid/region.properties b/python/mozbuild/mozbuild/test/action/data/invalid/region.properties new file mode 100644 index 000000000..d4d8109b6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/invalid/region.properties @@ -0,0 +1,12 @@ +# A region.properties file with invalid unicode byte sequences. The +# sequences were cribbed from Markus Kuhn's "UTF-8 decoder capability +# and stress test", available at +# http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + +# 3.5 Impossible bytes | +# | +# The following two bytes cannot appear in a correct UTF-8 string | +# | +# 3.5.1 fe = "þ" | +# 3.5.2 ff = "ÿ" | +# 3.5.3 fe fe ff ff = "þþÿÿ" | diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/assets/asset.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/assets/asset.txt new file mode 100644 index 000000000..b01830602 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/assets/asset.txt @@ -0,0 +1 @@ +assets/asset.txt diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/classes.dex b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/classes.dex new file mode 100644 index 000000000..dfc99f9c2 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/classes.dex @@ -0,0 +1 @@ +classes.dex
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1.ap_ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1.ap_ Binary files differnew file mode 100644 index 000000000..915be683b --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1.ap_ diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1/res/res.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1/res/res.txt new file mode 100644 index 000000000..01d2fb0a1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1/res/res.txt @@ -0,0 +1 @@ +input1/res/res.txt diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1/resources.arsc b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1/resources.arsc new file mode 100644 index 000000000..6274a181a --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input1/resources.arsc @@ -0,0 +1 @@ +input1/resources.arsc
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2.apk b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2.apk Binary files differnew file mode 100644 index 000000000..3003f5ae9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2.apk diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/assets/asset.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/assets/asset.txt new file mode 100644 index 000000000..31a0e5129 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/assets/asset.txt @@ -0,0 +1 @@ +input2/assets/asset.txt diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/assets/omni.ja b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/assets/omni.ja new file mode 100644 index 000000000..36deb6725 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/assets/omni.ja @@ -0,0 +1 @@ +input2/assets/omni.ja
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/classes.dex b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/classes.dex new file mode 100644 index 000000000..99779eb45 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/classes.dex @@ -0,0 +1 @@ +input2/classes.dex
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/lib/lib.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/lib/lib.txt new file mode 100644 index 000000000..7a2594a02 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/lib/lib.txt @@ -0,0 +1 @@ +input2/lib/lib.txt diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/res/res.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/res/res.txt new file mode 100644 index 000000000..2a52ab524 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/res/res.txt @@ -0,0 +1 @@ +input2/res/res.txt diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/resources.arsc b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/resources.arsc new file mode 100644 index 000000000..64f4b77ad --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/resources.arsc @@ -0,0 +1 @@ +input/resources.arsc
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/root_file.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/root_file.txt new file mode 100644 index 000000000..9f2f53518 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/input2/root_file.txt @@ -0,0 +1 @@ +input2/root_file.txt diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/lib/lib.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/lib/lib.txt new file mode 100644 index 000000000..acbcebb3d --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/lib/lib.txt @@ -0,0 +1 @@ +lib/lib.txt diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/omni.ja b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/omni.ja new file mode 100644 index 000000000..48c422a3a --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/omni.ja @@ -0,0 +1 @@ +omni.ja
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/root_file.txt b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/root_file.txt new file mode 100644 index 000000000..89b006da4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/package_fennec_apk/root_file.txt @@ -0,0 +1 @@ +root_file.txt diff --git a/python/mozbuild/mozbuild/test/action/data/valid-zh-CN/region.properties b/python/mozbuild/mozbuild/test/action/data/valid-zh-CN/region.properties new file mode 100644 index 000000000..d4d7fcfee --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/valid-zh-CN/region.properties @@ -0,0 +1,37 @@ +# 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/. + +# Default search engine +browser.search.defaultenginename=百度 + +# Search engine order (order displayed in the search bar dropdown)s +browser.search.order.1=百度 +browser.search.order.2=Google + +# This is the default set of web based feed handlers shown in the reader +# selection UI +browser.contentHandlers.types.0.title=Bloglines +browser.contentHandlers.types.0.uri=http://www.bloglines.com/login?r=/sub/%s + +# increment this number when anything gets changed in the list below. This will +# cause Firefox to re-read these prefs and inject any new handlers into the +# profile database. Note that "new" is defined as "has a different URL"; this +# means that it's not possible to update the name of existing handler, so +# don't make any spelling errors here. +gecko.handlerService.defaultHandlersVersion=3 + +# The default set of protocol handlers for webcal: +gecko.handlerService.schemes.webcal.0.name=30 Boxes +gecko.handlerService.schemes.webcal.0.uriTemplate=https://30boxes.com/external/widget?refer=ff&url=%s + +# The default set of protocol handlers for mailto: +gecko.handlerService.schemes.mailto.0.name=Yahoo! 邮件 +gecko.handlerService.schemes.mailto.0.uriTemplate=https://compose.mail.yahoo.com/?To=%s +gecko.handlerService.schemes.mailto.1.name=Gmail +gecko.handlerService.schemes.mailto.1.uriTemplate=https://mail.google.com/mail/?extsrc=mailto&url=%s + +# This is the default set of web based feed handlers shown in the reader +# selection UI +browser.contentHandlers.types.0.title=My Yahoo! +browser.contentHandlers.types.0.uri=http://www.bloglines.com/login?r=/sub/%s diff --git a/python/mozbuild/mozbuild/test/action/test_buildlist.py b/python/mozbuild/mozbuild/test/action/test_buildlist.py new file mode 100644 index 000000000..9c2631812 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/test_buildlist.py @@ -0,0 +1,89 @@ +# 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/. + +import unittest + +import os, sys, os.path, time +from tempfile import mkdtemp +from shutil import rmtree +import mozunit + +from mozbuild.action.buildlist import addEntriesToListFile + + +class TestBuildList(unittest.TestCase): + """ + Unit tests for buildlist.py + """ + def setUp(self): + self.tmpdir = mkdtemp() + + def tearDown(self): + rmtree(self.tmpdir) + + # utility methods for tests + def touch(self, file, dir=None): + if dir is None: + dir = self.tmpdir + f = os.path.join(dir, file) + open(f, 'w').close() + return f + + def assertFileContains(self, filename, l): + """Assert that the lines in the file |filename| are equal + to the contents of the list |l|, in order.""" + l = l[:] + f = open(filename, 'r') + lines = [line.rstrip() for line in f.readlines()] + f.close() + for line in lines: + self.assert_(len(l) > 0, + "ran out of expected lines! (expected '{0}', got '{1}')" + .format(l, lines)) + self.assertEqual(line, l.pop(0)) + self.assert_(len(l) == 0, + "not enough lines in file! (expected '{0}'," + " got '{1}'".format(l, lines)) + + def test_basic(self): + "Test that addEntriesToListFile works when file doesn't exist." + testfile = os.path.join(self.tmpdir, "test.list") + l = ["a", "b", "c"] + addEntriesToListFile(testfile, l) + self.assertFileContains(testfile, l) + # ensure that attempting to add the same entries again doesn't change it + addEntriesToListFile(testfile, l) + self.assertFileContains(testfile, l) + + def test_append(self): + "Test adding new entries." + testfile = os.path.join(self.tmpdir, "test.list") + l = ["a", "b", "c"] + addEntriesToListFile(testfile, l) + self.assertFileContains(testfile, l) + l2 = ["x","y","z"] + addEntriesToListFile(testfile, l2) + l.extend(l2) + self.assertFileContains(testfile, l) + + def test_append_some(self): + "Test adding new entries mixed with existing entries." + testfile = os.path.join(self.tmpdir, "test.list") + l = ["a", "b", "c"] + addEntriesToListFile(testfile, l) + self.assertFileContains(testfile, l) + addEntriesToListFile(testfile, ["a", "x", "c", "z"]) + self.assertFileContains(testfile, ["a", "b", "c", "x", "z"]) + + def test_add_multiple(self): + """Test that attempting to add the same entry multiple times results in + only one entry being added.""" + testfile = os.path.join(self.tmpdir, "test.list") + addEntriesToListFile(testfile, ["a","b","a","a","b"]) + self.assertFileContains(testfile, ["a","b"]) + addEntriesToListFile(testfile, ["c","a","c","b","c"]) + self.assertFileContains(testfile, ["a","b","c"]) + +if __name__ == '__main__': + mozunit.main() diff --git a/python/mozbuild/mozbuild/test/action/test_generate_browsersearch.py b/python/mozbuild/mozbuild/test/action/test_generate_browsersearch.py new file mode 100644 index 000000000..4c7f5635e --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/test_generate_browsersearch.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from __future__ import unicode_literals + +import json +import os +import unittest + +import mozunit + +import mozbuild.action.generate_browsersearch as generate_browsersearch + +from mozfile.mozfile import ( + NamedTemporaryFile, + TemporaryDirectory, +) + +import mozpack.path as mozpath + + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data') + + +class TestGenerateBrowserSearch(unittest.TestCase): + """ + Unit tests for generate_browsersearch.py. + """ + + def _test_one(self, name): + with TemporaryDirectory() as tmpdir: + with NamedTemporaryFile(mode='r+') as temp: + srcdir = os.path.join(test_data_path, name) + + generate_browsersearch.main([ + '--silent', + '--srcdir', srcdir, + temp.name]) + return json.load(temp) + + def test_valid_unicode(self): + o = self._test_one('valid-zh-CN') + self.assertEquals(o['default'], '百度') + self.assertEquals(o['engines'], ['百度', 'Google']) + + def test_invalid_unicode(self): + with self.assertRaises(UnicodeDecodeError): + self._test_one('invalid') + + +if __name__ == '__main__': + mozunit.main() diff --git a/python/mozbuild/mozbuild/test/action/test_package_fennec_apk.py b/python/mozbuild/mozbuild/test/action/test_package_fennec_apk.py new file mode 100644 index 000000000..5b7760836 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/test_package_fennec_apk.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from __future__ import unicode_literals + +import os +import unittest + +import mozunit + +from mozbuild.action.package_fennec_apk import ( + package_fennec_apk as package, +) +from mozpack.mozjar import JarReader +import mozpack.path as mozpath + + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data', 'package_fennec_apk') + + +def data(name): + return os.path.join(test_data_path, name) + + +class TestPackageFennecAPK(unittest.TestCase): + """ + Unit tests for package_fennec_apk.py. + """ + + def test_arguments(self): + # Language repacks take updated resources from an ap_ and pack them + # into an apk. Make sure the second input overrides the first. + jarrer = package(inputs=[], + omni_ja=data('omni.ja'), + classes_dex=data('classes.dex'), + assets_dirs=[data('assets')], + lib_dirs=[data('lib')], + root_files=[data('root_file.txt')]) + + # omni.ja ends up in assets/omni.ja. + self.assertEquals(jarrer['assets/omni.ja'].open().read().strip(), 'omni.ja') + + # Everything else is in place. + for name in ('classes.dex', + 'assets/asset.txt', + 'lib/lib.txt', + 'root_file.txt'): + self.assertEquals(jarrer[name].open().read().strip(), name) + + def test_inputs(self): + # Language repacks take updated resources from an ap_ and pack them + # into an apk. In this case, the first input is the original package, + # the second input the update ap_. Make sure the second input + # overrides the first. + jarrer = package(inputs=[data('input2.apk'), data('input1.ap_')]) + + files1 = JarReader(data('input1.ap_')).entries.keys() + files2 = JarReader(data('input2.apk')).entries.keys() + for name in files2: + self.assertTrue(name in files1 or + jarrer[name].open().read().startswith('input2/')) + for name in files1: + self.assertTrue(jarrer[name].open().read().startswith('input1/')) + + +if __name__ == '__main__': + mozunit.main() diff --git a/python/mozbuild/mozbuild/test/backend/__init__.py b/python/mozbuild/mozbuild/test/backend/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/__init__.py diff --git a/python/mozbuild/mozbuild/test/backend/common.py b/python/mozbuild/mozbuild/test/backend/common.py new file mode 100644 index 000000000..85ccb1037 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/common.py @@ -0,0 +1,156 @@ +# 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 collections import defaultdict +from shutil import rmtree +from tempfile import mkdtemp + +from mach.logging import LoggingManager + +from mozbuild.backend.configenvironment import ConfigEnvironment +from mozbuild.frontend.emitter import TreeMetadataEmitter +from mozbuild.frontend.reader import BuildReader + +import mozpack.path as mozpath + + +log_manager = LoggingManager() +log_manager.add_terminal_logging() + + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data') + + +CONFIGS = defaultdict(lambda: { + 'defines': {}, + 'non_global_defines': [], + 'substs': {'OS_TARGET': 'WINNT'}, +}, { + 'android_eclipse': { + 'defines': { + 'MOZ_ANDROID_MIN_SDK_VERSION': '15', + }, + 'non_global_defines': [], + 'substs': { + 'ANDROID_TARGET_SDK': '16', + 'MOZ_WIDGET_TOOLKIT': 'android', + }, + }, + 'binary-components': { + 'defines': {}, + 'non_global_defines': [], + 'substs': { + 'LIB_PREFIX': 'lib', + 'LIB_SUFFIX': 'a', + 'COMPILE_ENVIRONMENT': '1', + }, + }, + 'sources': { + 'defines': {}, + 'non_global_defines': [], + 'substs': { + 'LIB_PREFIX': 'lib', + 'LIB_SUFFIX': 'a', + }, + }, + 'stub0': { + 'defines': { + 'MOZ_TRUE_1': '1', + 'MOZ_TRUE_2': '1', + }, + 'non_global_defines': [ + 'MOZ_NONGLOBAL_1', + 'MOZ_NONGLOBAL_2', + ], + 'substs': { + 'MOZ_FOO': 'foo', + 'MOZ_BAR': 'bar', + }, + }, + 'substitute_config_files': { + 'defines': {}, + 'non_global_defines': [], + 'substs': { + 'MOZ_FOO': 'foo', + 'MOZ_BAR': 'bar', + }, + }, + 'test_config': { + 'defines': { + 'foo': 'baz qux', + 'baz': 1, + }, + 'non_global_defines': [], + 'substs': { + 'foo': 'bar baz', + }, + }, + 'visual-studio': { + 'defines': {}, + 'non_global_defines': [], + 'substs': { + 'MOZ_APP_NAME': 'my_app', + }, + }, +}) + + +class BackendTester(unittest.TestCase): + def setUp(self): + self._old_env = dict(os.environ) + os.environ.pop('MOZ_OBJDIR', None) + + def tearDown(self): + os.environ.clear() + os.environ.update(self._old_env) + + def _get_environment(self, name): + """Obtain a new instance of a ConfigEnvironment for a known profile. + + A new temporary object directory is created for the environment. The + environment is cleaned up automatically when the test finishes. + """ + config = CONFIGS[name] + + objdir = mkdtemp() + self.addCleanup(rmtree, objdir) + + srcdir = mozpath.join(test_data_path, name) + config['substs']['top_srcdir'] = srcdir + return ConfigEnvironment(srcdir, objdir, **config) + + def _emit(self, name, env=None): + env = env or self._get_environment(name) + reader = BuildReader(env) + emitter = TreeMetadataEmitter(env) + + return env, emitter.emit(reader.read_topsrcdir()) + + def _consume(self, name, cls, env=None): + env, objs = self._emit(name, env=env) + backend = cls(env) + backend.consume(objs) + + return env + + def _tree_paths(self, topdir, filename): + for dirpath, dirnames, filenames in os.walk(topdir): + for f in filenames: + if f == filename: + yield mozpath.relpath(mozpath.join(dirpath, f), topdir) + + def _mozbuild_paths(self, env): + return self._tree_paths(env.topsrcdir, 'moz.build') + + def _makefile_in_paths(self, env): + return self._tree_paths(env.topsrcdir, 'Makefile.in') + + +__all__ = ['BackendTester'] diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/library1/resources/values/strings.xml b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/library1/resources/values/strings.xml new file mode 100644 index 000000000..a7337c554 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/library1/resources/values/strings.xml @@ -0,0 +1 @@ +<string name="label">library1</string> diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main1/AndroidManifest.xml b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main1/AndroidManifest.xml new file mode 100644 index 000000000..7a906454d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main1/AndroidManifest.xml @@ -0,0 +1 @@ +<!-- Placeholder. --> diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/AndroidManifest.xml b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/AndroidManifest.xml new file mode 100644 index 000000000..7a906454d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/AndroidManifest.xml @@ -0,0 +1 @@ +<!-- Placeholder. --> diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/assets/dummy.txt b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/assets/dummy.txt new file mode 100644 index 000000000..c32a95993 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/assets/dummy.txt @@ -0,0 +1 @@ +# Placeholder.
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/extra.jar b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/extra.jar new file mode 100644 index 000000000..c32a95993 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/extra.jar @@ -0,0 +1 @@ +# Placeholder.
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/res/values/strings.xml b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/res/values/strings.xml new file mode 100644 index 000000000..0b28bf41e --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main2/res/values/strings.xml @@ -0,0 +1 @@ +<string name="label">main1</string> diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/AndroidManifest.xml b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/AndroidManifest.xml new file mode 100644 index 000000000..7a906454d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/AndroidManifest.xml @@ -0,0 +1 @@ +<!-- Placeholder. --> diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/a/A.java b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/a/A.java new file mode 100644 index 000000000..0ab867d3d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/a/A.java @@ -0,0 +1 @@ +package a.a; diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/b/B.java b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/b/B.java new file mode 100644 index 000000000..66eb44c15 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/b/B.java @@ -0,0 +1 @@ +package b; diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/c/C.java b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/c/C.java new file mode 100644 index 000000000..ca474ff33 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main3/c/C.java @@ -0,0 +1 @@ +package d.e; diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main4 b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main4 new file mode 100644 index 000000000..7a906454d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/main4 @@ -0,0 +1 @@ +<!-- Placeholder. --> diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/moz.build b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/moz.build new file mode 100644 index 000000000..327284c88 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/moz.build @@ -0,0 +1,37 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + + +p = add_android_eclipse_library_project('library1') +p.package_name = 'org.mozilla.test.library1' +p.res = 'library1/resources' + +p = add_android_eclipse_library_project('library2') +p.package_name = 'org.mozilla.test.library2' + +p = add_android_eclipse_project('main1', 'main1/AndroidManifest.xml') +p.package_name = 'org.mozilla.test.main1' +p.recursive_make_targets += ['target1', 'target2'] + +p = add_android_eclipse_project('main2', 'main2/AndroidManifest.xml') +p.package_name = 'org.mozilla.test.main2' +p.res = 'main2/res' +p.assets = 'main2/assets' +p.extra_jars = ['main2/extra.jar'] + +p = add_android_eclipse_project('main3', 'main3/AndroidManifest.xml') +p.package_name = 'org.mozilla.test.main3' +cpe = p.add_classpathentry('a', 'main3/a', dstdir='a/a') +cpe = p.add_classpathentry('b', 'main3/b', dstdir='b') +cpe.exclude_patterns += ['b/Excludes.java', 'b/Excludes2.java'] +cpe = p.add_classpathentry('c', 'main3/c', dstdir='d/e') +cpe.ignore_warnings = True + +p = add_android_eclipse_project('main4', 'main3/AndroidManifest.xml') +p.package_name = 'org.mozilla.test.main3' +p.referenced_projects += ['library1'] +p.included_projects += ['library2'] +p.recursive_make_targets += ['target3', 'target4'] + +DIRS += ['subdir'] diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/subdir/moz.build b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/subdir/moz.build new file mode 100644 index 000000000..c75aec456 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/subdir/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DEFINES['FOO'] = 'FOO' + +p = add_android_eclipse_library_project('sublibrary') +p.package_name = 'org.mozilla.test.sublibrary' +p.is_library = True + +p = add_android_eclipse_project('submain', 'submain/AndroidManifest.xml') +p.package_name = 'org.mozilla.test.submain' +p.recursive_make_targets += ['subtarget1', 'subtarget2'] diff --git a/python/mozbuild/mozbuild/test/backend/data/android_eclipse/subdir/submain/AndroidManifest.xml b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/subdir/submain/AndroidManifest.xml new file mode 100644 index 000000000..7a906454d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/android_eclipse/subdir/submain/AndroidManifest.xml @@ -0,0 +1 @@ +<!-- Placeholder. --> diff --git a/python/mozbuild/mozbuild/test/backend/data/binary-components/bar/moz.build b/python/mozbuild/mozbuild/test/backend/data/binary-components/bar/moz.build new file mode 100644 index 000000000..2946e42aa --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/binary-components/bar/moz.build @@ -0,0 +1,2 @@ +Component('bar') +NO_COMPONENTS_MANIFEST = True diff --git a/python/mozbuild/mozbuild/test/backend/data/binary-components/foo/moz.build b/python/mozbuild/mozbuild/test/backend/data/binary-components/foo/moz.build new file mode 100644 index 000000000..8611a74be --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/binary-components/foo/moz.build @@ -0,0 +1 @@ +Component('foo') diff --git a/python/mozbuild/mozbuild/test/backend/data/binary-components/moz.build b/python/mozbuild/mozbuild/test/backend/data/binary-components/moz.build new file mode 100644 index 000000000..1776d0514 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/binary-components/moz.build @@ -0,0 +1,10 @@ +@template +def Component(name): + LIBRARY_NAME = name + FORCE_SHARED_LIB = True + IS_COMPONENT = True + +DIRS += [ + 'foo', + 'bar', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/bar.ico b/python/mozbuild/mozbuild/test/backend/data/branding-files/bar.ico new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/branding-files/bar.ico diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/foo.ico b/python/mozbuild/mozbuild/test/backend/data/branding-files/foo.ico new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/branding-files/foo.ico diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build b/python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build new file mode 100644 index 000000000..083f0f82d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build @@ -0,0 +1,12 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +BRANDING_FILES += [ + 'bar.ico', + 'sub/quux.png', +] + +BRANDING_FILES.icons += [ + 'foo.ico', +] + diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/sub/quux.png b/python/mozbuild/mozbuild/test/backend/data/branding-files/sub/quux.png new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/branding-files/sub/quux.png diff --git a/python/mozbuild/mozbuild/test/backend/data/build/app/moz.build b/python/mozbuild/mozbuild/test/backend/data/build/app/moz.build new file mode 100644 index 000000000..8d6218ea9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/app/moz.build @@ -0,0 +1,54 @@ +DIST_SUBDIR = 'app' + +EXTRA_JS_MODULES += [ + '../foo.jsm', +] + +EXTRA_JS_MODULES.child += [ + '../bar.jsm', +] + +EXTRA_PP_JS_MODULES += [ + '../baz.jsm', +] + +EXTRA_PP_JS_MODULES.child2 += [ + '../qux.jsm', +] + +FINAL_TARGET_FILES += [ + '../foo.ini', +] + +FINAL_TARGET_FILES.child += [ + '../bar.ini', +] + +FINAL_TARGET_PP_FILES += [ + '../baz.ini', + '../foo.css', +] + +FINAL_TARGET_PP_FILES.child2 += [ + '../qux.ini', +] + +EXTRA_COMPONENTS += [ + '../components.manifest', + '../foo.js', +] + +EXTRA_PP_COMPONENTS += [ + '../bar.js', +] + +JS_PREFERENCE_FILES += [ + '../prefs.js', +] + +JAR_MANIFESTS += [ + '../jar.mn', +] + +DEFINES['FOO'] = 'bar' +DEFINES['BAR'] = True diff --git a/python/mozbuild/mozbuild/test/backend/data/build/bar.ini b/python/mozbuild/mozbuild/test/backend/data/build/bar.ini new file mode 100644 index 000000000..91dcbe153 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/bar.ini @@ -0,0 +1 @@ +bar.ini diff --git a/python/mozbuild/mozbuild/test/backend/data/build/bar.js b/python/mozbuild/mozbuild/test/backend/data/build/bar.js new file mode 100644 index 000000000..1a608e8a5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/bar.js @@ -0,0 +1,2 @@ +#filter substitution +bar.js: FOO is @FOO@ diff --git a/python/mozbuild/mozbuild/test/backend/data/build/bar.jsm b/python/mozbuild/mozbuild/test/backend/data/build/bar.jsm new file mode 100644 index 000000000..05db2e2f6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/bar.jsm @@ -0,0 +1 @@ +bar.jsm diff --git a/python/mozbuild/mozbuild/test/backend/data/build/baz.ini b/python/mozbuild/mozbuild/test/backend/data/build/baz.ini new file mode 100644 index 000000000..975a1e437 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/baz.ini @@ -0,0 +1,2 @@ +#filter substitution +baz.ini: FOO is @FOO@ diff --git a/python/mozbuild/mozbuild/test/backend/data/build/baz.jsm b/python/mozbuild/mozbuild/test/backend/data/build/baz.jsm new file mode 100644 index 000000000..f39ed0208 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/baz.jsm @@ -0,0 +1,2 @@ +#filter substitution +baz.jsm: FOO is @FOO@ diff --git a/python/mozbuild/mozbuild/test/backend/data/build/components.manifest b/python/mozbuild/mozbuild/test/backend/data/build/components.manifest new file mode 100644 index 000000000..b5bb87254 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/components.manifest @@ -0,0 +1,2 @@ +component {foo} foo.js +component {bar} bar.js diff --git a/python/mozbuild/mozbuild/test/backend/data/build/foo.css b/python/mozbuild/mozbuild/test/backend/data/build/foo.css new file mode 100644 index 000000000..1803d6c57 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/foo.css @@ -0,0 +1,2 @@ +%filter substitution +foo.css: FOO is @FOO@ diff --git a/python/mozbuild/mozbuild/test/backend/data/build/foo.ini b/python/mozbuild/mozbuild/test/backend/data/build/foo.ini new file mode 100644 index 000000000..c93c9d765 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/foo.ini @@ -0,0 +1 @@ +foo.ini diff --git a/python/mozbuild/mozbuild/test/backend/data/build/foo.js b/python/mozbuild/mozbuild/test/backend/data/build/foo.js new file mode 100644 index 000000000..4fa71e2d2 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/foo.js @@ -0,0 +1 @@ +foo.js diff --git a/python/mozbuild/mozbuild/test/backend/data/build/foo.jsm b/python/mozbuild/mozbuild/test/backend/data/build/foo.jsm new file mode 100644 index 000000000..d58fd61c1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/foo.jsm @@ -0,0 +1 @@ +foo.jsm diff --git a/python/mozbuild/mozbuild/test/backend/data/build/jar.mn b/python/mozbuild/mozbuild/test/backend/data/build/jar.mn new file mode 100644 index 000000000..393055c4e --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/jar.mn @@ -0,0 +1,11 @@ +foo.jar: +% content bar %child/ +% content foo % + foo.js +* foo.css + bar.js (subdir/bar.js) + qux.js (subdir/bar.js) +* child/hoge.js (bar.js) +* child/baz.jsm + +% override chrome://foo/bar.svg#hello chrome://bar/bar.svg#hello diff --git a/python/mozbuild/mozbuild/test/backend/data/build/moz.build b/python/mozbuild/mozbuild/test/backend/data/build/moz.build new file mode 100644 index 000000000..b0b0cabd1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/moz.build @@ -0,0 +1,68 @@ +CONFIGURE_SUBST_FILES += [ + '/config/autoconf.mk', + '/config/emptyvars.mk', +] + +EXTRA_JS_MODULES += [ + 'foo.jsm', +] + +EXTRA_JS_MODULES.child += [ + 'bar.jsm', +] + +EXTRA_PP_JS_MODULES += [ + 'baz.jsm', +] + +EXTRA_PP_JS_MODULES.child2 += [ + 'qux.jsm', +] + +FINAL_TARGET_FILES += [ + 'foo.ini', +] + +FINAL_TARGET_FILES.child += [ + 'bar.ini', +] + +FINAL_TARGET_PP_FILES += [ + 'baz.ini', +] + +FINAL_TARGET_PP_FILES.child2 += [ + 'foo.css', + 'qux.ini', +] + +EXTRA_COMPONENTS += [ + 'components.manifest', + 'foo.js', +] + +EXTRA_PP_COMPONENTS += [ + 'bar.js', +] + +JS_PREFERENCE_FILES += [ + 'prefs.js', +] + +RESOURCE_FILES += [ + 'resource', +] + +RESOURCE_FILES.child += [ + 'resource2', +] + +DEFINES['FOO'] = 'foo' + +JAR_MANIFESTS += [ + 'jar.mn', +] + +DIRS += [ + 'app', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/build/prefs.js b/python/mozbuild/mozbuild/test/backend/data/build/prefs.js new file mode 100644 index 000000000..a030da9fd --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/prefs.js @@ -0,0 +1 @@ +prefs.js diff --git a/python/mozbuild/mozbuild/test/backend/data/build/qux.ini b/python/mozbuild/mozbuild/test/backend/data/build/qux.ini new file mode 100644 index 000000000..3ce157eb6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/qux.ini @@ -0,0 +1,5 @@ +#ifdef BAR +qux.ini: BAR is defined +#else +qux.ini: BAR is not defined +#endif diff --git a/python/mozbuild/mozbuild/test/backend/data/build/qux.jsm b/python/mozbuild/mozbuild/test/backend/data/build/qux.jsm new file mode 100644 index 000000000..9c5fe28d5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/qux.jsm @@ -0,0 +1,5 @@ +#ifdef BAR +qux.jsm: BAR is defined +#else +qux.jsm: BAR is not defined +#endif diff --git a/python/mozbuild/mozbuild/test/backend/data/build/resource b/python/mozbuild/mozbuild/test/backend/data/build/resource new file mode 100644 index 000000000..91e75c679 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/resource @@ -0,0 +1 @@ +resource diff --git a/python/mozbuild/mozbuild/test/backend/data/build/resource2 b/python/mozbuild/mozbuild/test/backend/data/build/resource2 new file mode 100644 index 000000000..b7c270096 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/resource2 @@ -0,0 +1 @@ +resource2 diff --git a/python/mozbuild/mozbuild/test/backend/data/build/subdir/bar.js b/python/mozbuild/mozbuild/test/backend/data/build/subdir/bar.js new file mode 100644 index 000000000..80c887a84 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/build/subdir/bar.js @@ -0,0 +1 @@ +bar.js diff --git a/python/mozbuild/mozbuild/test/backend/data/defines/moz.build b/python/mozbuild/mozbuild/test/backend/data/defines/moz.build new file mode 100644 index 000000000..be4b31143 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/defines/moz.build @@ -0,0 +1,14 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +value = 'xyz' +DEFINES = { + 'FOO': True, +} + +DEFINES['BAZ'] = '"ab\'cd"' +DEFINES.update({ + 'BAR': 7, + 'VALUE': value, + 'QUX': False, +}) diff --git a/python/mozbuild/mozbuild/test/backend/data/dist-files/install.rdf b/python/mozbuild/mozbuild/test/backend/data/dist-files/install.rdf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/dist-files/install.rdf diff --git a/python/mozbuild/mozbuild/test/backend/data/dist-files/main.js b/python/mozbuild/mozbuild/test/backend/data/dist-files/main.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/dist-files/main.js diff --git a/python/mozbuild/mozbuild/test/backend/data/dist-files/moz.build b/python/mozbuild/mozbuild/test/backend/data/dist-files/moz.build new file mode 100644 index 000000000..cbd2c942b --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/dist-files/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FINAL_TARGET_PP_FILES += [ + 'install.rdf', + 'main.js', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/dom1.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/dom1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports-generated/dom1.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/foo.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/foo.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports-generated/foo.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/gfx.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/gfx.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports-generated/gfx.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/moz.build b/python/mozbuild/mozbuild/test/backend/data/exports-generated/moz.build new file mode 100644 index 000000000..b604ef1a0 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports-generated/moz.build @@ -0,0 +1,12 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['!bar.h', 'foo.h'] +EXPORTS.mozilla += ['!mozilla2.h', 'mozilla1.h'] +EXPORTS.mozilla.dom += ['!dom2.h', '!dom3.h', 'dom1.h'] +EXPORTS.gfx += ['gfx.h'] + +GENERATED_FILES += ['bar.h'] +GENERATED_FILES += ['mozilla2.h'] +GENERATED_FILES += ['dom2.h'] +GENERATED_FILES += ['dom3.h'] diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/mozilla1.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/mozilla1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports-generated/mozilla1.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/dom1.h b/python/mozbuild/mozbuild/test/backend/data/exports/dom1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/dom1.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/dom2.h b/python/mozbuild/mozbuild/test/backend/data/exports/dom2.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/dom2.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/foo.h b/python/mozbuild/mozbuild/test/backend/data/exports/foo.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/foo.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/gfx.h b/python/mozbuild/mozbuild/test/backend/data/exports/gfx.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/gfx.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/moz.build b/python/mozbuild/mozbuild/test/backend/data/exports/moz.build new file mode 100644 index 000000000..725fa1fd4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/moz.build @@ -0,0 +1,8 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS.mozilla += ['mozilla1.h', 'mozilla2.h'] +EXPORTS.mozilla.dom += ['dom1.h', 'dom2.h'] +EXPORTS.mozilla.gfx += ['gfx.h'] +EXPORTS.nspr.private += ['pprio.h'] diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/mozilla1.h b/python/mozbuild/mozbuild/test/backend/data/exports/mozilla1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/mozilla1.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/mozilla2.h b/python/mozbuild/mozbuild/test/backend/data/exports/mozilla2.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/mozilla2.h diff --git a/python/mozbuild/mozbuild/test/backend/data/exports/pprio.h b/python/mozbuild/mozbuild/test/backend/data/exports/pprio.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports/pprio.h diff --git a/python/mozbuild/mozbuild/test/backend/data/final_target/both/moz.build b/python/mozbuild/mozbuild/test/backend/data/final_target/both/moz.build new file mode 100644 index 000000000..c926e3788 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/final_target/both/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +XPI_NAME = 'mycrazyxpi' +DIST_SUBDIR = 'asubdir' diff --git a/python/mozbuild/mozbuild/test/backend/data/final_target/dist-subdir/moz.build b/python/mozbuild/mozbuild/test/backend/data/final_target/dist-subdir/moz.build new file mode 100644 index 000000000..8dcf066a4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/final_target/dist-subdir/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIST_SUBDIR = 'asubdir' diff --git a/python/mozbuild/mozbuild/test/backend/data/final_target/final-target/moz.build b/python/mozbuild/mozbuild/test/backend/data/final_target/final-target/moz.build new file mode 100644 index 000000000..1d746eea5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/final_target/final-target/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FINAL_TARGET = 'random-final-target' diff --git a/python/mozbuild/mozbuild/test/backend/data/final_target/moz.build b/python/mozbuild/mozbuild/test/backend/data/final_target/moz.build new file mode 100644 index 000000000..280299475 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/final_target/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS += ['xpi-name', 'dist-subdir', 'both', 'final-target'] diff --git a/python/mozbuild/mozbuild/test/backend/data/final_target/xpi-name/moz.build b/python/mozbuild/mozbuild/test/backend/data/final_target/xpi-name/moz.build new file mode 100644 index 000000000..54bc30fec --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/final_target/xpi-name/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +XPI_NAME = 'mycrazyxpi' diff --git a/python/mozbuild/mozbuild/test/backend/data/generated-files/foo-data b/python/mozbuild/mozbuild/test/backend/data/generated-files/foo-data new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/generated-files/foo-data diff --git a/python/mozbuild/mozbuild/test/backend/data/generated-files/generate-bar.py b/python/mozbuild/mozbuild/test/backend/data/generated-files/generate-bar.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/generated-files/generate-bar.py diff --git a/python/mozbuild/mozbuild/test/backend/data/generated-files/generate-foo.py b/python/mozbuild/mozbuild/test/backend/data/generated-files/generate-foo.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/generated-files/generate-foo.py diff --git a/python/mozbuild/mozbuild/test/backend/data/generated-files/moz.build b/python/mozbuild/mozbuild/test/backend/data/generated-files/moz.build new file mode 100644 index 000000000..1fa389f51 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/generated-files/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +GENERATED_FILES += [ 'bar.c', 'foo.c', 'quux.c' ] + +bar = GENERATED_FILES['bar.c'] +bar.script = 'generate-bar.py:baz' + +foo = GENERATED_FILES['foo.c'] +foo.script = 'generate-foo.py' +foo.inputs = ['foo-data'] diff --git a/python/mozbuild/mozbuild/test/backend/data/generated_includes/moz.build b/python/mozbuild/mozbuild/test/backend/data/generated_includes/moz.build new file mode 100644 index 000000000..14deaf8cf --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/generated_includes/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +LOCAL_INCLUDES += ['!/bar/baz', '!foo'] diff --git a/python/mozbuild/mozbuild/test/backend/data/host-defines/moz.build b/python/mozbuild/mozbuild/test/backend/data/host-defines/moz.build new file mode 100644 index 000000000..30f8c160f --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/host-defines/moz.build @@ -0,0 +1,14 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +value = 'xyz' +HOST_DEFINES = { + 'FOO': True, +} + +HOST_DEFINES['BAZ'] = '"ab\'cd"' +HOST_DEFINES.update({ + 'BAR': 7, + 'VALUE': value, + 'QUX': False, +}) diff --git a/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/moz.build b/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/moz.build new file mode 100644 index 000000000..dbadef914 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +# We want to test recursion into the subdir, so do the real work in 'sub' +DIRS += ['sub'] diff --git a/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/sub/foo.h.in b/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/sub/foo.h.in new file mode 100644 index 000000000..da287dfca --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/sub/foo.h.in @@ -0,0 +1 @@ +#define MOZ_FOO @MOZ_FOO@ diff --git a/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/sub/moz.build b/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/sub/moz.build new file mode 100644 index 000000000..c2ef44079 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/install_substitute_config_files/sub/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +CONFIGURE_SUBST_FILES = ['foo.h'] + +EXPORTS.out += ['!foo.h'] diff --git a/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/bar/moz.build b/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/bar/moz.build new file mode 100644 index 000000000..f189212fd --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/bar/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +IPDL_SOURCES += [ + 'bar.ipdl', + 'bar2.ipdlh', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/foo/moz.build b/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/foo/moz.build new file mode 100644 index 000000000..4e1554559 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/foo/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +IPDL_SOURCES += [ + 'foo.ipdl', + 'foo2.ipdlh', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/moz.build b/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/moz.build new file mode 100644 index 000000000..03cf5e236 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/ipdl_sources/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DIRS += [ + 'bar', + 'foo', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/jar-manifests/moz.build b/python/mozbuild/mozbuild/test/backend/data/jar-manifests/moz.build new file mode 100644 index 000000000..7daa419f1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/jar-manifests/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ['jar.mn'] + diff --git a/python/mozbuild/mozbuild/test/backend/data/local_includes/bar/baz/dummy_file_for_nonempty_directory b/python/mozbuild/mozbuild/test/backend/data/local_includes/bar/baz/dummy_file_for_nonempty_directory new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/local_includes/bar/baz/dummy_file_for_nonempty_directory diff --git a/python/mozbuild/mozbuild/test/backend/data/local_includes/foo/dummy_file_for_nonempty_directory b/python/mozbuild/mozbuild/test/backend/data/local_includes/foo/dummy_file_for_nonempty_directory new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/local_includes/foo/dummy_file_for_nonempty_directory diff --git a/python/mozbuild/mozbuild/test/backend/data/local_includes/moz.build b/python/mozbuild/mozbuild/test/backend/data/local_includes/moz.build new file mode 100644 index 000000000..565c2bee6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/local_includes/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +LOCAL_INCLUDES += ['/bar/baz', 'foo'] diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/bar.res.in b/python/mozbuild/mozbuild/test/backend/data/resources/bar.res.in new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/bar.res.in diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/cursor.cur b/python/mozbuild/mozbuild/test/backend/data/resources/cursor.cur new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/cursor.cur diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/desktop1.ttf b/python/mozbuild/mozbuild/test/backend/data/resources/desktop1.ttf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/desktop1.ttf diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/desktop2.ttf b/python/mozbuild/mozbuild/test/backend/data/resources/desktop2.ttf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/desktop2.ttf diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/extra.manifest b/python/mozbuild/mozbuild/test/backend/data/resources/extra.manifest new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/extra.manifest diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/font1.ttf b/python/mozbuild/mozbuild/test/backend/data/resources/font1.ttf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/font1.ttf diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/font2.ttf b/python/mozbuild/mozbuild/test/backend/data/resources/font2.ttf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/font2.ttf diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/foo.res b/python/mozbuild/mozbuild/test/backend/data/resources/foo.res new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/foo.res diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/mobile.ttf b/python/mozbuild/mozbuild/test/backend/data/resources/mobile.ttf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/mobile.ttf diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/moz.build b/python/mozbuild/mozbuild/test/backend/data/resources/moz.build new file mode 100644 index 000000000..a5771c808 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/moz.build @@ -0,0 +1,9 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +RESOURCE_FILES += ['bar.res.in', 'foo.res'] +RESOURCE_FILES.cursors += ['cursor.cur'] +RESOURCE_FILES.fonts += ['font1.ttf', 'font2.ttf'] +RESOURCE_FILES.fonts.desktop += ['desktop1.ttf', 'desktop2.ttf'] +RESOURCE_FILES.fonts.mobile += ['mobile.ttf'] +RESOURCE_FILES.tests += ['extra.manifest', 'test.manifest'] diff --git a/python/mozbuild/mozbuild/test/backend/data/resources/test.manifest b/python/mozbuild/mozbuild/test/backend/data/resources/test.manifest new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/resources/test.manifest diff --git a/python/mozbuild/mozbuild/test/backend/data/sdk-files/bar.ico b/python/mozbuild/mozbuild/test/backend/data/sdk-files/bar.ico new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sdk-files/bar.ico diff --git a/python/mozbuild/mozbuild/test/backend/data/sdk-files/foo.ico b/python/mozbuild/mozbuild/test/backend/data/sdk-files/foo.ico new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sdk-files/foo.ico diff --git a/python/mozbuild/mozbuild/test/backend/data/sdk-files/moz.build b/python/mozbuild/mozbuild/test/backend/data/sdk-files/moz.build new file mode 100644 index 000000000..342987741 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sdk-files/moz.build @@ -0,0 +1,11 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +SDK_FILES += [ + 'bar.ico', + 'sub/quux.png', +] + +SDK_FILES.icons += [ + 'foo.ico', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/sdk-files/sub/quux.png b/python/mozbuild/mozbuild/test/backend/data/sdk-files/sub/quux.png new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sdk-files/sub/quux.png diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/bar.c b/python/mozbuild/mozbuild/test/backend/data/sources/bar.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/bar.c diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/bar.cpp b/python/mozbuild/mozbuild/test/backend/data/sources/bar.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/bar.cpp diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/bar.mm b/python/mozbuild/mozbuild/test/backend/data/sources/bar.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/bar.mm diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/bar.s b/python/mozbuild/mozbuild/test/backend/data/sources/bar.s new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/bar.s diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/baz.S b/python/mozbuild/mozbuild/test/backend/data/sources/baz.S new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/baz.S diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/foo.S b/python/mozbuild/mozbuild/test/backend/data/sources/foo.S new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/foo.S diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/foo.asm b/python/mozbuild/mozbuild/test/backend/data/sources/foo.asm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/foo.asm diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/foo.c b/python/mozbuild/mozbuild/test/backend/data/sources/foo.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/foo.c diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/foo.cpp b/python/mozbuild/mozbuild/test/backend/data/sources/foo.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/foo.cpp diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/foo.mm b/python/mozbuild/mozbuild/test/backend/data/sources/foo.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/foo.mm diff --git a/python/mozbuild/mozbuild/test/backend/data/sources/moz.build b/python/mozbuild/mozbuild/test/backend/data/sources/moz.build new file mode 100644 index 000000000..d31acae3d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/sources/moz.build @@ -0,0 +1,21 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + +Library('dummy') + +SOURCES += ['bar.s', 'foo.asm'] + +HOST_SOURCES += ['bar.cpp', 'foo.cpp'] +HOST_SOURCES += ['bar.c', 'foo.c'] + +SOURCES += ['bar.c', 'foo.c'] + +SOURCES += ['bar.mm', 'foo.mm'] + +SOURCES += ['baz.S', 'foo.S'] diff --git a/python/mozbuild/mozbuild/test/backend/data/stub0/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/stub0/Makefile.in new file mode 100644 index 000000000..02ff0a3f9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/stub0/Makefile.in @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FOO := foo diff --git a/python/mozbuild/mozbuild/test/backend/data/stub0/dir1/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/stub0/dir1/Makefile.in new file mode 100644 index 000000000..17c147d97 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/stub0/dir1/Makefile.in @@ -0,0 +1,7 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include $(DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/rules.mk + diff --git a/python/mozbuild/mozbuild/test/backend/data/stub0/dir1/moz.build b/python/mozbuild/mozbuild/test/backend/data/stub0/dir1/moz.build new file mode 100644 index 000000000..041381548 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/stub0/dir1/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + + diff --git a/python/mozbuild/mozbuild/test/backend/data/stub0/dir2/moz.build b/python/mozbuild/mozbuild/test/backend/data/stub0/dir2/moz.build new file mode 100644 index 000000000..32a37fe46 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/stub0/dir2/moz.build @@ -0,0 +1,4 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + diff --git a/python/mozbuild/mozbuild/test/backend/data/stub0/dir3/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/stub0/dir3/Makefile.in new file mode 100644 index 000000000..17c147d97 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/stub0/dir3/Makefile.in @@ -0,0 +1,7 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include $(DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/rules.mk + diff --git a/python/mozbuild/mozbuild/test/backend/data/stub0/dir3/moz.build b/python/mozbuild/mozbuild/test/backend/data/stub0/dir3/moz.build new file mode 100644 index 000000000..32a37fe46 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/stub0/dir3/moz.build @@ -0,0 +1,4 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + diff --git a/python/mozbuild/mozbuild/test/backend/data/stub0/moz.build b/python/mozbuild/mozbuild/test/backend/data/stub0/moz.build new file mode 100644 index 000000000..0d92bb7c3 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/stub0/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS += ['dir1'] +DIRS += ['dir2'] +TEST_DIRS += ['dir3'] diff --git a/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/Makefile.in new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/Makefile.in diff --git a/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/foo.in b/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/foo.in new file mode 100644 index 000000000..5331f1f05 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/foo.in @@ -0,0 +1 @@ +TEST = @MOZ_FOO@ diff --git a/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/moz.build b/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/moz.build new file mode 100644 index 000000000..01545c250 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/substitute_config_files/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +CONFIGURE_SUBST_FILES = ['foo'] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/another-file.sjs b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/another-file.sjs new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/another-file.sjs diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/browser.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/browser.ini new file mode 100644 index 000000000..4f1335d6b --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/browser.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + another-file.sjs + data/** + +[test_sub.js]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/data/one.txt b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/data/one.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/data/one.txt diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/data/two.txt b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/data/two.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/data/two.txt diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/test_sub.js b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/test_sub.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/child/test_sub.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/mochitest.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/mochitest.ini new file mode 100644 index 000000000..a9860f3de --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/mochitest.ini @@ -0,0 +1,8 @@ +[DEFAULT] +support-files = + support-file.txt + !/child/test_sub.js + !/child/another-file.sjs + !/child/data/** + +[test_foo.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/moz.build b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/moz.build new file mode 100644 index 000000000..1c1d064ea --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['mochitest.ini'] +BROWSER_CHROME_MANIFESTS += ['child/browser.ini'] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/support-file.txt b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/support-file.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/support-file.txt diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/test_foo.js b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/test_foo.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifest-shared-support/test_foo.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/mochitest1.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/mochitest1.ini new file mode 100644 index 000000000..1f9816a89 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/mochitest1.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = support-file.txt + +[test_foo.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/mochitest2.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/mochitest2.ini new file mode 100644 index 000000000..e2a2fc96a --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/mochitest2.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = support-file.txt + +[test_bar.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/moz.build b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/moz.build new file mode 100644 index 000000000..d10500f8d --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/moz.build @@ -0,0 +1,7 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += [ + 'mochitest1.ini', + 'mochitest2.ini', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/test_bar.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/test_bar.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/test_bar.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/test_foo.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/test_foo.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-duplicate-support-files/test_foo.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/instrumentation.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/instrumentation.ini new file mode 100644 index 000000000..03d4f794e --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/instrumentation.ini @@ -0,0 +1 @@ +[not_packaged.java] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/mochitest.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/mochitest.ini new file mode 100644 index 000000000..009b2b223 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/mochitest.ini @@ -0,0 +1 @@ +[mochitest.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/mochitest.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/mochitest.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/mochitest.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/moz.build b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/moz.build new file mode 100644 index 000000000..82dba29dc --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/moz.build @@ -0,0 +1,10 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += [ + 'mochitest.ini', +] + +ANDROID_INSTRUMENTATION_MANIFESTS += [ + 'instrumentation.ini', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/not_packaged.java b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/not_packaged.java new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-package-tests/not_packaged.java diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/dir1/test_bar.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/dir1/test_bar.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/dir1/test_bar.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/dir1/xpcshell.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/dir1/xpcshell.ini new file mode 100644 index 000000000..0cddad8ba --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/dir1/xpcshell.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[test_bar.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/mochitest.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/mochitest.ini new file mode 100644 index 000000000..81869e1fa --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/mochitest.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[mochitest.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/mochitest.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/mochitest.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/mochitest.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/moz.build b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/moz.build new file mode 100644 index 000000000..d004cdd0f --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/moz.build @@ -0,0 +1,9 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +XPCSHELL_TESTS_MANIFESTS += [ + 'dir1/xpcshell.ini', + 'xpcshell.ini', +] + +MOCHITEST_MANIFESTS += ['mochitest.ini'] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/xpcshell.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/xpcshell.ini new file mode 100644 index 000000000..f6a5351e9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/xpcshell.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = support/** + +[xpcshell.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/xpcshell.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/xpcshell.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-written/xpcshell.js diff --git a/python/mozbuild/mozbuild/test/backend/data/test_config/file.in b/python/mozbuild/mozbuild/test/backend/data/test_config/file.in new file mode 100644 index 000000000..07aa30deb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test_config/file.in @@ -0,0 +1,3 @@ +#ifdef foo +@foo@ +@bar@ diff --git a/python/mozbuild/mozbuild/test/backend/data/test_config/moz.build b/python/mozbuild/mozbuild/test/backend/data/test_config/moz.build new file mode 100644 index 000000000..f0c357aaf --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test_config/moz.build @@ -0,0 +1,3 @@ +CONFIGURE_SUBST_FILES = [ + 'file', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/Makefile.in new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/Makefile.in diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build new file mode 100644 index 000000000..36a2603b1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +NO_VISIBILITY_FLAGS = True + +DELAYLOAD_DLLS = ['foo.dll', 'bar.dll'] + +RCFILE = 'foo.rc' +RESFILE = 'bar.res' +RCINCLUDE = 'bar.rc' +DEFFILE = 'baz.def' + +CFLAGS += ['-fno-exceptions', '-w'] +CXXFLAGS += ['-fcxx-exceptions', '-option with spaces'] +LDFLAGS += ['-ld flag with spaces', '-x'] +HOST_CFLAGS += ['-funroll-loops', '-wall'] +HOST_CXXFLAGS += ['-funroll-loops-harder', '-wall-day-everyday'] +WIN32_EXE_LDFLAGS += ['-subsystem:console'] + +DISABLE_STL_WRAPPING = True + +ALLOW_COMPILER_WARNINGS = True diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.c b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.c diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.cpp b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.cpp diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.mm b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test1.mm diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.c b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.c diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.cpp b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.cpp diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.mm b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/test2.mm diff --git a/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/bar.cpp b/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/bar.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/bar.cpp diff --git a/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/foo.cpp b/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/foo.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/foo.cpp diff --git a/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/moz.build b/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/moz.build new file mode 100644 index 000000000..b77e67ade --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/visual-studio/dir1/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FINAL_LIBRARY = 'test' +SOURCES += ['bar.cpp', 'foo.cpp'] +LOCAL_INCLUDES += ['/includeA/foo'] +DEFINES['DEFINEFOO'] = True +DEFINES['DEFINEBAR'] = 'bar' diff --git a/python/mozbuild/mozbuild/test/backend/data/visual-studio/moz.build b/python/mozbuild/mozbuild/test/backend/data/visual-studio/moz.build new file mode 100644 index 000000000..d339b48c4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/visual-studio/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS += ['dir1'] + +Library('test') diff --git a/python/mozbuild/mozbuild/test/backend/data/xpidl/config/makefiles/xpidl/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/xpidl/config/makefiles/xpidl/Makefile.in new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/xpidl/config/makefiles/xpidl/Makefile.in diff --git a/python/mozbuild/mozbuild/test/backend/data/xpidl/moz.build b/python/mozbuild/mozbuild/test/backend/data/xpidl/moz.build new file mode 100644 index 000000000..d49efde26 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/xpidl/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +XPIDL_MODULE = 'my_module' +XPIDL_SOURCES = ['bar.idl', 'foo.idl'] diff --git a/python/mozbuild/mozbuild/test/backend/test_android_eclipse.py b/python/mozbuild/mozbuild/test/backend/test_android_eclipse.py new file mode 100644 index 000000000..c4e9221c9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/test_android_eclipse.py @@ -0,0 +1,153 @@ +# 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 json +import os +import unittest + +from mozbuild.backend.android_eclipse import AndroidEclipseBackend +from mozbuild.frontend.emitter import TreeMetadataEmitter +from mozbuild.frontend.reader import BuildReader +from mozbuild.test.backend.common import BackendTester +from mozpack.manifests import InstallManifest +from mozunit import main + +import mozpack.path as mozpath + +class TestAndroidEclipseBackend(BackendTester): + def __init__(self, *args, **kwargs): + BackendTester.__init__(self, *args, **kwargs) + self.env = None + + def assertExists(self, *args): + p = mozpath.join(self.env.topobjdir, 'android_eclipse', *args) + self.assertTrue(os.path.exists(p), "Path %s exists" % p) + + def assertNotExists(self, *args): + p = mozpath.join(self.env.topobjdir, 'android_eclipse', *args) + self.assertFalse(os.path.exists(p), "Path %s does not exist" % p) + + def test_library_project_files(self): + """Ensure we generate reasonable files for library projects.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + for f in ['.classpath', + '.project', + '.settings', + 'AndroidManifest.xml', + 'project.properties']: + self.assertExists('library1', f) + + def test_main_project_files(self): + """Ensure we generate reasonable files for main (non-library) projects.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + for f in ['.classpath', + '.project', + '.settings', + 'gen', + 'lint.xml', + 'project.properties']: + self.assertExists('main1', f) + + def test_library_manifest(self): + """Ensure we generate manifest for library projects.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertExists('library1', 'AndroidManifest.xml') + + def test_classpathentries(self): + """Ensure we produce reasonable classpathentries.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertExists('main3', '.classpath') + # This is brittle but simple. + with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main3', '.classpath'), 'rt') as fh: + lines = fh.readlines() + lines = [line.strip() for line in lines] + self.assertIn('<classpathentry including="**/*.java" kind="src" path="a" />', lines) + self.assertIn('<classpathentry excluding="b/Excludes.java|b/Excludes2.java" including="**/*.java" kind="src" path="b" />', lines) + self.assertIn('<classpathentry including="**/*.java" kind="src" path="c"><attributes><attribute name="ignore_optional_problems" value="true" /></attributes></classpathentry>', lines) + + def test_library_project_setting(self): + """Ensure we declare a library project correctly.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + + self.assertExists('library1', 'project.properties') + with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'library1', 'project.properties'), 'rt') as fh: + lines = fh.readlines() + lines = [line.strip() for line in lines] + self.assertIn('android.library=true', lines) + + self.assertExists('main1', 'project.properties') + with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main1', 'project.properties'), 'rt') as fh: + lines = fh.readlines() + lines = [line.strip() for line in lines] + self.assertNotIn('android.library=true', lines) + + def test_referenced_projects(self): + """Ensure we reference another project correctly.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertExists('main4', '.classpath') + # This is brittle but simple. + with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main4', '.classpath'), 'rt') as fh: + lines = fh.readlines() + lines = [line.strip() for line in lines] + self.assertIn('<classpathentry combineaccessrules="false" kind="src" path="/library1" />', lines) + + def test_extra_jars(self): + """Ensure we add class path entries to extra jars iff asked to.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertExists('main2', '.classpath') + # This is brittle but simple. + with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main2', '.classpath'), 'rt') as fh: + lines = fh.readlines() + lines = [line.strip() for line in lines] + self.assertIn('<classpathentry exported="true" kind="lib" path="%s/main2/extra.jar" />' % self.env.topsrcdir, lines) + + def test_included_projects(self): + """Ensure we include another project correctly.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertExists('main4', 'project.properties') + # This is brittle but simple. + with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main4', 'project.properties'), 'rt') as fh: + lines = fh.readlines() + lines = [line.strip() for line in lines] + self.assertIn('android.library.reference.1=library2', lines) + + def assertInManifest(self, project_name, *args): + manifest_path = mozpath.join(self.env.topobjdir, 'android_eclipse', '%s.manifest' % project_name) + manifest = InstallManifest(manifest_path) + for arg in args: + self.assertIn(arg, manifest, '%s in manifest for project %s' % (arg, project_name)) + + def assertNotInManifest(self, project_name, *args): + manifest_path = mozpath.join(self.env.topobjdir, 'android_eclipse', '%s.manifest' % project_name) + manifest = InstallManifest(manifest_path) + for arg in args: + self.assertNotIn(arg, manifest, '%s not in manifest for project %s' % (arg, project_name)) + + def test_manifest_main_manifest(self): + """Ensure we symlink manifest if asked to for main projects.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertInManifest('main1', 'AndroidManifest.xml') + + def test_manifest_res(self): + """Ensure we symlink res/ iff asked to.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertInManifest('library1', 'res') + self.assertNotInManifest('library2', 'res') + + def test_manifest_classpathentries(self): + """Ensure we symlink classpathentries correctly.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertInManifest('main3', 'a/a', 'b', 'd/e') + + def test_manifest_assets(self): + """Ensure we symlink assets/ iff asked to.""" + self.env = self._consume('android_eclipse', AndroidEclipseBackend) + self.assertNotInManifest('main1', 'assets') + self.assertInManifest('main2', 'assets') + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/backend/test_build.py b/python/mozbuild/mozbuild/test/backend/test_build.py new file mode 100644 index 000000000..d3f5fb6a9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/test_build.py @@ -0,0 +1,233 @@ +# 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, print_function + +import buildconfig +import os +import shutil +import sys +import unittest +import mozpack.path as mozpath +from contextlib import contextmanager +from mozunit import main +from mozbuild.backend import get_backend_class +from mozbuild.backend.configenvironment import ConfigEnvironment +from mozbuild.backend.recursivemake import RecursiveMakeBackend +from mozbuild.backend.fastermake import FasterMakeBackend +from mozbuild.base import MozbuildObject +from mozbuild.frontend.emitter import TreeMetadataEmitter +from mozbuild.frontend.reader import BuildReader +from mozbuild.util import ensureParentDir +from mozpack.files import FileFinder +from tempfile import mkdtemp + + +BASE_SUBSTS = [ + ('PYTHON', mozpath.normsep(sys.executable)), +] + + +class TestBuild(unittest.TestCase): + def setUp(self): + self._old_env = dict(os.environ) + os.environ.pop('MOZCONFIG', None) + os.environ.pop('MOZ_OBJDIR', None) + + def tearDown(self): + os.environ.clear() + os.environ.update(self._old_env) + + @contextmanager + def do_test_backend(self, *backends, **kwargs): + topobjdir = mkdtemp() + try: + config = ConfigEnvironment(buildconfig.topsrcdir, topobjdir, + **kwargs) + reader = BuildReader(config) + emitter = TreeMetadataEmitter(config) + moz_build = mozpath.join(config.topsrcdir, 'test.mozbuild') + definitions = list(emitter.emit( + reader.read_mozbuild(moz_build, config))) + for backend in backends: + backend(config).consume(definitions) + + yield config + except: + raise + finally: + if not os.environ.get('MOZ_NO_CLEANUP'): + shutil.rmtree(topobjdir) + + @contextmanager + def line_handler(self): + lines = [] + + def handle_make_line(line): + lines.append(line) + + try: + yield handle_make_line + except: + print('\n'.join(lines)) + raise + + if os.environ.get('MOZ_VERBOSE_MAKE'): + print('\n'.join(lines)) + + def test_recursive_make(self): + substs = list(BASE_SUBSTS) + with self.do_test_backend(RecursiveMakeBackend, + substs=substs) as config: + build = MozbuildObject(config.topsrcdir, None, None, + config.topobjdir) + overrides = [ + 'install_manifest_depends=', + 'MOZ_JAR_MAKER_FILE_FORMAT=flat', + 'TEST_MOZBUILD=1', + ] + with self.line_handler() as handle_make_line: + build._run_make(directory=config.topobjdir, target=overrides, + silent=False, line_handler=handle_make_line) + + self.validate(config) + + def test_faster_recursive_make(self): + substs = list(BASE_SUBSTS) + [ + ('BUILD_BACKENDS', 'FasterMake+RecursiveMake'), + ] + with self.do_test_backend(get_backend_class( + 'FasterMake+RecursiveMake'), substs=substs) as config: + buildid = mozpath.join(config.topobjdir, 'config', 'buildid') + ensureParentDir(buildid) + with open(buildid, 'w') as fh: + fh.write('20100101012345\n') + + build = MozbuildObject(config.topsrcdir, None, None, + config.topobjdir) + overrides = [ + 'install_manifest_depends=', + 'MOZ_JAR_MAKER_FILE_FORMAT=flat', + 'TEST_MOZBUILD=1', + ] + with self.line_handler() as handle_make_line: + build._run_make(directory=config.topobjdir, target=overrides, + silent=False, line_handler=handle_make_line) + + self.validate(config) + + def test_faster_make(self): + substs = list(BASE_SUBSTS) + [ + ('MOZ_BUILD_APP', 'dummy_app'), + ('MOZ_WIDGET_TOOLKIT', 'dummy_widget'), + ] + with self.do_test_backend(RecursiveMakeBackend, FasterMakeBackend, + substs=substs) as config: + buildid = mozpath.join(config.topobjdir, 'config', 'buildid') + ensureParentDir(buildid) + with open(buildid, 'w') as fh: + fh.write('20100101012345\n') + + build = MozbuildObject(config.topsrcdir, None, None, + config.topobjdir) + overrides = [ + 'TEST_MOZBUILD=1', + ] + with self.line_handler() as handle_make_line: + build._run_make(directory=mozpath.join(config.topobjdir, + 'faster'), + target=overrides, silent=False, + line_handler=handle_make_line) + + self.validate(config) + + def validate(self, config): + self.maxDiff = None + test_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'data', 'build') + os.sep + + # We want unicode instances out of the files, because having plain str + # makes assertEqual diff output in case of error extra verbose because + # of the difference in type. + result = { + p: f.open().read().decode('utf-8') + for p, f in FileFinder(mozpath.join(config.topobjdir, 'dist')) + } + self.assertTrue(len(result)) + self.assertEqual(result, { + 'bin/baz.ini': 'baz.ini: FOO is foo\n', + 'bin/child/bar.ini': 'bar.ini\n', + 'bin/child2/foo.css': 'foo.css: FOO is foo\n', + 'bin/child2/qux.ini': 'qux.ini: BAR is not defined\n', + 'bin/chrome.manifest': + 'manifest chrome/foo.manifest\n' + 'manifest components/components.manifest\n', + 'bin/chrome/foo.manifest': + 'content bar foo/child/\n' + 'content foo foo/\n' + 'override chrome://foo/bar.svg#hello ' + 'chrome://bar/bar.svg#hello\n', + 'bin/chrome/foo/bar.js': 'bar.js\n', + 'bin/chrome/foo/child/baz.jsm': + '//@line 2 "%sbaz.jsm"\nbaz.jsm: FOO is foo\n' % (test_path), + 'bin/chrome/foo/child/hoge.js': + '//@line 2 "%sbar.js"\nbar.js: FOO is foo\n' % (test_path), + 'bin/chrome/foo/foo.css': 'foo.css: FOO is foo\n', + 'bin/chrome/foo/foo.js': 'foo.js\n', + 'bin/chrome/foo/qux.js': 'bar.js\n', + 'bin/components/bar.js': + '//@line 2 "%sbar.js"\nbar.js: FOO is foo\n' % (test_path), + 'bin/components/components.manifest': + 'component {foo} foo.js\ncomponent {bar} bar.js\n', + 'bin/components/foo.js': 'foo.js\n', + 'bin/defaults/pref/prefs.js': 'prefs.js\n', + 'bin/foo.ini': 'foo.ini\n', + 'bin/modules/baz.jsm': + '//@line 2 "%sbaz.jsm"\nbaz.jsm: FOO is foo\n' % (test_path), + 'bin/modules/child/bar.jsm': 'bar.jsm\n', + 'bin/modules/child2/qux.jsm': + '//@line 4 "%squx.jsm"\nqux.jsm: BAR is not defined\n' + % (test_path), + 'bin/modules/foo.jsm': 'foo.jsm\n', + 'bin/res/resource': 'resource\n', + 'bin/res/child/resource2': 'resource2\n', + + 'bin/app/baz.ini': 'baz.ini: FOO is bar\n', + 'bin/app/child/bar.ini': 'bar.ini\n', + 'bin/app/child2/qux.ini': 'qux.ini: BAR is defined\n', + 'bin/app/chrome.manifest': + 'manifest chrome/foo.manifest\n' + 'manifest components/components.manifest\n', + 'bin/app/chrome/foo.manifest': + 'content bar foo/child/\n' + 'content foo foo/\n' + 'override chrome://foo/bar.svg#hello ' + 'chrome://bar/bar.svg#hello\n', + 'bin/app/chrome/foo/bar.js': 'bar.js\n', + 'bin/app/chrome/foo/child/baz.jsm': + '//@line 2 "%sbaz.jsm"\nbaz.jsm: FOO is bar\n' % (test_path), + 'bin/app/chrome/foo/child/hoge.js': + '//@line 2 "%sbar.js"\nbar.js: FOO is bar\n' % (test_path), + 'bin/app/chrome/foo/foo.css': 'foo.css: FOO is bar\n', + 'bin/app/chrome/foo/foo.js': 'foo.js\n', + 'bin/app/chrome/foo/qux.js': 'bar.js\n', + 'bin/app/components/bar.js': + '//@line 2 "%sbar.js"\nbar.js: FOO is bar\n' % (test_path), + 'bin/app/components/components.manifest': + 'component {foo} foo.js\ncomponent {bar} bar.js\n', + 'bin/app/components/foo.js': 'foo.js\n', + 'bin/app/defaults/preferences/prefs.js': 'prefs.js\n', + 'bin/app/foo.css': 'foo.css: FOO is bar\n', + 'bin/app/foo.ini': 'foo.ini\n', + 'bin/app/modules/baz.jsm': + '//@line 2 "%sbaz.jsm"\nbaz.jsm: FOO is bar\n' % (test_path), + 'bin/app/modules/child/bar.jsm': 'bar.jsm\n', + 'bin/app/modules/child2/qux.jsm': + '//@line 2 "%squx.jsm"\nqux.jsm: BAR is defined\n' + % (test_path), + 'bin/app/modules/foo.jsm': 'foo.jsm\n', + }) + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/backend/test_configenvironment.py b/python/mozbuild/mozbuild/test/backend/test_configenvironment.py new file mode 100644 index 000000000..95593e186 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/test_configenvironment.py @@ -0,0 +1,63 @@ +# 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/. + +import os, posixpath +from StringIO import StringIO +import unittest +from mozunit import main, MockedOpen + +import mozbuild.backend.configenvironment as ConfigStatus + +from mozbuild.util import ReadOnlyDict + +import mozpack.path as mozpath + + +class ConfigEnvironment(ConfigStatus.ConfigEnvironment): + def __init__(self, *args, **kwargs): + ConfigStatus.ConfigEnvironment.__init__(self, *args, **kwargs) + # Be helpful to unit tests + if not 'top_srcdir' in self.substs: + if os.path.isabs(self.topsrcdir): + top_srcdir = self.topsrcdir.replace(os.sep, '/') + else: + top_srcdir = mozpath.relpath(self.topsrcdir, self.topobjdir).replace(os.sep, '/') + + d = dict(self.substs) + d['top_srcdir'] = top_srcdir + self.substs = ReadOnlyDict(d) + + d = dict(self.substs_unicode) + d[u'top_srcdir'] = top_srcdir.decode('utf-8') + self.substs_unicode = ReadOnlyDict(d) + + +class TestEnvironment(unittest.TestCase): + def test_auto_substs(self): + '''Test the automatically set values of ACDEFINES, ALLSUBSTS + and ALLEMPTYSUBSTS. + ''' + env = ConfigEnvironment('.', '.', + defines = { 'foo': 'bar', 'baz': 'qux 42', + 'abc': "d'e'f", 'extra': 'foobar' }, + non_global_defines = ['extra', 'ignore'], + substs = { 'FOO': 'bar', 'FOOBAR': '', 'ABC': 'def', + 'bar': 'baz qux', 'zzz': '"abc def"', + 'qux': '' }) + # non_global_defines should be filtered out in ACDEFINES. + # Original order of the defines need to be respected in ACDEFINES + self.assertEqual(env.substs['ACDEFINES'], """-Dabc='d'\\''e'\\''f' -Dbaz='qux 42' -Dfoo=bar""") + # Likewise for ALLSUBSTS, which also must contain ACDEFINES + self.assertEqual(env.substs['ALLSUBSTS'], '''ABC = def +ACDEFINES = -Dabc='d'\\''e'\\''f' -Dbaz='qux 42' -Dfoo=bar +FOO = bar +bar = baz qux +zzz = "abc def"''') + # ALLEMPTYSUBSTS contains all substs with no value. + self.assertEqual(env.substs['ALLEMPTYSUBSTS'], '''FOOBAR = +qux =''') + + +if __name__ == "__main__": + main() diff --git a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py new file mode 100644 index 000000000..87f50f497 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py @@ -0,0 +1,942 @@ +# 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 cPickle as pickle +import json +import os +import unittest + +from mozpack.manifests import ( + InstallManifest, +) +from mozunit import main + +from mozbuild.backend.recursivemake import ( + RecursiveMakeBackend, + RecursiveMakeTraversal, +) +from mozbuild.frontend.emitter import TreeMetadataEmitter +from mozbuild.frontend.reader import BuildReader + +from mozbuild.test.backend.common import BackendTester + +import mozpack.path as mozpath + + +class TestRecursiveMakeTraversal(unittest.TestCase): + def test_traversal(self): + traversal = RecursiveMakeTraversal() + traversal.add('', dirs=['A', 'B', 'C']) + traversal.add('', dirs=['D']) + traversal.add('A') + traversal.add('B', dirs=['E', 'F']) + traversal.add('C', dirs=['G', 'H']) + traversal.add('D', dirs=['I', 'K']) + traversal.add('D', dirs=['J', 'L']) + traversal.add('E') + traversal.add('F') + traversal.add('G') + traversal.add('H') + traversal.add('I', dirs=['M', 'N']) + traversal.add('J', dirs=['O', 'P']) + traversal.add('K', dirs=['Q', 'R']) + traversal.add('L', dirs=['S']) + traversal.add('M') + traversal.add('N', dirs=['T']) + traversal.add('O') + traversal.add('P', dirs=['U']) + traversal.add('Q') + traversal.add('R', dirs=['V']) + traversal.add('S', dirs=['W']) + traversal.add('T') + traversal.add('U') + traversal.add('V') + traversal.add('W', dirs=['X']) + traversal.add('X') + + parallels = set(('G', 'H', 'I', 'J', 'O', 'P', 'Q', 'R', 'U')) + def filter(current, subdirs): + return (current, [d for d in subdirs.dirs if d in parallels], + [d for d in subdirs.dirs if d not in parallels]) + + start, deps = traversal.compute_dependencies(filter) + self.assertEqual(start, ('X',)) + self.maxDiff = None + self.assertEqual(deps, { + 'A': ('',), + 'B': ('A',), + 'C': ('F',), + 'D': ('G', 'H'), + 'E': ('B',), + 'F': ('E',), + 'G': ('C',), + 'H': ('C',), + 'I': ('D',), + 'J': ('D',), + 'K': ('T', 'O', 'U'), + 'L': ('Q', 'V'), + 'M': ('I',), + 'N': ('M',), + 'O': ('J',), + 'P': ('J',), + 'Q': ('K',), + 'R': ('K',), + 'S': ('L',), + 'T': ('N',), + 'U': ('P',), + 'V': ('R',), + 'W': ('S',), + 'X': ('W',), + }) + + self.assertEqual(list(traversal.traverse('', filter)), + ['', 'A', 'B', 'E', 'F', 'C', 'G', 'H', 'D', 'I', + 'M', 'N', 'T', 'J', 'O', 'P', 'U', 'K', 'Q', 'R', + 'V', 'L', 'S', 'W', 'X']) + + self.assertEqual(list(traversal.traverse('C', filter)), + ['C', 'G', 'H']) + + def test_traversal_2(self): + traversal = RecursiveMakeTraversal() + traversal.add('', dirs=['A', 'B', 'C']) + traversal.add('A') + traversal.add('B', dirs=['D', 'E', 'F']) + traversal.add('C', dirs=['G', 'H', 'I']) + traversal.add('D') + traversal.add('E') + traversal.add('F') + traversal.add('G') + traversal.add('H') + traversal.add('I') + + start, deps = traversal.compute_dependencies() + self.assertEqual(start, ('I',)) + self.assertEqual(deps, { + 'A': ('',), + 'B': ('A',), + 'C': ('F',), + 'D': ('B',), + 'E': ('D',), + 'F': ('E',), + 'G': ('C',), + 'H': ('G',), + 'I': ('H',), + }) + + def test_traversal_filter(self): + traversal = RecursiveMakeTraversal() + traversal.add('', dirs=['A', 'B', 'C']) + traversal.add('A') + traversal.add('B', dirs=['D', 'E', 'F']) + traversal.add('C', dirs=['G', 'H', 'I']) + traversal.add('D') + traversal.add('E') + traversal.add('F') + traversal.add('G') + traversal.add('H') + traversal.add('I') + + def filter(current, subdirs): + if current == 'B': + current = None + return current, [], subdirs.dirs + + start, deps = traversal.compute_dependencies(filter) + self.assertEqual(start, ('I',)) + self.assertEqual(deps, { + 'A': ('',), + 'C': ('F',), + 'D': ('A',), + 'E': ('D',), + 'F': ('E',), + 'G': ('C',), + 'H': ('G',), + 'I': ('H',), + }) + +class TestRecursiveMakeBackend(BackendTester): + def test_basic(self): + """Ensure the RecursiveMakeBackend works without error.""" + env = self._consume('stub0', RecursiveMakeBackend) + self.assertTrue(os.path.exists(mozpath.join(env.topobjdir, + 'backend.RecursiveMakeBackend'))) + self.assertTrue(os.path.exists(mozpath.join(env.topobjdir, + 'backend.RecursiveMakeBackend.in'))) + + def test_output_files(self): + """Ensure proper files are generated.""" + env = self._consume('stub0', RecursiveMakeBackend) + + expected = ['', 'dir1', 'dir2'] + + for d in expected: + out_makefile = mozpath.join(env.topobjdir, d, 'Makefile') + out_backend = mozpath.join(env.topobjdir, d, 'backend.mk') + + self.assertTrue(os.path.exists(out_makefile)) + self.assertTrue(os.path.exists(out_backend)) + + def test_makefile_conversion(self): + """Ensure Makefile.in is converted properly.""" + env = self._consume('stub0', RecursiveMakeBackend) + + p = mozpath.join(env.topobjdir, 'Makefile') + + lines = [l.strip() for l in open(p, 'rt').readlines()[1:] if not l.startswith('#')] + self.assertEqual(lines, [ + 'DEPTH := .', + 'topobjdir := %s' % env.topobjdir, + 'topsrcdir := %s' % env.topsrcdir, + 'srcdir := %s' % env.topsrcdir, + 'VPATH := %s' % env.topsrcdir, + 'relativesrcdir := .', + 'include $(DEPTH)/config/autoconf.mk', + '', + 'FOO := foo', + '', + 'include $(topsrcdir)/config/recurse.mk', + ]) + + def test_missing_makefile_in(self): + """Ensure missing Makefile.in results in Makefile creation.""" + env = self._consume('stub0', RecursiveMakeBackend) + + p = mozpath.join(env.topobjdir, 'dir2', 'Makefile') + self.assertTrue(os.path.exists(p)) + + lines = [l.strip() for l in open(p, 'rt').readlines()] + self.assertEqual(len(lines), 10) + + self.assertTrue(lines[0].startswith('# THIS FILE WAS AUTOMATICALLY')) + + def test_backend_mk(self): + """Ensure backend.mk file is written out properly.""" + env = self._consume('stub0', RecursiveMakeBackend) + + p = mozpath.join(env.topobjdir, 'backend.mk') + + lines = [l.strip() for l in open(p, 'rt').readlines()[2:]] + self.assertEqual(lines, [ + 'DIRS := dir1 dir2', + ]) + + # Make env.substs writable to add ENABLE_TESTS + env.substs = dict(env.substs) + env.substs['ENABLE_TESTS'] = '1' + self._consume('stub0', RecursiveMakeBackend, env=env) + p = mozpath.join(env.topobjdir, 'backend.mk') + + lines = [l.strip() for l in open(p, 'rt').readlines()[2:]] + self.assertEqual(lines, [ + 'DIRS := dir1 dir2 dir3', + ]) + + def test_mtime_no_change(self): + """Ensure mtime is not updated if file content does not change.""" + + env = self._consume('stub0', RecursiveMakeBackend) + + makefile_path = mozpath.join(env.topobjdir, 'Makefile') + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + makefile_mtime = os.path.getmtime(makefile_path) + backend_mtime = os.path.getmtime(backend_path) + + reader = BuildReader(env) + emitter = TreeMetadataEmitter(env) + backend = RecursiveMakeBackend(env) + backend.consume(emitter.emit(reader.read_topsrcdir())) + + self.assertEqual(os.path.getmtime(makefile_path), makefile_mtime) + self.assertEqual(os.path.getmtime(backend_path), backend_mtime) + + def test_substitute_config_files(self): + """Ensure substituted config files are produced.""" + env = self._consume('substitute_config_files', RecursiveMakeBackend) + + p = mozpath.join(env.topobjdir, 'foo') + self.assertTrue(os.path.exists(p)) + lines = [l.strip() for l in open(p, 'rt').readlines()] + self.assertEqual(lines, [ + 'TEST = foo', + ]) + + def test_install_substitute_config_files(self): + """Ensure we recurse into the dirs that install substituted config files.""" + env = self._consume('install_substitute_config_files', RecursiveMakeBackend) + + root_deps_path = mozpath.join(env.topobjdir, 'root-deps.mk') + lines = [l.strip() for l in open(root_deps_path, 'rt').readlines()] + + # Make sure we actually recurse into the sub directory during export to + # install the subst file. + self.assertTrue(any(l == 'recurse_export: sub/export' for l in lines)) + + def test_variable_passthru(self): + """Ensure variable passthru is written out correctly.""" + env = self._consume('variable_passthru', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + expected = { + 'ALLOW_COMPILER_WARNINGS': [ + 'ALLOW_COMPILER_WARNINGS := 1', + ], + 'DISABLE_STL_WRAPPING': [ + 'DISABLE_STL_WRAPPING := 1', + ], + 'VISIBILITY_FLAGS': [ + 'VISIBILITY_FLAGS :=', + ], + 'RCFILE': [ + 'RCFILE := foo.rc', + ], + 'RESFILE': [ + 'RESFILE := bar.res', + ], + 'RCINCLUDE': [ + 'RCINCLUDE := bar.rc', + ], + 'DEFFILE': [ + 'DEFFILE := baz.def', + ], + 'MOZBUILD_CFLAGS': [ + 'MOZBUILD_CFLAGS += -fno-exceptions', + 'MOZBUILD_CFLAGS += -w', + ], + 'MOZBUILD_CXXFLAGS': [ + 'MOZBUILD_CXXFLAGS += -fcxx-exceptions', + "MOZBUILD_CXXFLAGS += '-option with spaces'", + ], + 'MOZBUILD_LDFLAGS': [ + "MOZBUILD_LDFLAGS += '-ld flag with spaces'", + 'MOZBUILD_LDFLAGS += -x', + 'MOZBUILD_LDFLAGS += -DELAYLOAD:foo.dll', + 'MOZBUILD_LDFLAGS += -DELAYLOAD:bar.dll', + ], + 'MOZBUILD_HOST_CFLAGS': [ + 'MOZBUILD_HOST_CFLAGS += -funroll-loops', + 'MOZBUILD_HOST_CFLAGS += -wall', + ], + 'MOZBUILD_HOST_CXXFLAGS': [ + 'MOZBUILD_HOST_CXXFLAGS += -funroll-loops-harder', + 'MOZBUILD_HOST_CXXFLAGS += -wall-day-everyday', + ], + 'WIN32_EXE_LDFLAGS': [ + 'WIN32_EXE_LDFLAGS += -subsystem:console', + ], + } + + for var, val in expected.items(): + # print("test_variable_passthru[%s]" % (var)) + found = [str for str in lines if str.startswith(var)] + self.assertEqual(found, val) + + def test_sources(self): + """Ensure SOURCES and HOST_SOURCES are handled properly.""" + env = self._consume('sources', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + expected = { + 'ASFILES': [ + 'ASFILES += bar.s', + 'ASFILES += foo.asm', + ], + 'CMMSRCS': [ + 'CMMSRCS += bar.mm', + 'CMMSRCS += foo.mm', + ], + 'CSRCS': [ + 'CSRCS += bar.c', + 'CSRCS += foo.c', + ], + 'HOST_CPPSRCS': [ + 'HOST_CPPSRCS += bar.cpp', + 'HOST_CPPSRCS += foo.cpp', + ], + 'HOST_CSRCS': [ + 'HOST_CSRCS += bar.c', + 'HOST_CSRCS += foo.c', + ], + 'SSRCS': [ + 'SSRCS += baz.S', + 'SSRCS += foo.S', + ], + } + + for var, val in expected.items(): + found = [str for str in lines if str.startswith(var)] + self.assertEqual(found, val) + + def test_exports(self): + """Ensure EXPORTS is handled properly.""" + env = self._consume('exports', RecursiveMakeBackend) + + # EXPORTS files should appear in the dist_include install manifest. + m = InstallManifest(path=mozpath.join(env.topobjdir, + '_build_manifests', 'install', 'dist_include')) + self.assertEqual(len(m), 7) + self.assertIn('foo.h', m) + self.assertIn('mozilla/mozilla1.h', m) + self.assertIn('mozilla/dom/dom2.h', m) + + def test_generated_files(self): + """Ensure GENERATED_FILES is handled properly.""" + env = self._consume('generated-files', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + expected = [ + 'export:: bar.c', + 'GARBAGE += bar.c', + 'EXTRA_MDDEPEND_FILES += bar.c.pp', + 'bar.c: %s/generate-bar.py' % env.topsrcdir, + '$(REPORT_BUILD)', + '$(call py_action,file_generate,%s/generate-bar.py baz bar.c $(MDDEPDIR)/bar.c.pp)' % env.topsrcdir, + '', + 'export:: foo.c', + 'GARBAGE += foo.c', + 'EXTRA_MDDEPEND_FILES += foo.c.pp', + 'foo.c: %s/generate-foo.py $(srcdir)/foo-data' % (env.topsrcdir), + '$(REPORT_BUILD)', + '$(call py_action,file_generate,%s/generate-foo.py main foo.c $(MDDEPDIR)/foo.c.pp $(srcdir)/foo-data)' % (env.topsrcdir), + '', + 'export:: quux.c', + 'GARBAGE += quux.c', + 'EXTRA_MDDEPEND_FILES += quux.c.pp', + ] + + self.maxDiff = None + self.assertEqual(lines, expected) + + def test_exports_generated(self): + """Ensure EXPORTS that are listed in GENERATED_FILES + are handled properly.""" + env = self._consume('exports-generated', RecursiveMakeBackend) + + # EXPORTS files should appear in the dist_include install manifest. + m = InstallManifest(path=mozpath.join(env.topobjdir, + '_build_manifests', 'install', 'dist_include')) + self.assertEqual(len(m), 8) + self.assertIn('foo.h', m) + self.assertIn('mozilla/mozilla1.h', m) + self.assertIn('mozilla/dom/dom1.h', m) + self.assertIn('gfx/gfx.h', m) + self.assertIn('bar.h', m) + self.assertIn('mozilla/mozilla2.h', m) + self.assertIn('mozilla/dom/dom2.h', m) + self.assertIn('mozilla/dom/dom3.h', m) + # EXPORTS files that are also GENERATED_FILES should be handled as + # INSTALL_TARGETS. + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + expected = [ + 'export:: bar.h', + 'GARBAGE += bar.h', + 'EXTRA_MDDEPEND_FILES += bar.h.pp', + 'export:: mozilla2.h', + 'GARBAGE += mozilla2.h', + 'EXTRA_MDDEPEND_FILES += mozilla2.h.pp', + 'export:: dom2.h', + 'GARBAGE += dom2.h', + 'EXTRA_MDDEPEND_FILES += dom2.h.pp', + 'export:: dom3.h', + 'GARBAGE += dom3.h', + 'EXTRA_MDDEPEND_FILES += dom3.h.pp', + 'dist_include_FILES += bar.h', + 'dist_include_DEST := $(DEPTH)/dist/include/', + 'dist_include_TARGET := export', + 'INSTALL_TARGETS += dist_include', + 'dist_include_mozilla_FILES += mozilla2.h', + 'dist_include_mozilla_DEST := $(DEPTH)/dist/include/mozilla', + 'dist_include_mozilla_TARGET := export', + 'INSTALL_TARGETS += dist_include_mozilla', + 'dist_include_mozilla_dom_FILES += dom2.h', + 'dist_include_mozilla_dom_FILES += dom3.h', + 'dist_include_mozilla_dom_DEST := $(DEPTH)/dist/include/mozilla/dom', + 'dist_include_mozilla_dom_TARGET := export', + 'INSTALL_TARGETS += dist_include_mozilla_dom', + ] + self.maxDiff = None + self.assertEqual(lines, expected) + + def test_resources(self): + """Ensure RESOURCE_FILES is handled properly.""" + env = self._consume('resources', RecursiveMakeBackend) + + # RESOURCE_FILES should appear in the dist_bin install manifest. + m = InstallManifest(path=os.path.join(env.topobjdir, + '_build_manifests', 'install', 'dist_bin')) + self.assertEqual(len(m), 10) + self.assertIn('res/foo.res', m) + self.assertIn('res/fonts/font1.ttf', m) + self.assertIn('res/fonts/desktop/desktop2.ttf', m) + + self.assertIn('res/bar.res.in', m) + self.assertIn('res/tests/test.manifest', m) + self.assertIn('res/tests/extra.manifest', m) + + def test_branding_files(self): + """Ensure BRANDING_FILES is handled properly.""" + env = self._consume('branding-files', RecursiveMakeBackend) + + #BRANDING_FILES should appear in the dist_branding install manifest. + m = InstallManifest(path=os.path.join(env.topobjdir, + '_build_manifests', 'install', 'dist_branding')) + self.assertEqual(len(m), 3) + self.assertIn('bar.ico', m) + self.assertIn('quux.png', m) + self.assertIn('icons/foo.ico', m) + + def test_sdk_files(self): + """Ensure SDK_FILES is handled properly.""" + env = self._consume('sdk-files', RecursiveMakeBackend) + + #SDK_FILES should appear in the dist_sdk install manifest. + m = InstallManifest(path=os.path.join(env.topobjdir, + '_build_manifests', 'install', 'dist_sdk')) + self.assertEqual(len(m), 3) + self.assertIn('bar.ico', m) + self.assertIn('quux.png', m) + self.assertIn('icons/foo.ico', m) + + def test_test_manifests_files_written(self): + """Ensure test manifests get turned into files.""" + env = self._consume('test-manifests-written', RecursiveMakeBackend) + + tests_dir = mozpath.join(env.topobjdir, '_tests') + m_master = mozpath.join(tests_dir, 'testing', 'mochitest', 'tests', 'mochitest.ini') + x_master = mozpath.join(tests_dir, 'xpcshell', 'xpcshell.ini') + self.assertTrue(os.path.exists(m_master)) + self.assertTrue(os.path.exists(x_master)) + + lines = [l.strip() for l in open(x_master, 'rt').readlines()] + self.assertEqual(lines, [ + '; THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.', + '', + '[include:dir1/xpcshell.ini]', + '[include:xpcshell.ini]', + ]) + + all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl') + self.assertTrue(os.path.exists(all_tests_path)) + + with open(all_tests_path, 'rb') as fh: + o = pickle.load(fh) + + self.assertIn('xpcshell.js', o) + self.assertIn('dir1/test_bar.js', o) + + self.assertEqual(len(o['xpcshell.js']), 1) + + def test_test_manifest_pattern_matches_recorded(self): + """Pattern matches in test manifests' support-files should be recorded.""" + env = self._consume('test-manifests-written', RecursiveMakeBackend) + m = InstallManifest(path=mozpath.join(env.topobjdir, + '_build_manifests', 'install', '_test_files')) + + # This is not the most robust test in the world, but it gets the job + # done. + entries = [e for e in m._dests.keys() if '**' in e] + self.assertEqual(len(entries), 1) + self.assertIn('support/**', entries[0]) + + def test_test_manifest_deffered_installs_written(self): + """Shared support files are written to their own data file by the backend.""" + env = self._consume('test-manifest-shared-support', RecursiveMakeBackend) + all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl') + self.assertTrue(os.path.exists(all_tests_path)) + test_installs_path = mozpath.join(env.topobjdir, 'test-installs.pkl') + + with open(test_installs_path, 'r') as fh: + test_installs = pickle.load(fh) + + self.assertEqual(set(test_installs.keys()), + set(['child/test_sub.js', + 'child/data/**', + 'child/another-file.sjs'])) + for key in test_installs.keys(): + self.assertIn(key, test_installs) + + test_files_manifest = mozpath.join(env.topobjdir, + '_build_manifests', + 'install', + '_test_files') + + # First, read the generated for ini manifest contents. + m = InstallManifest(path=test_files_manifest) + + # Then, synthesize one from the test-installs.pkl file. This should + # allow us to re-create a subset of the above. + synthesized_manifest = InstallManifest() + for item, installs in test_installs.items(): + for install_info in installs: + if len(install_info) == 3: + synthesized_manifest.add_pattern_symlink(*install_info) + if len(install_info) == 2: + synthesized_manifest.add_symlink(*install_info) + + self.assertEqual(len(synthesized_manifest), 3) + for item, info in synthesized_manifest._dests.items(): + self.assertIn(item, m) + self.assertEqual(info, m._dests[item]) + + def test_xpidl_generation(self): + """Ensure xpidl files and directories are written out.""" + env = self._consume('xpidl', RecursiveMakeBackend) + + # Install manifests should contain entries. + install_dir = mozpath.join(env.topobjdir, '_build_manifests', + 'install') + self.assertTrue(os.path.isfile(mozpath.join(install_dir, 'dist_idl'))) + self.assertTrue(os.path.isfile(mozpath.join(install_dir, 'xpidl'))) + + m = InstallManifest(path=mozpath.join(install_dir, 'dist_idl')) + self.assertEqual(len(m), 2) + self.assertIn('bar.idl', m) + self.assertIn('foo.idl', m) + + m = InstallManifest(path=mozpath.join(install_dir, 'xpidl')) + self.assertIn('.deps/my_module.pp', m) + + m = InstallManifest(path=os.path.join(install_dir, 'dist_bin')) + self.assertIn('components/my_module.xpt', m) + self.assertIn('components/interfaces.manifest', m) + + m = InstallManifest(path=mozpath.join(install_dir, 'dist_include')) + self.assertIn('foo.h', m) + + p = mozpath.join(env.topobjdir, 'config/makefiles/xpidl') + self.assertTrue(os.path.isdir(p)) + + self.assertTrue(os.path.isfile(mozpath.join(p, 'Makefile'))) + + def test_old_install_manifest_deleted(self): + # Simulate an install manifest from a previous backend version. Ensure + # it is deleted. + env = self._get_environment('stub0') + purge_dir = mozpath.join(env.topobjdir, '_build_manifests', 'install') + manifest_path = mozpath.join(purge_dir, 'old_manifest') + os.makedirs(purge_dir) + m = InstallManifest() + m.write(path=manifest_path) + with open(mozpath.join( + env.topobjdir, 'backend.RecursiveMakeBackend'), 'w') as f: + f.write('%s\n' % manifest_path) + + self.assertTrue(os.path.exists(manifest_path)) + self._consume('stub0', RecursiveMakeBackend, env) + self.assertFalse(os.path.exists(manifest_path)) + + def test_install_manifests_written(self): + env, objs = self._emit('stub0') + backend = RecursiveMakeBackend(env) + + m = InstallManifest() + backend._install_manifests['testing'] = m + m.add_symlink(__file__, 'self') + backend.consume(objs) + + man_dir = mozpath.join(env.topobjdir, '_build_manifests', 'install') + self.assertTrue(os.path.isdir(man_dir)) + + expected = ['testing'] + for e in expected: + full = mozpath.join(man_dir, e) + self.assertTrue(os.path.exists(full)) + + m2 = InstallManifest(path=full) + self.assertEqual(m, m2) + + def test_ipdl_sources(self): + """Test that IPDL_SOURCES are written to ipdlsrcs.mk correctly.""" + env = self._consume('ipdl_sources', RecursiveMakeBackend) + + manifest_path = mozpath.join(env.topobjdir, + 'ipc', 'ipdl', 'ipdlsrcs.mk') + lines = [l.strip() for l in open(manifest_path, 'rt').readlines()] + + # Handle Windows paths correctly + topsrcdir = env.topsrcdir.replace(os.sep, '/') + + expected = [ + "ALL_IPDLSRCS := %s/bar/bar.ipdl %s/bar/bar2.ipdlh %s/foo/foo.ipdl %s/foo/foo2.ipdlh" % tuple([topsrcdir] * 4), + "CPPSRCS := UnifiedProtocols0.cpp", + "IPDLDIRS := %s/bar %s/foo" % (topsrcdir, topsrcdir), + ] + + found = [str for str in lines if str.startswith(('ALL_IPDLSRCS', + 'CPPSRCS', + 'IPDLDIRS'))] + self.assertEqual(found, expected) + + def test_defines(self): + """Test that DEFINES are written to backend.mk correctly.""" + env = self._consume('defines', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + var = 'DEFINES' + defines = [val for val in lines if val.startswith(var)] + + expected = ['DEFINES += -DFOO \'-DBAZ="ab\'\\\'\'cd"\' -UQUX -DBAR=7 -DVALUE=xyz'] + self.assertEqual(defines, expected) + + def test_host_defines(self): + """Test that HOST_DEFINES are written to backend.mk correctly.""" + env = self._consume('host-defines', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + var = 'HOST_DEFINES' + defines = [val for val in lines if val.startswith(var)] + + expected = ['HOST_DEFINES += -DFOO \'-DBAZ="ab\'\\\'\'cd"\' -UQUX -DBAR=7 -DVALUE=xyz'] + self.assertEqual(defines, expected) + + def test_local_includes(self): + """Test that LOCAL_INCLUDES are written to backend.mk correctly.""" + env = self._consume('local_includes', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + expected = [ + 'LOCAL_INCLUDES += -I$(srcdir)/bar/baz', + 'LOCAL_INCLUDES += -I$(srcdir)/foo', + ] + + found = [str for str in lines if str.startswith('LOCAL_INCLUDES')] + self.assertEqual(found, expected) + + def test_generated_includes(self): + """Test that GENERATED_INCLUDES are written to backend.mk correctly.""" + env = self._consume('generated_includes', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + topobjdir = env.topobjdir.replace('\\', '/') + + expected = [ + 'LOCAL_INCLUDES += -I$(CURDIR)/bar/baz', + 'LOCAL_INCLUDES += -I$(CURDIR)/foo', + ] + + found = [str for str in lines if str.startswith('LOCAL_INCLUDES')] + self.assertEqual(found, expected) + + def test_final_target(self): + """Test that FINAL_TARGET is written to backend.mk correctly.""" + env = self._consume('final_target', RecursiveMakeBackend) + + final_target_rule = "FINAL_TARGET = $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin)$(DIST_SUBDIR:%=/%)" + expected = dict() + expected[env.topobjdir] = [] + expected[mozpath.join(env.topobjdir, 'both')] = [ + 'XPI_NAME = mycrazyxpi', + 'DIST_SUBDIR = asubdir', + final_target_rule + ] + expected[mozpath.join(env.topobjdir, 'dist-subdir')] = [ + 'DIST_SUBDIR = asubdir', + final_target_rule + ] + expected[mozpath.join(env.topobjdir, 'xpi-name')] = [ + 'XPI_NAME = mycrazyxpi', + final_target_rule + ] + expected[mozpath.join(env.topobjdir, 'final-target')] = [ + 'FINAL_TARGET = $(DEPTH)/random-final-target' + ] + for key, expected_rules in expected.iteritems(): + backend_path = mozpath.join(key, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + found = [str for str in lines if + str.startswith('FINAL_TARGET') or str.startswith('XPI_NAME') or + str.startswith('DIST_SUBDIR')] + self.assertEqual(found, expected_rules) + + def test_final_target_pp_files(self): + """Test that FINAL_TARGET_PP_FILES is written to backend.mk correctly.""" + env = self._consume('dist-files', RecursiveMakeBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + + expected = [ + 'DIST_FILES_0 += $(srcdir)/install.rdf', + 'DIST_FILES_0 += $(srcdir)/main.js', + 'DIST_FILES_0_PATH := $(DEPTH)/dist/bin/', + 'DIST_FILES_0_TARGET := misc', + 'PP_TARGETS += DIST_FILES_0', + ] + + found = [str for str in lines if 'DIST_FILES' in str] + self.assertEqual(found, expected) + + def test_config(self): + """Test that CONFIGURE_SUBST_FILES are properly handled.""" + env = self._consume('test_config', RecursiveMakeBackend) + + self.assertEqual( + open(os.path.join(env.topobjdir, 'file'), 'r').readlines(), [ + '#ifdef foo\n', + 'bar baz\n', + '@bar@\n', + ]) + + def test_jar_manifests(self): + env = self._consume('jar-manifests', RecursiveMakeBackend) + + with open(os.path.join(env.topobjdir, 'backend.mk'), 'rb') as fh: + lines = fh.readlines() + + lines = [line.rstrip() for line in lines] + + self.assertIn('JAR_MANIFEST := %s/jar.mn' % env.topsrcdir, lines) + + def test_test_manifests_duplicate_support_files(self): + """Ensure duplicate support-files in test manifests work.""" + env = self._consume('test-manifests-duplicate-support-files', + RecursiveMakeBackend) + + p = os.path.join(env.topobjdir, '_build_manifests', 'install', '_test_files') + m = InstallManifest(p) + self.assertIn('testing/mochitest/tests/support-file.txt', m) + + def test_android_eclipse(self): + env = self._consume('android_eclipse', RecursiveMakeBackend) + + with open(mozpath.join(env.topobjdir, 'backend.mk'), 'rb') as fh: + lines = fh.readlines() + + lines = [line.rstrip() for line in lines] + + # Dependencies first. + self.assertIn('ANDROID_ECLIPSE_PROJECT_main1: target1 target2', lines) + self.assertIn('ANDROID_ECLIPSE_PROJECT_main4: target3 target4', lines) + + command_template = '\t$(call py_action,process_install_manifest,' + \ + '--no-remove --no-remove-all-directory-symlinks ' + \ + '--no-remove-empty-directories %s %s.manifest)' + # Commands second. + for project_name in ['main1', 'main2', 'library1', 'library2']: + stem = '%s/android_eclipse/%s' % (env.topobjdir, project_name) + self.assertIn(command_template % (stem, stem), lines) + + # Projects declared in subdirectories. + with open(mozpath.join(env.topobjdir, 'subdir', 'backend.mk'), 'rb') as fh: + lines = fh.readlines() + + lines = [line.rstrip() for line in lines] + + self.assertIn('ANDROID_ECLIPSE_PROJECT_submain: subtarget1 subtarget2', lines) + + for project_name in ['submain', 'sublibrary']: + # Destination and install manifest are relative to topobjdir. + stem = '%s/android_eclipse/%s' % (env.topobjdir, project_name) + self.assertIn(command_template % (stem, stem), lines) + + def test_install_manifests_package_tests(self): + """Ensure test suites honor package_tests=False.""" + env = self._consume('test-manifests-package-tests', RecursiveMakeBackend) + + all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl') + self.assertTrue(os.path.exists(all_tests_path)) + + with open(all_tests_path, 'rb') as fh: + o = pickle.load(fh) + self.assertIn('mochitest.js', o) + self.assertIn('not_packaged.java', o) + + man_dir = mozpath.join(env.topobjdir, '_build_manifests', 'install') + self.assertTrue(os.path.isdir(man_dir)) + + full = mozpath.join(man_dir, '_test_files') + self.assertTrue(os.path.exists(full)) + + m = InstallManifest(path=full) + + # Only mochitest.js should be in the install manifest. + self.assertTrue('testing/mochitest/tests/mochitest.js' in m) + + # The path is odd here because we do not normalize at test manifest + # processing time. This is a fragile test because there's currently no + # way to iterate the manifest. + self.assertFalse('instrumentation/./not_packaged.java' in m) + + def test_binary_components(self): + """Ensure binary components are correctly handled.""" + env = self._consume('binary-components', RecursiveMakeBackend) + + with open(mozpath.join(env.topobjdir, 'foo', 'backend.mk')) as fh: + lines = fh.readlines()[2:] + + self.assertEqual(lines, [ + 'misc::\n', + '\t$(call py_action,buildlist,$(DEPTH)/dist/bin/chrome.manifest ' + + "'manifest components/components.manifest')\n", + '\t$(call py_action,buildlist,' + + '$(DEPTH)/dist/bin/components/components.manifest ' + + "'binary-component foo')\n", + 'LIBRARY_NAME := foo\n', + 'FORCE_SHARED_LIB := 1\n', + 'IMPORT_LIBRARY := foo\n', + 'SHARED_LIBRARY := foo\n', + 'IS_COMPONENT := 1\n', + 'DSO_SONAME := foo\n', + 'LIB_IS_C_ONLY := 1\n', + ]) + + with open(mozpath.join(env.topobjdir, 'bar', 'backend.mk')) as fh: + lines = fh.readlines()[2:] + + self.assertEqual(lines, [ + 'LIBRARY_NAME := bar\n', + 'FORCE_SHARED_LIB := 1\n', + 'IMPORT_LIBRARY := bar\n', + 'SHARED_LIBRARY := bar\n', + 'IS_COMPONENT := 1\n', + 'DSO_SONAME := bar\n', + 'LIB_IS_C_ONLY := 1\n', + ]) + + self.assertTrue(os.path.exists(mozpath.join(env.topobjdir, 'binaries.json'))) + with open(mozpath.join(env.topobjdir, 'binaries.json'), 'rb') as fh: + binaries = json.load(fh) + + self.assertEqual(binaries, { + 'programs': [], + 'shared_libraries': [ + { + 'basename': 'foo', + 'import_name': 'foo', + 'install_target': 'dist/bin', + 'lib_name': 'foo', + 'relobjdir': 'foo', + 'soname': 'foo', + }, + { + 'basename': 'bar', + 'import_name': 'bar', + 'install_target': 'dist/bin', + 'lib_name': 'bar', + 'relobjdir': 'bar', + 'soname': 'bar', + } + ], + }) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/backend/test_visualstudio.py b/python/mozbuild/mozbuild/test/backend/test_visualstudio.py new file mode 100644 index 000000000..bfc95e552 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/test_visualstudio.py @@ -0,0 +1,64 @@ +# 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 + +from xml.dom.minidom import parse +import os +import unittest + +from mozbuild.backend.visualstudio import VisualStudioBackend +from mozbuild.test.backend.common import BackendTester + +from mozunit import main + + +class TestVisualStudioBackend(BackendTester): + @unittest.skip('Failing inconsistently in automation.') + def test_basic(self): + """Ensure we can consume our stub project.""" + + env = self._consume('visual-studio', VisualStudioBackend) + + msvc = os.path.join(env.topobjdir, 'msvc') + self.assertTrue(os.path.isdir(msvc)) + + self.assertTrue(os.path.isfile(os.path.join(msvc, 'mozilla.sln'))) + self.assertTrue(os.path.isfile(os.path.join(msvc, 'mozilla.props'))) + self.assertTrue(os.path.isfile(os.path.join(msvc, 'mach.bat'))) + self.assertTrue(os.path.isfile(os.path.join(msvc, 'binary_my_app.vcxproj'))) + self.assertTrue(os.path.isfile(os.path.join(msvc, 'target_full.vcxproj'))) + self.assertTrue(os.path.isfile(os.path.join(msvc, 'library_dir1.vcxproj'))) + self.assertTrue(os.path.isfile(os.path.join(msvc, 'library_dir1.vcxproj.user'))) + + d = parse(os.path.join(msvc, 'library_dir1.vcxproj')) + self.assertEqual(d.documentElement.tagName, 'Project') + els = d.getElementsByTagName('ClCompile') + self.assertEqual(len(els), 2) + + # mozilla-config.h should be explicitly listed as an include. + els = d.getElementsByTagName('NMakeForcedIncludes') + self.assertEqual(len(els), 1) + self.assertEqual(els[0].firstChild.nodeValue, + '$(TopObjDir)\\dist\\include\\mozilla-config.h') + + # LOCAL_INCLUDES get added to the include search path. + els = d.getElementsByTagName('NMakeIncludeSearchPath') + self.assertEqual(len(els), 1) + includes = els[0].firstChild.nodeValue.split(';') + self.assertIn(os.path.normpath('$(TopSrcDir)/includeA/foo'), includes) + self.assertIn(os.path.normpath('$(TopSrcDir)/dir1'), includes) + self.assertIn(os.path.normpath('$(TopObjDir)/dir1'), includes) + self.assertIn(os.path.normpath('$(TopObjDir)\\dist\\include'), includes) + + # DEFINES get added to the project. + els = d.getElementsByTagName('NMakePreprocessorDefinitions') + self.assertEqual(len(els), 1) + defines = els[0].firstChild.nodeValue.split(';') + self.assertIn('DEFINEFOO', defines) + self.assertIn('DEFINEBAR=bar', defines) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/common.py b/python/mozbuild/mozbuild/test/common.py new file mode 100644 index 000000000..76a39b313 --- /dev/null +++ b/python/mozbuild/mozbuild/test/common.py @@ -0,0 +1,50 @@ +# 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 + +from mach.logging import LoggingManager + +from mozbuild.util import ReadOnlyDict + +import mozpack.path as mozpath + + +# By including this module, tests get structured logging. +log_manager = LoggingManager() +log_manager.add_terminal_logging() + +# mozconfig is not a reusable type (it's actually a module) so, we +# have to mock it. +class MockConfig(object): + def __init__(self, + topsrcdir='/path/to/topsrcdir', + extra_substs={}, + error_is_fatal=True, + ): + self.topsrcdir = mozpath.abspath(topsrcdir) + self.topobjdir = mozpath.abspath('/path/to/topobjdir') + + self.substs = ReadOnlyDict({ + 'MOZ_FOO': 'foo', + 'MOZ_BAR': 'bar', + 'MOZ_TRUE': '1', + 'MOZ_FALSE': '', + 'DLL_PREFIX': 'lib', + 'DLL_SUFFIX': '.so' + }, **extra_substs) + + self.substs_unicode = ReadOnlyDict({k.decode('utf-8'): v.decode('utf-8', + 'replace') for k, v in self.substs.items()}) + + self.defines = self.substs + + self.external_source_dir = None + self.lib_prefix = 'lib' + self.lib_suffix = '.a' + self.import_prefix = 'lib' + self.import_suffix = '.so' + self.dll_prefix = 'lib' + self.dll_suffix = '.so' + self.error_is_fatal = error_is_fatal diff --git a/python/mozbuild/mozbuild/test/compilation/__init__.py b/python/mozbuild/mozbuild/test/compilation/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/compilation/__init__.py diff --git a/python/mozbuild/mozbuild/test/compilation/test_warnings.py b/python/mozbuild/mozbuild/test/compilation/test_warnings.py new file mode 100644 index 000000000..cd2406dfc --- /dev/null +++ b/python/mozbuild/mozbuild/test/compilation/test_warnings.py @@ -0,0 +1,241 @@ +# 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/. + +import os +import unittest + +from mozfile.mozfile import NamedTemporaryFile + +from mozbuild.compilation.warnings import CompilerWarning +from mozbuild.compilation.warnings import WarningsCollector +from mozbuild.compilation.warnings import WarningsDatabase + +from mozunit import main + +CLANG_TESTS = [ + ('foobar.cpp:123:10: warning: you messed up [-Wfoo]', + 'foobar.cpp', 123, 10, 'you messed up', '-Wfoo'), + ("c_locale_dummy.c:457:1: warning: (near initialization for " + "'full_wmonthname[0]') [-Wpointer-sign]", + 'c_locale_dummy.c', 457, 1, + "(near initialization for 'full_wmonthname[0]')", '-Wpointer-sign') +] + +MSVC_TESTS = [ + ("C:/mozilla-central/test/foo.cpp(793) : warning C4244: 'return' : " + "conversion from 'double' to 'uint32_t', possible loss of data", + 'C:/mozilla-central/test/foo.cpp', 793, 'C4244', + "'return' : conversion from 'double' to 'uint32_t', possible loss of " + 'data') +] + +CURRENT_LINE = 1 + +def get_warning(): + global CURRENT_LINE + + w = CompilerWarning() + w['filename'] = '/foo/bar/baz.cpp' + w['line'] = CURRENT_LINE + w['column'] = 12 + w['message'] = 'This is irrelevant' + + CURRENT_LINE += 1 + + return w + +class TestCompilerWarning(unittest.TestCase): + def test_equivalence(self): + w1 = CompilerWarning() + w2 = CompilerWarning() + + s = set() + + # Empty warnings should be equal. + self.assertEqual(w1, w2) + + s.add(w1) + s.add(w2) + + self.assertEqual(len(s), 1) + + w1['filename'] = '/foo.c' + w2['filename'] = '/bar.c' + + self.assertNotEqual(w1, w2) + + s = set() + s.add(w1) + s.add(w2) + + self.assertEqual(len(s), 2) + + w1['filename'] = '/foo.c' + w1['line'] = 5 + w2['line'] = 5 + + w2['filename'] = '/foo.c' + w1['column'] = 3 + w2['column'] = 3 + + self.assertEqual(w1, w2) + + def test_comparison(self): + w1 = CompilerWarning() + w2 = CompilerWarning() + + w1['filename'] = '/aaa.c' + w1['line'] = 5 + w1['column'] = 5 + + w2['filename'] = '/bbb.c' + w2['line'] = 5 + w2['column'] = 5 + + self.assertLess(w1, w2) + self.assertGreater(w2, w1) + self.assertGreaterEqual(w2, w1) + + w2['filename'] = '/aaa.c' + w2['line'] = 4 + w2['column'] = 6 + + self.assertLess(w2, w1) + self.assertGreater(w1, w2) + self.assertGreaterEqual(w1, w2) + + w2['filename'] = '/aaa.c' + w2['line'] = 5 + w2['column'] = 10 + + self.assertLess(w1, w2) + self.assertGreater(w2, w1) + self.assertGreaterEqual(w2, w1) + + w2['filename'] = '/aaa.c' + w2['line'] = 5 + w2['column'] = 5 + + self.assertLessEqual(w1, w2) + self.assertLessEqual(w2, w1) + self.assertGreaterEqual(w2, w1) + self.assertGreaterEqual(w1, w2) + +class TestWarningsParsing(unittest.TestCase): + def test_clang_parsing(self): + for source, filename, line, column, message, flag in CLANG_TESTS: + collector = WarningsCollector(resolve_files=False) + warning = collector.process_line(source) + + self.assertIsNotNone(warning) + + self.assertEqual(warning['filename'], filename) + self.assertEqual(warning['line'], line) + self.assertEqual(warning['column'], column) + self.assertEqual(warning['message'], message) + self.assertEqual(warning['flag'], flag) + + def test_msvc_parsing(self): + for source, filename, line, flag, message in MSVC_TESTS: + collector = WarningsCollector(resolve_files=False) + warning = collector.process_line(source) + + self.assertIsNotNone(warning) + + self.assertEqual(warning['filename'], os.path.normpath(filename)) + self.assertEqual(warning['line'], line) + self.assertEqual(warning['flag'], flag) + self.assertEqual(warning['message'], message) + +class TestWarningsDatabase(unittest.TestCase): + def test_basic(self): + db = WarningsDatabase() + + self.assertEqual(len(db), 0) + + for i in range(10): + db.insert(get_warning(), compute_hash=False) + + self.assertEqual(len(db), 10) + + warnings = list(db) + self.assertEqual(len(warnings), 10) + + def test_hashing(self): + """Ensure that hashing files on insert works.""" + db = WarningsDatabase() + + temp = NamedTemporaryFile(mode='wt') + temp.write('x' * 100) + temp.flush() + + w = CompilerWarning() + w['filename'] = temp.name + w['line'] = 1 + w['column'] = 4 + w['message'] = 'foo bar' + + # Should not throw. + db.insert(w) + + w['filename'] = 'DOES_NOT_EXIST' + + with self.assertRaises(Exception): + db.insert(w) + + def test_pruning(self): + """Ensure old warnings are removed from database appropriately.""" + db = WarningsDatabase() + + source_files = [] + for i in range(1, 21): + temp = NamedTemporaryFile(mode='wt') + temp.write('x' * (100 * i)) + temp.flush() + + # Keep reference so it doesn't get GC'd and deleted. + source_files.append(temp) + + w = CompilerWarning() + w['filename'] = temp.name + w['line'] = 1 + w['column'] = i * 10 + w['message'] = 'irrelevant' + + db.insert(w) + + self.assertEqual(len(db), 20) + + # If we change a source file, inserting a new warning should nuke the + # old one. + source_files[0].write('extra') + source_files[0].flush() + + w = CompilerWarning() + w['filename'] = source_files[0].name + w['line'] = 1 + w['column'] = 50 + w['message'] = 'replaced' + + db.insert(w) + + self.assertEqual(len(db), 20) + + warnings = list(db.warnings_for_file(source_files[0].name)) + self.assertEqual(len(warnings), 1) + self.assertEqual(warnings[0]['column'], w['column']) + + # If we delete the source file, calling prune should cause the warnings + # to go away. + old_filename = source_files[0].name + del source_files[0] + + self.assertFalse(os.path.exists(old_filename)) + + db.prune() + self.assertEqual(len(db), 19) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/common.py b/python/mozbuild/mozbuild/test/configure/common.py new file mode 100644 index 000000000..089d61a0d --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/common.py @@ -0,0 +1,279 @@ +# 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 + +import copy +import errno +import os +import subprocess +import sys +import tempfile +import unittest + +from mozbuild.configure import ConfigureSandbox +from mozbuild.util import ReadOnlyNamespace +from mozpack import path as mozpath + +from StringIO import StringIO +from which import WhichError + +from buildconfig import ( + topobjdir, + topsrcdir, +) + + +def fake_short_path(path): + if sys.platform.startswith('win'): + return '/'.join(p.split(' ', 1)[0] + '~1' if ' 'in p else p + for p in mozpath.split(path)) + return path + +def ensure_exe_extension(path): + if sys.platform.startswith('win'): + return path + '.exe' + return path + + +class ConfigureTestVFS(object): + def __init__(self, paths): + self._paths = set(mozpath.abspath(p) for p in paths) + + def exists(self, path): + path = mozpath.abspath(path) + if path in self._paths: + return True + if mozpath.basedir(path, [topsrcdir, topobjdir]): + return os.path.exists(path) + return False + + def isfile(self, path): + path = mozpath.abspath(path) + if path in self._paths: + return True + if mozpath.basedir(path, [topsrcdir, topobjdir]): + return os.path.isfile(path) + return False + + +class ConfigureTestSandbox(ConfigureSandbox): + '''Wrapper around the ConfigureSandbox for testing purposes. + + Its arguments are the same as ConfigureSandbox, except for the additional + `paths` argument, which is a dict where the keys are file paths and the + values are either None or a function that will be called when the sandbox + calls an implemented function from subprocess with the key as command. + When the command is CONFIG_SHELL, the function for the path of the script + that follows will be called. + + The API for those functions is: + retcode, stdout, stderr = func(stdin, args) + + This class is only meant to implement the minimal things to make + moz.configure testing possible. As such, it takes shortcuts. + ''' + def __init__(self, paths, config, environ, *args, **kwargs): + self._search_path = environ.get('PATH', '').split(os.pathsep) + + self._subprocess_paths = { + mozpath.abspath(k): v for k, v in paths.iteritems() if v + } + + paths = paths.keys() + + environ = dict(environ) + if 'CONFIG_SHELL' not in environ: + environ['CONFIG_SHELL'] = mozpath.abspath('/bin/sh') + self._subprocess_paths[environ['CONFIG_SHELL']] = self.shell + paths.append(environ['CONFIG_SHELL']) + self._environ = copy.copy(environ) + + vfs = ConfigureTestVFS(paths) + + os_path = { + k: getattr(vfs, k) for k in dir(vfs) if not k.startswith('_') + } + + os_path.update(self.OS.path.__dict__) + + self.imported_os = ReadOnlyNamespace(path=ReadOnlyNamespace(**os_path)) + + super(ConfigureTestSandbox, self).__init__(config, environ, *args, + **kwargs) + + def _get_one_import(self, what): + if what == 'which.which': + return self.which + + if what == 'which': + return ReadOnlyNamespace( + which=self.which, + WhichError=WhichError, + ) + + if what == 'subprocess.Popen': + return self.Popen + + if what == 'subprocess': + return ReadOnlyNamespace( + CalledProcessError=subprocess.CalledProcessError, + check_output=self.check_output, + PIPE=subprocess.PIPE, + STDOUT=subprocess.STDOUT, + Popen=self.Popen, + ) + + if what == 'os.environ': + return self._environ + + if what == 'ctypes.wintypes': + return ReadOnlyNamespace( + LPCWSTR=0, + LPWSTR=1, + DWORD=2, + ) + + if what == 'ctypes': + class CTypesFunc(object): + def __init__(self, func): + self._func = func + + def __call__(self, *args, **kwargs): + return self._func(*args, **kwargs) + + + return ReadOnlyNamespace( + create_unicode_buffer=self.create_unicode_buffer, + windll=ReadOnlyNamespace( + kernel32=ReadOnlyNamespace( + GetShortPathNameW=CTypesFunc(self.GetShortPathNameW), + ) + ), + ) + + if what == '_winreg': + def OpenKey(*args, **kwargs): + raise WindowsError() + + return ReadOnlyNamespace( + HKEY_LOCAL_MACHINE=0, + OpenKey=OpenKey, + ) + + return super(ConfigureTestSandbox, self)._get_one_import(what) + + def create_unicode_buffer(self, *args, **kwargs): + class Buffer(object): + def __init__(self): + self.value = '' + + return Buffer() + + def GetShortPathNameW(self, path_in, path_out, length): + path_out.value = fake_short_path(path_in) + return length + + def which(self, command, path=None): + for parent in (path or self._search_path): + c = mozpath.abspath(mozpath.join(parent, command)) + for candidate in (c, ensure_exe_extension(c)): + if self.imported_os.path.exists(candidate): + return candidate + raise WhichError() + + def Popen(self, args, stdin=None, stdout=None, stderr=None, **kargs): + try: + program = self.which(args[0]) + except WhichError: + raise OSError(errno.ENOENT, 'File not found') + + func = self._subprocess_paths.get(program) + retcode, stdout, stderr = func(stdin, args[1:]) + + class Process(object): + def communicate(self, stdin=None): + return stdout, stderr + + def wait(self): + return retcode + + return Process() + + def check_output(self, args, **kwargs): + proc = self.Popen(args, **kwargs) + stdout, stderr = proc.communicate() + retcode = proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, args, stdout) + return stdout + + def shell(self, stdin, args): + script = mozpath.abspath(args[0]) + if script in self._subprocess_paths: + return self._subprocess_paths[script](stdin, args[1:]) + return 127, '', 'File not found' + + +class BaseConfigureTest(unittest.TestCase): + HOST = 'x86_64-pc-linux-gnu' + + def setUp(self): + self._cwd = os.getcwd() + os.chdir(topobjdir) + + def tearDown(self): + os.chdir(self._cwd) + + def config_guess(self, stdin, args): + return 0, self.HOST, '' + + def config_sub(self, stdin, args): + return 0, args[0], '' + + def get_sandbox(self, paths, config, args=[], environ={}, mozconfig='', + out=None, logger=None): + kwargs = {} + if logger: + kwargs['logger'] = logger + else: + if not out: + out = StringIO() + kwargs['stdout'] = out + kwargs['stderr'] = out + + if hasattr(self, 'TARGET'): + target = ['--target=%s' % self.TARGET] + else: + target = [] + + if mozconfig: + fh, mozconfig_path = tempfile.mkstemp() + os.write(fh, mozconfig) + os.close(fh) + else: + mozconfig_path = os.path.join(os.path.dirname(__file__), 'data', + 'empty_mozconfig') + + try: + environ = dict( + environ, + OLD_CONFIGURE=os.path.join(topsrcdir, 'old-configure'), + MOZCONFIG=mozconfig_path) + + paths = dict(paths) + autoconf_dir = mozpath.join(topsrcdir, 'build', 'autoconf') + paths[mozpath.join(autoconf_dir, + 'config.guess')] = self.config_guess + paths[mozpath.join(autoconf_dir, 'config.sub')] = self.config_sub + + sandbox = ConfigureTestSandbox(paths, config, environ, + ['configure'] + target + args, + **kwargs) + sandbox.include_file(os.path.join(topsrcdir, 'moz.configure')) + + return sandbox + finally: + if mozconfig: + os.remove(mozconfig_path) diff --git a/python/mozbuild/mozbuild/test/configure/data/decorators.configure b/python/mozbuild/mozbuild/test/configure/data/decorators.configure new file mode 100644 index 000000000..e5e41c68a --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/decorators.configure @@ -0,0 +1,44 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +@template +def simple_decorator(func): + return func + +@template +def wrapper_decorator(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + +@template +def function_decorator(*args, **kwargs): + # We could return wrapper_decorator from above here, but then we wouldn't + # know if this works as expected because wrapper_decorator itself was + # modified or because the right thing happened here. + def wrapper_decorator(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + return wrapper_decorator + +@depends('--help') +@simple_decorator +def foo(help): + global FOO + FOO = 1 + +@depends('--help') +@wrapper_decorator +def bar(help): + global BAR + BAR = 1 + +@depends('--help') +@function_decorator('a', 'b', 'c') +def qux(help): + global QUX + QUX = 1 diff --git a/python/mozbuild/mozbuild/test/configure/data/empty_mozconfig b/python/mozbuild/mozbuild/test/configure/data/empty_mozconfig new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/empty_mozconfig diff --git a/python/mozbuild/mozbuild/test/configure/data/extra.configure b/python/mozbuild/mozbuild/test/configure/data/extra.configure new file mode 100644 index 000000000..43fbf7c5d --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/extra.configure @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--extra', help='Extra') + +@depends('--extra') +def extra(extra): + return extra + +set_config('EXTRA', extra) diff --git a/python/mozbuild/mozbuild/test/configure/data/imply_option/imm.configure b/python/mozbuild/mozbuild/test/configure/data/imply_option/imm.configure new file mode 100644 index 000000000..ad05e383c --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/imply_option/imm.configure @@ -0,0 +1,32 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +imply_option('--enable-foo', True) + +option('--enable-foo', help='enable foo') + +@depends('--enable-foo', '--help') +def foo(value, help): + if value: + return True + +imply_option('--enable-bar', ('foo', 'bar')) + +option('--enable-bar', nargs='*', help='enable bar') + +@depends('--enable-bar') +def bar(value): + if value: + return value + +imply_option('--enable-baz', 'BAZ') + +option('--enable-baz', nargs=1, help='enable baz') + +@depends('--enable-baz') +def bar(value): + if value: + return value diff --git a/python/mozbuild/mozbuild/test/configure/data/imply_option/infer.configure b/python/mozbuild/mozbuild/test/configure/data/imply_option/infer.configure new file mode 100644 index 000000000..2ad1506ef --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/imply_option/infer.configure @@ -0,0 +1,24 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--enable-foo', help='enable foo') + +@depends('--enable-foo', '--help') +def foo(value, help): + if value: + return True + +imply_option('--enable-bar', foo) + + +option('--enable-bar', help='enable bar') + +@depends('--enable-bar') +def bar(value): + if value: + return value + +set_config('BAR', bar) diff --git a/python/mozbuild/mozbuild/test/configure/data/imply_option/infer_ko.configure b/python/mozbuild/mozbuild/test/configure/data/imply_option/infer_ko.configure new file mode 100644 index 000000000..72b88d7b5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/imply_option/infer_ko.configure @@ -0,0 +1,31 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--enable-hoge', help='enable hoge') + +@depends('--enable-hoge') +def hoge(value): + return value + + +option('--enable-foo', help='enable foo') + +@depends('--enable-foo', hoge) +def foo(value, hoge): + if value: + return True + +imply_option('--enable-bar', foo) + + +option('--enable-bar', help='enable bar') + +@depends('--enable-bar') +def bar(value): + if value: + return value + +set_config('BAR', bar) diff --git a/python/mozbuild/mozbuild/test/configure/data/imply_option/negative.configure b/python/mozbuild/mozbuild/test/configure/data/imply_option/negative.configure new file mode 100644 index 000000000..ca8e9df3a --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/imply_option/negative.configure @@ -0,0 +1,34 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--enable-foo', help='enable foo') + +@depends('--enable-foo') +def foo(value): + if value: + return False + +imply_option('--enable-bar', foo) + + +option('--disable-hoge', help='enable hoge') + +@depends('--disable-hoge') +def hoge(value): + if not value: + return False + +imply_option('--enable-bar', hoge) + + +option('--enable-bar', default=True, help='enable bar') + +@depends('--enable-bar') +def bar(value): + if not value: + return value + +set_config('BAR', bar) diff --git a/python/mozbuild/mozbuild/test/configure/data/imply_option/simple.configure b/python/mozbuild/mozbuild/test/configure/data/imply_option/simple.configure new file mode 100644 index 000000000..6d905ebbb --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/imply_option/simple.configure @@ -0,0 +1,24 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--enable-foo', help='enable foo') + +@depends('--enable-foo') +def foo(value): + if value: + return True + +imply_option('--enable-bar', foo) + + +option('--enable-bar', help='enable bar') + +@depends('--enable-bar') +def bar(value): + if value: + return value + +set_config('BAR', bar) diff --git a/python/mozbuild/mozbuild/test/configure/data/imply_option/values.configure b/python/mozbuild/mozbuild/test/configure/data/imply_option/values.configure new file mode 100644 index 000000000..6af4b1eda --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/imply_option/values.configure @@ -0,0 +1,24 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--enable-foo', nargs='*', help='enable foo') + +@depends('--enable-foo') +def foo(value): + if value: + return value + +imply_option('--enable-bar', foo) + + +option('--enable-bar', nargs='*', help='enable bar') + +@depends('--enable-bar') +def bar(value): + if value: + return value + +set_config('BAR', bar) diff --git a/python/mozbuild/mozbuild/test/configure/data/included.configure b/python/mozbuild/mozbuild/test/configure/data/included.configure new file mode 100644 index 000000000..5c056764d --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/included.configure @@ -0,0 +1,53 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +# For more complex and repetitive things, we can create templates +@template +def check_compiler_flag(flag): + @depends(is_gcc) + def check(value): + if value: + return [flag] + set_config('CFLAGS', check) + return check + + +check_compiler_flag('-Werror=foobar') + +# Normal functions can be used in @depends functions. +def fortytwo(): + return 42 + +def twentyone(): + yield 21 + +@depends(is_gcc) +def check(value): + if value: + return fortytwo() + +set_config('TEMPLATE_VALUE', check) + +@depends(is_gcc) +def check(value): + if value: + for val in twentyone(): + return val + +set_config('TEMPLATE_VALUE_2', check) + +# Normal functions can use @imports too to import modules. +@imports('sys') +def platform(): + return sys.platform + +option('--enable-imports-in-template', help='Imports in template') +@depends('--enable-imports-in-template') +def check(value): + if value: + return platform() + +set_config('PLATFORM', check) diff --git a/python/mozbuild/mozbuild/test/configure/data/moz.configure b/python/mozbuild/mozbuild/test/configure/data/moz.configure new file mode 100644 index 000000000..32c4b8535 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/moz.configure @@ -0,0 +1,174 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--enable-simple', help='Enable simple') + +# Setting MOZ_WITH_ENV in the environment has the same effect as passing +# --enable-with-env. +option('--enable-with-env', env='MOZ_WITH_ENV', help='Enable with env') + +# Optional values +option('--enable-values', nargs='*', help='Enable values') + +# Everything supported in the Option class is supported in option(). Assume +# the tests of the Option class are extensive about this. + +# Alternatively to --enable/--disable, there also is --with/--without. The +# difference is semantic only. Behavior is the same as --enable/--disable. + +# When the option name starts with --disable/--without, the default is for +# the option to be enabled. +option('--without-thing', help='Build without thing') + +# A --enable/--with option with a default of False is equivalent to a +# --disable/--without option. This can be used to change the defaults +# depending on e.g. the target or the built application. +option('--with-stuff', default=False, help='Build with stuff') + +# Other kinds of arbitrary options are also allowed. This is effectively +# equivalent to --enable/--with, with no possibility of --disable/--without. +option('--option', env='MOZ_OPTION', help='Option') + +# It is also possible to pass options through the environment only. +option(env='CC', nargs=1, help='C Compiler') + +# Call the function when the --enable-simple option is processed, with its +# OptionValue as argument. +@depends('--enable-simple') +def simple(simple): + if simple: + return simple + +set_config('ENABLED_SIMPLE', simple) + +# There can be multiple functions depending on the same option. +@depends('--enable-simple') +def simple(simple): + return simple + +set_config('SIMPLE', simple) + +@depends('--enable-with-env') +def with_env(with_env): + return with_env + +set_config('WITH_ENV', with_env) + +# It doesn't matter if the dependency is on --enable or --disable +@depends('--disable-values') +def with_env2(values): + return values + +set_config('VALUES', with_env2) + +# It is possible to @depends on environment-only options. +@depends('CC') +def is_gcc(cc): + return cc and 'gcc' in cc[0] + +set_config('IS_GCC', is_gcc) + +# It is possible to depend on the result from another function. +@depends(with_env2) +def with_env3(values): + return values + +set_config('VALUES2', with_env3) + +# @depends functions can also return results for use as input to another +# @depends. +@depends(with_env3) +def with_env4(values): + return values + +@depends(with_env4) +def with_env5(values): + return values + +set_config('VALUES3', with_env5) + +# The result from @depends functions can also be used as input to options. +# The result must be returned, not implied. The function must also depend +# on --help. +@depends('--enable-simple', '--help') +def simple(simple, help): + return 'simple' if simple else 'not-simple' + +option('--with-returned-default', default=simple, help='Returned default') + +@depends('--with-returned-default') +def default(value): + return value + +set_config('DEFAULTED', default) + +@depends('--enable-values', '--help') +def choices(values, help): + if len(values): + return { + 'alpha': ('a', 'b', 'c'), + 'numeric': ('0', '1', '2'), + }.get(values[0]) + +option('--returned-choices', choices=choices, help='Choices') + +@depends('--returned-choices') +def returned_choices(values): + return values + +set_config('CHOICES', returned_choices) + +# All options must be referenced by some @depends function. +# It is possible to depend on multiple options/functions +@depends('--without-thing', '--with-stuff', with_env4, '--option') +def remainder(*args): + return args + +set_config('REMAINDER', remainder) + +# It is possible to include other files to extend the configuration script. +include('included.configure') + +# It is also possible for the include file path to come from the result of a +# @depends function. That function needs to depend on '--help' like for option +# defaults and choices. +option('--enable-include', nargs=1, help='Include') +@depends('--enable-include', '--help') +def include_path(path, help): + return path[0] if path else None + +include(include_path) + +# Sandboxed functions can import from modules through the use of the @imports +# decorator. +# The order of the decorators matter: @imports needs to appear after other +# decorators. +option('--with-imports', nargs='?', help='Imports') + +# A limited set of functions from os.path are exposed by default. +@depends('--with-imports') +def with_imports(value): + if len(value): + return hasattr(os.path, 'abspath') + +set_config('HAS_ABSPATH', with_imports) + +# It is still possible to import the full set from os.path. +# It is also possible to cherry-pick builtins. +@depends('--with-imports') +@imports('os.path') +def with_imports(value): + if len(value): + return hasattr(os.path, 'getatime') + +set_config('HAS_GETATIME', with_imports) + +@depends('--with-imports') +def with_imports(value): + if len(value): + return hasattr(os.path, 'getatime') + +set_config('HAS_GETATIME2', with_imports) diff --git a/python/mozbuild/mozbuild/test/configure/data/set_config.configure b/python/mozbuild/mozbuild/test/configure/data/set_config.configure new file mode 100644 index 000000000..cf5743963 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/set_config.configure @@ -0,0 +1,43 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--set-foo', help='set foo') + +@depends('--set-foo') +def foo(value): + if value: + return True + +set_config('FOO', foo) + + +option('--set-bar', help='set bar') + +@depends('--set-bar') +def bar(value): + return bool(value) + +set_config('BAR', bar) + + +option('--set-value', nargs=1, help='set value') + +@depends('--set-value') +def set_value(value): + if value: + return value[0] + +set_config('VALUE', set_value) + + +option('--set-name', nargs=1, help='set name') + +@depends('--set-name') +def set_name(value): + if value: + return value[0] + +set_config(set_name, True) diff --git a/python/mozbuild/mozbuild/test/configure/data/set_define.configure b/python/mozbuild/mozbuild/test/configure/data/set_define.configure new file mode 100644 index 000000000..422263427 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/set_define.configure @@ -0,0 +1,43 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +option('--set-foo', help='set foo') + +@depends('--set-foo') +def foo(value): + if value: + return True + +set_define('FOO', foo) + + +option('--set-bar', help='set bar') + +@depends('--set-bar') +def bar(value): + return bool(value) + +set_define('BAR', bar) + + +option('--set-value', nargs=1, help='set value') + +@depends('--set-value') +def set_value(value): + if value: + return value[0] + +set_define('VALUE', set_value) + + +option('--set-name', nargs=1, help='set name') + +@depends('--set-name') +def set_name(value): + if value: + return value[0] + +set_define(set_name, True) diff --git a/python/mozbuild/mozbuild/test/configure/data/subprocess.configure b/python/mozbuild/mozbuild/test/configure/data/subprocess.configure new file mode 100644 index 000000000..de6be9cec --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/subprocess.configure @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +@depends('--help') +@imports('codecs') +@imports(_from='mozbuild.configure.util', _import='getpreferredencoding') +@imports('os') +@imports(_from='__builtin__', _import='open') +def dies_when_logging(_): + test_file = 'test.txt' + quote_char = "'" + if getpreferredencoding().lower() == 'utf-8': + quote_char = '\u00B4'.encode('utf-8') + try: + with open(test_file, 'w+') as fh: + fh.write(quote_char) + out = check_cmd_output('cat', 'test.txt') + log.info(out) + finally: + os.remove(test_file) diff --git a/python/mozbuild/mozbuild/test/configure/lint.py b/python/mozbuild/mozbuild/test/configure/lint.py new file mode 100644 index 000000000..9965a60e9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/lint.py @@ -0,0 +1,65 @@ +# 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 + +import os +import unittest +from StringIO import StringIO +from mozunit import main +from buildconfig import ( + topobjdir, + topsrcdir, +) + +from mozbuild.configure.lint import LintSandbox + + +test_path = os.path.abspath(__file__) + + +class LintMeta(type): + def __new__(mcs, name, bases, attrs): + def create_test(project, func): + def test(self): + return func(self, project) + return test + + for project in ( + 'b2g', + 'b2g/dev', + 'b2g/graphene', + 'browser', + 'embedding/ios', + 'extensions', + 'js', + 'mobile/android', + ): + attrs['test_%s' % project.replace('/', '_')] = create_test( + project, attrs['lint']) + + return type.__new__(mcs, name, bases, attrs) + + +class Lint(unittest.TestCase): + __metaclass__ = LintMeta + + def setUp(self): + self._curdir = os.getcwd() + os.chdir(topobjdir) + + def tearDown(self): + os.chdir(self._curdir) + + def lint(self, project): + sandbox = LintSandbox({ + 'OLD_CONFIGURE': os.path.join(topsrcdir, 'old-configure'), + 'MOZCONFIG': os.path.join(os.path.dirname(test_path), 'data', + 'empty_mozconfig'), + }, ['--enable-project=%s' % project]) + sandbox.run(os.path.join(topsrcdir, 'moz.configure')) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py new file mode 100644 index 000000000..181c7acbd --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py @@ -0,0 +1,940 @@ +# 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 sys +import textwrap +import unittest + +from mozunit import ( + main, + MockedOpen, +) + +from mozbuild.configure import ( + ConfigureError, + ConfigureSandbox, +) +from mozbuild.util import exec_ +from mozpack import path as mozpath + +from buildconfig import topsrcdir +from common import ( + ConfigureTestSandbox, + ensure_exe_extension, + fake_short_path, +) + + +class TestChecksConfigure(unittest.TestCase): + def test_checking(self): + out = StringIO() + 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) + + foo = sandbox['foo'] + + foo(True) + self.assertEqual(out.getvalue(), 'checking for a thing... yes\n') + + out.truncate(0) + foo(False) + self.assertEqual(out.getvalue(), 'checking for a thing... no\n') + + out.truncate(0) + foo(42) + self.assertEqual(out.getvalue(), 'checking for a thing... 42\n') + + out.truncate(0) + foo('foo') + self.assertEqual(out.getvalue(), 'checking for a thing... foo\n') + + out.truncate(0) + data = ['foo', 'bar'] + foo(data) + self.assertEqual(out.getvalue(), 'checking for a thing... %r\n' % data) + + # When the function given to checking does nothing interesting, the + # behavior is not altered + exec_(textwrap.dedent(''' + @checking('for a thing', lambda x: x) + def foo(value): + return value + '''), sandbox) + + foo = sandbox['foo'] + + out.truncate(0) + foo(True) + self.assertEqual(out.getvalue(), 'checking for a thing... yes\n') + + out.truncate(0) + foo(False) + self.assertEqual(out.getvalue(), 'checking for a thing... no\n') + + out.truncate(0) + foo(42) + self.assertEqual(out.getvalue(), 'checking for a thing... 42\n') + + out.truncate(0) + foo('foo') + self.assertEqual(out.getvalue(), 'checking for a thing... foo\n') + + out.truncate(0) + data = ['foo', 'bar'] + foo(data) + self.assertEqual(out.getvalue(), 'checking for a thing... %r\n' % data) + + exec_(textwrap.dedent(''' + def munge(x): + if not x: + return 'not found' + if isinstance(x, (str, bool, int)): + return x + return ' '.join(x) + + @checking('for a thing', munge) + def foo(value): + return value + '''), sandbox) + + foo = sandbox['foo'] + + out.truncate(0) + foo(True) + self.assertEqual(out.getvalue(), 'checking for a thing... yes\n') + + out.truncate(0) + foo(False) + self.assertEqual(out.getvalue(), 'checking for a thing... not found\n') + + out.truncate(0) + foo(42) + self.assertEqual(out.getvalue(), 'checking for a thing... 42\n') + + 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 = ensure_exe_extension(mozpath.abspath('/usr/bin/known-a')) + KNOWN_B = ensure_exe_extension(mozpath.abspath('/usr/local/bin/known-b')) + KNOWN_C = ensure_exe_extension(mozpath.abspath('/home/user/bin/known c')) + OTHER_A = ensure_exe_extension(mozpath.abspath('/lib/other/known-a')) + + def get_result(self, command='', args=[], environ={}, + prog='/bin/configure', extra_paths=None, + includes=('util.configure', 'checks.configure')): + config = {} + out = StringIO() + paths = { + self.KNOWN_A: None, + self.KNOWN_B: None, + self.KNOWN_C: None, + } + if extra_paths: + paths.update(extra_paths) + environ = dict(environ) + if 'PATH' not in environ: + environ['PATH'] = os.pathsep.join(os.path.dirname(p) for p in paths) + paths[self.OTHER_A] = None + sandbox = ConfigureTestSandbox(paths, config, environ, [prog] + args, + out, out) + base_dir = os.path.join(topsrcdir, 'build', 'moz.configure') + for f in includes: + sandbox.include_file(os.path.join(base_dir, f)) + + status = 0 + try: + exec_(command, sandbox) + sandbox.run() + 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': 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': 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': fake_short_path(self.KNOWN_C)}) + self.assertEqual(out, "checking for foo... '%s'\n" + % fake_short_path(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 + ERROR: Cannot find foo + ''')) + + config, out, status = self.get_result( + 'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"))') + self.assertEqual(status, 1) + self.assertEqual(config, {}) + self.assertEqual(out, textwrap.dedent('''\ + checking for foo... not found + DEBUG: foo: Trying unknown + DEBUG: foo: Trying unknown-2 + DEBUG: foo: Trying 'unknown 3' + ERROR: Cannot find foo + ''')) + + 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') + + @unittest.skipIf(not sys.platform.startswith('win'), 'Windows-only test') + def test_check_prog_exe(self): + config, out, status = self.get_result( + 'check_prog("FOO", ("unknown", "known-b", "known c"))', + ['FOO=known-a.exe']) + self.assertEqual(status, 0) + 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=%s' % os.path.splitext(self.KNOWN_A)[0]]) + self.assertEqual(status, 0) + self.assertEqual(config, {'FOO': self.KNOWN_A}) + self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A) + + + 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': 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=%s' % self.KNOWN_A]) + self.assertEqual(status, 0) + 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=%s' % path]) + self.assertEqual(status, 1) + self.assertEqual(config, {}) + self.assertEqual(out, textwrap.dedent('''\ + checking for foo... not found + 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': fake_short_path(self.KNOWN_C)}) + self.assertEqual(out, "checking for foo... '%s'\n" + % fake_short_path(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': self.KNOWN_A}) + self.assertEqual( + 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 + DEBUG: cc: Trying unknown + DEBUG: cc: Trying unknown-2 + DEBUG: cc: Trying 'unknown 3' + ERROR: Cannot find the target C compiler + ''')) + + 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': 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': 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': 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': 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': 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") + if value[0] == "winnt": + return ("cl", "clang-cl") + check_prog("CC", compiler) + ''') + config, out, status = self.get_result(script) + self.assertEqual(status, 1) + self.assertEqual(config, {}) + self.assertEqual(out, textwrap.dedent('''\ + checking for cc... not found + DEBUG: cc: Trying gcc + DEBUG: cc: Trying clang + ERROR: Cannot find cc + ''')) + + config, out, status = self.get_result(script, ['TARGET=linux']) + self.assertEqual(status, 1) + self.assertEqual(config, {}) + self.assertEqual(out, textwrap.dedent('''\ + checking for cc... not found + DEBUG: cc: Trying gcc + DEBUG: cc: Trying clang + ERROR: Cannot find cc + ''')) + + config, out, status = self.get_result(script, ['TARGET=winnt']) + self.assertEqual(status, 1) + self.assertEqual(config, {}) + self.assertEqual(out, textwrap.dedent('''\ + checking for cc... not found + DEBUG: cc: Trying cl + DEBUG: cc: Trying clang-cl + ERROR: Cannot find cc + ''')) + + 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': 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': 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!') + + with self.assertRaises(ConfigureError) as e: + self.get_result( + 'foo = depends(when=True)(lambda: ("a", "b"))\n' + 'check_prog("FOO", ("known-a",), input=foo)' + ) + + self.assertEqual(e.exception.message, + 'input must resolve to a tuple or a list with a ' + 'single element, or a string') + + with self.assertRaises(ConfigureError) as e: + self.get_result( + 'foo = depends(when=True)(lambda: {"a": "b"})\n' + 'check_prog("FOO", ("known-a",), input=foo)' + ) + + self.assertEqual(e.exception.message, + 'input must resolve to a tuple or a list with a ' + 'single element, or a string') + + def test_check_prog_with_path(self): + config, out, status = self.get_result('check_prog("A", ("known-a",), paths=["/some/path"])') + self.assertEqual(status, 1) + self.assertEqual(config, {}) + self.assertEqual(out, textwrap.dedent('''\ + checking for a... not found + DEBUG: a: Trying known-a + ERROR: Cannot find a + ''')) + + config, out, status = self.get_result('check_prog("A", ("known-a",), paths=["%s"])' % + os.path.dirname(self.OTHER_A)) + self.assertEqual(status, 0) + self.assertEqual(config, {'A': self.OTHER_A}) + self.assertEqual(out, textwrap.dedent('''\ + checking for a... %s + ''' % self.OTHER_A)) + + dirs = map(mozpath.dirname, (self.OTHER_A, self.KNOWN_A)) + config, out, status = self.get_result(textwrap.dedent('''\ + check_prog("A", ("known-a",), paths=["%s"]) + ''' % os.pathsep.join(dirs))) + self.assertEqual(status, 0) + self.assertEqual(config, {'A': self.OTHER_A}) + self.assertEqual(out, textwrap.dedent('''\ + checking for a... %s + ''' % self.OTHER_A)) + + dirs = map(mozpath.dirname, (self.KNOWN_A, self.KNOWN_B)) + config, out, status = self.get_result(textwrap.dedent('''\ + check_prog("A", ("known-a",), paths=["%s", "%s"]) + ''' % (os.pathsep.join(dirs), self.OTHER_A))) + self.assertEqual(status, 0) + self.assertEqual(config, {'A': self.KNOWN_A}) + self.assertEqual(out, textwrap.dedent('''\ + checking for a... %s + ''' % self.KNOWN_A)) + + config, out, status = self.get_result('check_prog("A", ("known-a",), paths="%s")' % + os.path.dirname(self.OTHER_A)) + + self.assertEqual(status, 1) + self.assertEqual(config, {}) + self.assertEqual(out, textwrap.dedent('''\ + checking for a... + DEBUG: a: Trying known-a + ERROR: Paths provided to find_program must be a list of strings, not %r + ''' % mozpath.dirname(self.OTHER_A))) + + def test_java_tool_checks(self): + includes = ('util.configure', 'checks.configure', 'java.configure') + + def mock_valid_javac(_, args): + if len(args) == 1 and args[0] == '-version': + return 0, '1.7', '' + self.fail("Unexpected arguments to mock_valid_javac: %s" % args) + + # A valid set of tools in a standard location. + java = mozpath.abspath('/usr/bin/java') + javah = mozpath.abspath('/usr/bin/javah') + javac = mozpath.abspath('/usr/bin/javac') + jar = mozpath.abspath('/usr/bin/jar') + jarsigner = mozpath.abspath('/usr/bin/jarsigner') + keytool = mozpath.abspath('/usr/bin/keytool') + + paths = { + java: None, + javah: None, + javac: mock_valid_javac, + jar: None, + jarsigner: None, + keytool: None, + } + + config, out, status = self.get_result(includes=includes, extra_paths=paths) + self.assertEqual(status, 0) + self.assertEqual(config, { + 'JAVA': java, + 'JAVAH': javah, + 'JAVAC': javac, + 'JAR': jar, + 'JARSIGNER': jarsigner, + 'KEYTOOL': keytool, + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for java... %s + checking for javah... %s + checking for jar... %s + checking for jarsigner... %s + checking for keytool... %s + checking for javac... %s + checking for javac version... 1.7 + ''' % (java, javah, jar, jarsigner, keytool, javac))) + + # An alternative valid set of tools referred to by JAVA_HOME. + alt_java = mozpath.abspath('/usr/local/bin/java') + alt_javah = mozpath.abspath('/usr/local/bin/javah') + alt_javac = mozpath.abspath('/usr/local/bin/javac') + alt_jar = mozpath.abspath('/usr/local/bin/jar') + alt_jarsigner = mozpath.abspath('/usr/local/bin/jarsigner') + alt_keytool = mozpath.abspath('/usr/local/bin/keytool') + alt_java_home = mozpath.dirname(mozpath.dirname(alt_java)) + + paths.update({ + alt_java: None, + alt_javah: None, + alt_javac: mock_valid_javac, + alt_jar: None, + alt_jarsigner: None, + alt_keytool: None, + }) + + config, out, status = self.get_result(includes=includes, + extra_paths=paths, + environ={ + 'JAVA_HOME': alt_java_home, + 'PATH': mozpath.dirname(java) + }) + self.assertEqual(status, 0) + self.assertEqual(config, { + 'JAVA': alt_java, + 'JAVAH': alt_javah, + 'JAVAC': alt_javac, + 'JAR': alt_jar, + 'JARSIGNER': alt_jarsigner, + 'KEYTOOL': alt_keytool, + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for java... %s + checking for javah... %s + checking for jar... %s + checking for jarsigner... %s + checking for keytool... %s + checking for javac... %s + checking for javac version... 1.7 + ''' % (alt_java, alt_javah, alt_jar, alt_jarsigner, + alt_keytool, alt_javac))) + + # We can use --with-java-bin-path instead of JAVA_HOME to similar + # effect. + config, out, status = self.get_result( + args=['--with-java-bin-path=%s' % mozpath.dirname(alt_java)], + includes=includes, + extra_paths=paths, + environ={ + 'PATH': mozpath.dirname(java) + }) + self.assertEqual(status, 0) + self.assertEqual(config, { + 'JAVA': alt_java, + 'JAVAH': alt_javah, + 'JAVAC': alt_javac, + 'JAR': alt_jar, + 'JARSIGNER': alt_jarsigner, + 'KEYTOOL': alt_keytool, + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for java... %s + checking for javah... %s + checking for jar... %s + checking for jarsigner... %s + checking for keytool... %s + checking for javac... %s + checking for javac version... 1.7 + ''' % (alt_java, alt_javah, alt_jar, alt_jarsigner, + alt_keytool, alt_javac))) + + # If --with-java-bin-path and JAVA_HOME are both set, + # --with-java-bin-path takes precedence. + config, out, status = self.get_result( + args=['--with-java-bin-path=%s' % mozpath.dirname(alt_java)], + includes=includes, + extra_paths=paths, + environ={ + 'PATH': mozpath.dirname(java), + 'JAVA_HOME': mozpath.dirname(mozpath.dirname(java)), + }) + self.assertEqual(status, 0) + self.assertEqual(config, { + 'JAVA': alt_java, + 'JAVAH': alt_javah, + 'JAVAC': alt_javac, + 'JAR': alt_jar, + 'JARSIGNER': alt_jarsigner, + 'KEYTOOL': alt_keytool, + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for java... %s + checking for javah... %s + checking for jar... %s + checking for jarsigner... %s + checking for keytool... %s + checking for javac... %s + checking for javac version... 1.7 + ''' % (alt_java, alt_javah, alt_jar, alt_jarsigner, + alt_keytool, alt_javac))) + + def mock_old_javac(_, args): + if len(args) == 1 and args[0] == '-version': + return 0, '1.6.9', '' + self.fail("Unexpected arguments to mock_old_javac: %s" % args) + + # An old javac is fatal. + paths[javac] = mock_old_javac + config, out, status = self.get_result(includes=includes, + extra_paths=paths, + environ={ + 'PATH': mozpath.dirname(java) + }) + self.assertEqual(status, 1) + self.assertEqual(config, { + 'JAVA': java, + 'JAVAH': javah, + 'JAVAC': javac, + 'JAR': jar, + 'JARSIGNER': jarsigner, + 'KEYTOOL': keytool, + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for java... %s + checking for javah... %s + checking for jar... %s + checking for jarsigner... %s + checking for keytool... %s + checking for javac... %s + checking for javac version... + ERROR: javac 1.7 or higher is required (found 1.6.9) + ''' % (java, javah, jar, jarsigner, keytool, javac))) + + # Any missing tool is fatal when these checks run. + del paths[jarsigner] + config, out, status = self.get_result(includes=includes, + extra_paths=paths, + environ={ + 'PATH': mozpath.dirname(java) + }) + self.assertEqual(status, 1) + self.assertEqual(config, { + 'JAVA': java, + 'JAVAH': javah, + 'JAR': jar, + 'JARSIGNER': ':', + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for java... %s + checking for javah... %s + checking for jar... %s + checking for jarsigner... not found + ERROR: The program jarsigner was not found. Set $JAVA_HOME to your Java SDK directory or use '--with-java-bin-path={java-bin-dir}' + ''' % (java, javah, jar))) + + def test_pkg_check_modules(self): + mock_pkg_config_version = '0.10.0' + mock_pkg_config_path = mozpath.abspath('/usr/bin/pkg-config') + + def mock_pkg_config(_, args): + if args[0:2] == ['--errors-to-stdout', '--print-errors']: + assert len(args) == 3 + package = args[2] + if package == 'unknown': + return (1, "Package unknown was not found in the pkg-config search path.\n" + "Perhaps you should add the directory containing `unknown.pc'\n" + "to the PKG_CONFIG_PATH environment variable\n" + "No package 'unknown' found", '') + if package == 'valid': + return 0, '', '' + if package == 'new > 1.1': + return 1, "Requested 'new > 1.1' but version of new is 1.1", '' + if args[0] == '--cflags': + assert len(args) == 2 + return 0, '-I/usr/include/%s' % args[1], '' + if args[0] == '--libs': + assert len(args) == 2 + return 0, '-l%s' % args[1], '' + if args[0] == '--version': + return 0, mock_pkg_config_version, '' + self.fail("Unexpected arguments to mock_pkg_config: %s" % args) + + def get_result(cmd, args=[], extra_paths=None): + return self.get_result(textwrap.dedent('''\ + option('--disable-compile-environment', help='compile env') + include('%(topsrcdir)s/build/moz.configure/util.configure') + include('%(topsrcdir)s/build/moz.configure/checks.configure') + include('%(topsrcdir)s/build/moz.configure/pkg.configure') + ''' % {'topsrcdir': topsrcdir}) + cmd, args=args, extra_paths=extra_paths, + includes=()) + + extra_paths = { + mock_pkg_config_path: mock_pkg_config, + } + includes = ('util.configure', 'checks.configure', 'pkg.configure') + + config, output, status = get_result("pkg_check_modules('MOZ_VALID', 'valid')") + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for pkg_config... not found + ERROR: *** The pkg-config script could not be found. Make sure it is + *** in your path, or set the PKG_CONFIG environment variable + *** to the full path to pkg-config. + ''')) + + + config, output, status = get_result("pkg_check_modules('MOZ_VALID', 'valid')", + extra_paths=extra_paths) + self.assertEqual(status, 0) + self.assertEqual(output, textwrap.dedent('''\ + checking for pkg_config... %s + checking for pkg-config version... %s + checking for valid... yes + checking MOZ_VALID_CFLAGS... -I/usr/include/valid + checking MOZ_VALID_LIBS... -lvalid + ''' % (mock_pkg_config_path, mock_pkg_config_version))) + self.assertEqual(config, { + 'PKG_CONFIG': mock_pkg_config_path, + 'MOZ_VALID_CFLAGS': ('-I/usr/include/valid',), + 'MOZ_VALID_LIBS': ('-lvalid',), + }) + + config, output, status = get_result("pkg_check_modules('MOZ_UKNOWN', 'unknown')", + extra_paths=extra_paths) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for pkg_config... %s + checking for pkg-config version... %s + checking for unknown... no + ERROR: Package unknown was not found in the pkg-config search path. + ERROR: Perhaps you should add the directory containing `unknown.pc' + ERROR: to the PKG_CONFIG_PATH environment variable + ERROR: No package 'unknown' found + ''' % (mock_pkg_config_path, mock_pkg_config_version))) + self.assertEqual(config, { + 'PKG_CONFIG': mock_pkg_config_path, + }) + + config, output, status = get_result("pkg_check_modules('MOZ_NEW', 'new > 1.1')", + extra_paths=extra_paths) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for pkg_config... %s + checking for pkg-config version... %s + checking for new > 1.1... no + ERROR: Requested 'new > 1.1' but version of new is 1.1 + ''' % (mock_pkg_config_path, mock_pkg_config_version))) + self.assertEqual(config, { + 'PKG_CONFIG': mock_pkg_config_path, + }) + + # allow_missing makes missing packages non-fatal. + cmd = textwrap.dedent('''\ + have_new_module = pkg_check_modules('MOZ_NEW', 'new > 1.1', allow_missing=True) + @depends(have_new_module) + def log_new_module_error(mod): + if mod is not True: + log.info('Module not found.') + ''') + + config, output, status = get_result(cmd, extra_paths=extra_paths) + self.assertEqual(status, 0) + self.assertEqual(output, textwrap.dedent('''\ + checking for pkg_config... %s + checking for pkg-config version... %s + checking for new > 1.1... no + WARNING: Requested 'new > 1.1' but version of new is 1.1 + Module not found. + ''' % (mock_pkg_config_path, mock_pkg_config_version))) + self.assertEqual(config, { + 'PKG_CONFIG': mock_pkg_config_path, + }) + + config, output, status = get_result(cmd, + args=['--disable-compile-environment'], + extra_paths=extra_paths) + self.assertEqual(status, 0) + self.assertEqual(output, 'Module not found.\n') + self.assertEqual(config, {}) + + def mock_old_pkg_config(_, args): + if args[0] == '--version': + return 0, '0.8.10', '' + self.fail("Unexpected arguments to mock_old_pkg_config: %s" % args) + + extra_paths = { + mock_pkg_config_path: mock_old_pkg_config, + } + + config, output, status = get_result("pkg_check_modules('MOZ_VALID', 'valid')", + extra_paths=extra_paths) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for pkg_config... %s + checking for pkg-config version... 0.8.10 + ERROR: *** Your version of pkg-config is too old. You need version 0.9.0 or newer. + ''' % mock_pkg_config_path)) + + def test_simple_keyfile(self): + includes = ('util.configure', 'checks.configure', 'keyfiles.configure') + + config, output, status = self.get_result( + "simple_keyfile('Mozilla API')", includes=includes) + self.assertEqual(status, 0) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Mozilla API key... no + ''')) + self.assertEqual(config, { + 'MOZ_MOZILLA_API_KEY': 'no-mozilla-api-key', + }) + + config, output, status = self.get_result( + "simple_keyfile('Mozilla API')", + args=['--with-mozilla-api-keyfile=/foo/bar/does/not/exist'], + includes=includes) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Mozilla API key... no + ERROR: '/foo/bar/does/not/exist': No such file or directory. + ''')) + self.assertEqual(config, {}) + + with MockedOpen({'key': ''}): + config, output, status = self.get_result( + "simple_keyfile('Mozilla API')", + args=['--with-mozilla-api-keyfile=key'], + includes=includes) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Mozilla API key... no + ERROR: 'key' is empty. + ''')) + self.assertEqual(config, {}) + + with MockedOpen({'key': 'fake-key\n'}): + config, output, status = self.get_result( + "simple_keyfile('Mozilla API')", + args=['--with-mozilla-api-keyfile=key'], + includes=includes) + self.assertEqual(status, 0) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Mozilla API key... yes + ''')) + self.assertEqual(config, { + 'MOZ_MOZILLA_API_KEY': 'fake-key', + }) + + def test_id_and_secret_keyfile(self): + includes = ('util.configure', 'checks.configure', 'keyfiles.configure') + + config, output, status = self.get_result( + "id_and_secret_keyfile('Bing API')", includes=includes) + self.assertEqual(status, 0) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Bing API key... no + ''')) + self.assertEqual(config, { + 'MOZ_BING_API_CLIENTID': 'no-bing-api-clientid', + 'MOZ_BING_API_KEY': 'no-bing-api-key', + }) + + config, output, status = self.get_result( + "id_and_secret_keyfile('Bing API')", + args=['--with-bing-api-keyfile=/foo/bar/does/not/exist'], + includes=includes) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Bing API key... no + ERROR: '/foo/bar/does/not/exist': No such file or directory. + ''')) + self.assertEqual(config, {}) + + with MockedOpen({'key': ''}): + config, output, status = self.get_result( + "id_and_secret_keyfile('Bing API')", + args=['--with-bing-api-keyfile=key'], + includes=includes) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Bing API key... no + ERROR: 'key' is empty. + ''')) + self.assertEqual(config, {}) + + with MockedOpen({'key': 'fake-id fake-key\n'}): + config, output, status = self.get_result( + "id_and_secret_keyfile('Bing API')", + args=['--with-bing-api-keyfile=key'], + includes=includes) + self.assertEqual(status, 0) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Bing API key... yes + ''')) + self.assertEqual(config, { + 'MOZ_BING_API_CLIENTID': 'fake-id', + 'MOZ_BING_API_KEY': 'fake-key', + }) + + with MockedOpen({'key': 'fake-key\n'}): + config, output, status = self.get_result( + "id_and_secret_keyfile('Bing API')", + args=['--with-bing-api-keyfile=key'], + includes=includes) + self.assertEqual(status, 1) + self.assertEqual(output, textwrap.dedent('''\ + checking for the Bing API key... no + ERROR: Bing API key file has an invalid format. + ''')) + self.assertEqual(config, {}) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_compile_checks.py b/python/mozbuild/mozbuild/test/configure/test_compile_checks.py new file mode 100644 index 000000000..5913dbe3d --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_compile_checks.py @@ -0,0 +1,403 @@ +# 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 + +import os +import textwrap +import unittest +import mozpack.path as mozpath + +from StringIO import StringIO + +from buildconfig import topsrcdir +from common import ConfigureTestSandbox +from mozbuild.util import exec_ +from mozunit import main +from test_toolchain_helpers import FakeCompiler + + +class BaseCompileChecks(unittest.TestCase): + def get_mock_compiler(self, expected_test_content=None, expected_flags=None): + expected_flags = expected_flags or [] + def mock_compiler(stdin, args): + args, test_file = args[:-1], args[-1] + self.assertIn('-c', args) + for flag in expected_flags: + self.assertIn(flag, args) + + if expected_test_content: + with open(test_file) as fh: + test_content = fh.read() + self.assertEqual(test_content, expected_test_content) + + return FakeCompiler()(None, args) + return mock_compiler + + def do_compile_test(self, command, expected_test_content=None, + expected_flags=None): + + paths = { + os.path.abspath('/usr/bin/mockcc'): self.get_mock_compiler( + expected_test_content=expected_test_content, + expected_flags=expected_flags), + } + + base_dir = os.path.join(topsrcdir, 'build', 'moz.configure') + + mock_compiler_defs = textwrap.dedent('''\ + @depends(when=True) + def extra_toolchain_flags(): + return [] + + include('%s/compilers-util.configure') + + @compiler_class + @depends(when=True) + def c_compiler(): + return namespace( + flags=[], + type='gcc', + compiler=os.path.abspath('/usr/bin/mockcc'), + wrapper=[], + language='C', + ) + + @compiler_class + @depends(when=True) + def cxx_compiler(): + return namespace( + flags=[], + type='gcc', + compiler=os.path.abspath('/usr/bin/mockcc'), + wrapper=[], + language='C++', + ) + ''' % mozpath.normsep(base_dir)) + + config = {} + out = StringIO() + sandbox = ConfigureTestSandbox(paths, config, {}, ['/bin/configure'], + out, out) + sandbox.include_file(os.path.join(base_dir, 'util.configure')) + sandbox.include_file(os.path.join(base_dir, 'checks.configure')) + exec_(mock_compiler_defs, sandbox) + sandbox.include_file(os.path.join(base_dir, 'compile-checks.configure')) + + status = 0 + try: + exec_(command, sandbox) + sandbox.run() + except SystemExit as e: + status = e.code + + return config, out.getvalue(), status + + +class TestHeaderChecks(BaseCompileChecks): + def test_try_compile_include(self): + expected_test_content = textwrap.dedent('''\ + #include <foo.h> + #include <bar.h> + int + main(void) + { + + ; + return 0; + } + ''') + + cmd = textwrap.dedent('''\ + try_compile(['foo.h', 'bar.h'], language='C') + ''') + + config, out, status = self.do_compile_test(cmd, expected_test_content) + self.assertEqual(status, 0) + self.assertEqual(config, {}) + + def test_try_compile_flags(self): + expected_flags = ['--extra', '--flags'] + + cmd = textwrap.dedent('''\ + try_compile(language='C++', flags=['--flags', '--extra']) + ''') + + config, out, status = self.do_compile_test(cmd, expected_flags=expected_flags) + self.assertEqual(status, 0) + self.assertEqual(config, {}) + + def test_try_compile_failure(self): + cmd = textwrap.dedent('''\ + have_fn = try_compile(body='somefn();', flags=['-funknown-flag']) + set_config('HAVE_SOMEFN', have_fn) + + have_another = try_compile(body='anotherfn();', language='C') + set_config('HAVE_ANOTHERFN', have_another) + ''') + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + 'HAVE_ANOTHERFN': True, + }) + + def test_try_compile_msg(self): + cmd = textwrap.dedent('''\ + known_flag = try_compile(language='C++', flags=['-fknown-flag'], + check_msg='whether -fknown-flag works') + set_config('HAVE_KNOWN_FLAG', known_flag) + ''') + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, {'HAVE_KNOWN_FLAG': True}) + self.assertEqual(out, textwrap.dedent('''\ + checking whether -fknown-flag works... yes + ''')) + + def test_check_header(self): + expected_test_content = textwrap.dedent('''\ + #include <foo.h> + int + main(void) + { + + ; + return 0; + } + ''') + + cmd = textwrap.dedent('''\ + check_header('foo.h') + ''') + + config, out, status = self.do_compile_test(cmd, + expected_test_content=expected_test_content) + self.assertEqual(status, 0) + self.assertEqual(config, {'DEFINES': {'HAVE_FOO_H': True}}) + self.assertEqual(out, textwrap.dedent('''\ + checking for foo.h... yes + ''')) + + def test_check_header_conditional(self): + cmd = textwrap.dedent('''\ + check_headers('foo.h', 'bar.h', when=never) + ''') + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(out, '') + self.assertEqual(config, {'DEFINES':{}}) + + def test_check_header_include(self): + expected_test_content = textwrap.dedent('''\ + #include <std.h> + #include <bar.h> + #include <foo.h> + int + main(void) + { + + ; + return 0; + } + ''') + + cmd = textwrap.dedent('''\ + have_foo = check_header('foo.h', includes=['std.h', 'bar.h']) + set_config('HAVE_FOO_H', have_foo) + ''') + + config, out, status = self.do_compile_test(cmd, + expected_test_content=expected_test_content) + + self.assertEqual(status, 0) + self.assertEqual(config, { + 'HAVE_FOO_H': True, + 'DEFINES': { + 'HAVE_FOO_H': True, + } + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for foo.h... yes + ''')) + + def test_check_headers_multiple(self): + cmd = textwrap.dedent('''\ + baz_bar, quux_bar = check_headers('baz/foo-bar.h', 'baz-quux/foo-bar.h') + set_config('HAVE_BAZ_BAR', baz_bar) + set_config('HAVE_QUUX_BAR', quux_bar) + ''') + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + 'HAVE_BAZ_BAR': True, + 'HAVE_QUUX_BAR': True, + 'DEFINES': { + 'HAVE_BAZ_FOO_BAR_H': True, + 'HAVE_BAZ_QUUX_FOO_BAR_H': True, + } + }) + self.assertEqual(out, textwrap.dedent('''\ + checking for baz/foo-bar.h... yes + checking for baz-quux/foo-bar.h... yes + ''')) + + def test_check_headers_not_found(self): + + cmd = textwrap.dedent('''\ + baz_bar, quux_bar = check_headers('baz/foo-bar.h', 'baz-quux/foo-bar.h', + flags=['-funknown-flag']) + set_config('HAVE_BAZ_BAR', baz_bar) + set_config('HAVE_QUUX_BAR', quux_bar) + ''') + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, {'DEFINES': {}}) + self.assertEqual(out, textwrap.dedent('''\ + checking for baz/foo-bar.h... no + checking for baz-quux/foo-bar.h... no + ''')) + + +class TestWarningChecks(BaseCompileChecks): + def get_warnings(self): + return textwrap.dedent('''\ + set_config('_WARNINGS_CFLAGS', warnings_cflags) + set_config('_WARNINGS_CXXFLAGS', warnings_cxxflags) + ''') + + def test_check_and_add_gcc_warning(self): + for flag, expected_flags in ( + ('-Wfoo', ['-Werror', '-Wfoo']), + ('-Wno-foo', ['-Werror', '-Wfoo']), + ('-Werror=foo', ['-Werror=foo']), + ('-Wno-error=foo', ['-Wno-error=foo']), + ): + cmd = textwrap.dedent('''\ + check_and_add_gcc_warning('%s') + ''' % flag) + self.get_warnings() + + config, out, status = self.do_compile_test( + cmd, expected_flags=expected_flags) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': [flag], + '_WARNINGS_CXXFLAGS': [flag], + }) + self.assertEqual(out, textwrap.dedent('''\ + checking whether the C compiler supports {flag}... yes + checking whether the C++ compiler supports {flag}... yes + '''.format(flag=flag))) + + def test_check_and_add_gcc_warning_one(self): + cmd = textwrap.dedent('''\ + check_and_add_gcc_warning('-Wfoo', cxx_compiler) + ''') + self.get_warnings() + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': [], + '_WARNINGS_CXXFLAGS': ['-Wfoo'], + }) + self.assertEqual(out, textwrap.dedent('''\ + checking whether the C++ compiler supports -Wfoo... yes + ''')) + + def test_check_and_add_gcc_warning_when(self): + cmd = textwrap.dedent('''\ + @depends(when=True) + def never(): + return False + check_and_add_gcc_warning('-Wfoo', cxx_compiler, when=never) + ''') + self.get_warnings() + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': [], + '_WARNINGS_CXXFLAGS': [], + }) + self.assertEqual(out, '') + + cmd = textwrap.dedent('''\ + @depends(when=True) + def always(): + return True + check_and_add_gcc_warning('-Wfoo', cxx_compiler, when=always) + ''') + self.get_warnings() + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': [], + '_WARNINGS_CXXFLAGS': ['-Wfoo'], + }) + self.assertEqual(out, textwrap.dedent('''\ + checking whether the C++ compiler supports -Wfoo... yes + ''')) + + def test_add_gcc_warning(self): + cmd = textwrap.dedent('''\ + add_gcc_warning('-Wfoo') + ''') + self.get_warnings() + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': ['-Wfoo'], + '_WARNINGS_CXXFLAGS': ['-Wfoo'], + }) + self.assertEqual(out, '') + + def test_add_gcc_warning_one(self): + cmd = textwrap.dedent('''\ + add_gcc_warning('-Wfoo', c_compiler) + ''') + self.get_warnings() + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': ['-Wfoo'], + '_WARNINGS_CXXFLAGS': [], + }) + self.assertEqual(out, '') + + def test_add_gcc_warning_when(self): + cmd = textwrap.dedent('''\ + @depends(when=True) + def never(): + return False + add_gcc_warning('-Wfoo', c_compiler, when=never) + ''') + self.get_warnings() + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': [], + '_WARNINGS_CXXFLAGS': [], + }) + self.assertEqual(out, '') + + cmd = textwrap.dedent('''\ + @depends(when=True) + def always(): + return True + add_gcc_warning('-Wfoo', c_compiler, when=always) + ''') + self.get_warnings() + + config, out, status = self.do_compile_test(cmd) + self.assertEqual(status, 0) + self.assertEqual(config, { + '_WARNINGS_CFLAGS': ['-Wfoo'], + '_WARNINGS_CXXFLAGS': [], + }) + self.assertEqual(out, '') + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_configure.py b/python/mozbuild/mozbuild/test/configure/test_configure.py new file mode 100644 index 000000000..df97ba70d --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_configure.py @@ -0,0 +1,1273 @@ +# 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 sys +import textwrap +import unittest + +from mozunit import ( + main, + MockedOpen, +) + +from mozbuild.configure.options import ( + InvalidOptionError, + NegativeOptionValue, + PositiveOptionValue, +) +from mozbuild.configure import ( + ConfigureError, + ConfigureSandbox, +) +from mozbuild.util import exec_ + +import mozpack.path as mozpath + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data') + + +class TestConfigure(unittest.TestCase): + def get_config(self, options=[], env={}, configure='moz.configure', + prog='/bin/configure'): + config = {} + out = StringIO() + sandbox = ConfigureSandbox(config, env, [prog] + options, out, out) + + sandbox.run(mozpath.join(test_data_path, configure)) + + if '--help' in options: + return out.getvalue(), config + self.assertEquals('', out.getvalue()) + return config + + def moz_configure(self, source): + return MockedOpen({ + os.path.join(test_data_path, + 'moz.configure'): textwrap.dedent(source) + }) + + def test_defaults(self): + config = self.get_config() + self.maxDiff = None + self.assertEquals({ + 'CHOICES': NegativeOptionValue(), + 'DEFAULTED': PositiveOptionValue(('not-simple',)), + 'IS_GCC': NegativeOptionValue(), + 'REMAINDER': (PositiveOptionValue(), NegativeOptionValue(), + NegativeOptionValue(), NegativeOptionValue()), + 'SIMPLE': NegativeOptionValue(), + 'VALUES': NegativeOptionValue(), + 'VALUES2': NegativeOptionValue(), + 'VALUES3': NegativeOptionValue(), + 'WITH_ENV': NegativeOptionValue(), + }, config) + + def test_help(self): + help, config = self.get_config(['--help'], prog='configure') + + self.assertEquals({}, config) + self.maxDiff = None + self.assertEquals( + 'Usage: configure [options]\n' + '\n' + 'Options: [defaults in brackets after descriptions]\n' + ' --help print this message\n' + ' --enable-simple Enable simple\n' + ' --enable-with-env Enable with env\n' + ' --enable-values Enable values\n' + ' --without-thing Build without thing\n' + ' --with-stuff Build with stuff\n' + ' --option Option\n' + ' --with-returned-default Returned default [not-simple]\n' + ' --returned-choices Choices\n' + ' --enable-imports-in-template\n' + ' Imports in template\n' + ' --enable-include Include\n' + ' --with-imports Imports\n' + '\n' + 'Environment variables:\n' + ' CC C Compiler\n', + help + ) + + def test_unknown(self): + with self.assertRaises(InvalidOptionError): + self.get_config(['--unknown']) + + def test_simple(self): + for config in ( + self.get_config(), + self.get_config(['--disable-simple']), + # Last option wins. + self.get_config(['--enable-simple', '--disable-simple']), + ): + self.assertNotIn('ENABLED_SIMPLE', config) + self.assertIn('SIMPLE', config) + self.assertEquals(NegativeOptionValue(), config['SIMPLE']) + + for config in ( + self.get_config(['--enable-simple']), + self.get_config(['--disable-simple', '--enable-simple']), + ): + self.assertIn('ENABLED_SIMPLE', config) + self.assertIn('SIMPLE', config) + self.assertEquals(PositiveOptionValue(), config['SIMPLE']) + self.assertIs(config['SIMPLE'], config['ENABLED_SIMPLE']) + + # --enable-simple doesn't take values. + with self.assertRaises(InvalidOptionError): + self.get_config(['--enable-simple=value']) + + def test_with_env(self): + for config in ( + self.get_config(), + self.get_config(['--disable-with-env']), + self.get_config(['--enable-with-env', '--disable-with-env']), + self.get_config(env={'MOZ_WITH_ENV': ''}), + # Options win over environment + self.get_config(['--disable-with-env'], + env={'MOZ_WITH_ENV': '1'}), + ): + self.assertIn('WITH_ENV', config) + self.assertEquals(NegativeOptionValue(), config['WITH_ENV']) + + for config in ( + self.get_config(['--enable-with-env']), + self.get_config(['--disable-with-env', '--enable-with-env']), + self.get_config(env={'MOZ_WITH_ENV': '1'}), + self.get_config(['--enable-with-env'], + env={'MOZ_WITH_ENV': ''}), + ): + self.assertIn('WITH_ENV', config) + self.assertEquals(PositiveOptionValue(), config['WITH_ENV']) + + with self.assertRaises(InvalidOptionError): + self.get_config(['--enable-with-env=value']) + + with self.assertRaises(InvalidOptionError): + self.get_config(env={'MOZ_WITH_ENV': 'value'}) + + def test_values(self, name='VALUES'): + for config in ( + self.get_config(), + self.get_config(['--disable-values']), + self.get_config(['--enable-values', '--disable-values']), + ): + self.assertIn(name, config) + self.assertEquals(NegativeOptionValue(), config[name]) + + for config in ( + self.get_config(['--enable-values']), + self.get_config(['--disable-values', '--enable-values']), + ): + self.assertIn(name, config) + self.assertEquals(PositiveOptionValue(), config[name]) + + config = self.get_config(['--enable-values=foo']) + self.assertIn(name, config) + self.assertEquals(PositiveOptionValue(('foo',)), config[name]) + + config = self.get_config(['--enable-values=foo,bar']) + self.assertIn(name, config) + self.assertTrue(config[name]) + self.assertEquals(PositiveOptionValue(('foo', 'bar')), config[name]) + + def test_values2(self): + self.test_values('VALUES2') + + def test_values3(self): + self.test_values('VALUES3') + + def test_returned_default(self): + config = self.get_config(['--enable-simple']) + self.assertIn('DEFAULTED', config) + self.assertEquals( + PositiveOptionValue(('simple',)), config['DEFAULTED']) + + config = self.get_config(['--disable-simple']) + self.assertIn('DEFAULTED', config) + self.assertEquals( + PositiveOptionValue(('not-simple',)), config['DEFAULTED']) + + def test_returned_choices(self): + for val in ('a', 'b', 'c'): + config = self.get_config( + ['--enable-values=alpha', '--returned-choices=%s' % val]) + self.assertIn('CHOICES', config) + self.assertEquals(PositiveOptionValue((val,)), config['CHOICES']) + + for val in ('0', '1', '2'): + config = self.get_config( + ['--enable-values=numeric', '--returned-choices=%s' % val]) + self.assertIn('CHOICES', config) + self.assertEquals(PositiveOptionValue((val,)), config['CHOICES']) + + with self.assertRaises(InvalidOptionError): + self.get_config(['--enable-values=numeric', + '--returned-choices=a']) + + with self.assertRaises(InvalidOptionError): + self.get_config(['--enable-values=alpha', '--returned-choices=0']) + + def test_included(self): + config = self.get_config(env={'CC': 'gcc'}) + self.assertIn('IS_GCC', config) + self.assertEquals(config['IS_GCC'], True) + + config = self.get_config( + ['--enable-include=extra.configure', '--extra']) + self.assertIn('EXTRA', config) + self.assertEquals(PositiveOptionValue(), config['EXTRA']) + + with self.assertRaises(InvalidOptionError): + self.get_config(['--extra']) + + def test_template(self): + config = self.get_config(env={'CC': 'gcc'}) + self.assertIn('CFLAGS', config) + self.assertEquals(config['CFLAGS'], ['-Werror=foobar']) + + config = self.get_config(env={'CC': 'clang'}) + self.assertNotIn('CFLAGS', config) + + def test_imports(self): + config = {} + out = StringIO() + sandbox = ConfigureSandbox(config, {}, [], out, out) + + with self.assertRaises(ImportError): + exec_(textwrap.dedent(''' + @template + def foo(): + import sys + foo()'''), + sandbox + ) + + exec_(textwrap.dedent(''' + @template + @imports('sys') + def foo(): + return sys'''), + sandbox + ) + + self.assertIs(sandbox['foo'](), sys) + + exec_(textwrap.dedent(''' + @template + @imports(_from='os', _import='path') + def foo(): + return path'''), + sandbox + ) + + self.assertIs(sandbox['foo'](), os.path) + + exec_(textwrap.dedent(''' + @template + @imports(_from='os', _import='path', _as='os_path') + def foo(): + return os_path'''), + sandbox + ) + + self.assertIs(sandbox['foo'](), os.path) + + exec_(textwrap.dedent(''' + @template + @imports('__builtin__') + def foo(): + return __builtin__'''), + sandbox + ) + + import __builtin__ + self.assertIs(sandbox['foo'](), __builtin__) + + exec_(textwrap.dedent(''' + @template + @imports(_from='__builtin__', _import='open') + def foo(): + return open('%s')''' % os.devnull), + sandbox + ) + + f = sandbox['foo']() + self.assertEquals(f.name, os.devnull) + f.close() + + # This unlocks the sandbox + exec_(textwrap.dedent(''' + @template + @imports(_import='__builtin__', _as='__builtins__') + def foo(): + import sys + return sys'''), + sandbox + ) + + self.assertIs(sandbox['foo'](), sys) + + exec_(textwrap.dedent(''' + @template + @imports('__sandbox__') + def foo(): + return __sandbox__'''), + sandbox + ) + + self.assertIs(sandbox['foo'](), sandbox) + + exec_(textwrap.dedent(''' + @template + @imports(_import='__sandbox__', _as='s') + def foo(): + return s'''), + sandbox + ) + + self.assertIs(sandbox['foo'](), sandbox) + + # Nothing leaked from the function being executed + self.assertEquals(sandbox.keys(), ['__builtins__', 'foo']) + self.assertEquals(sandbox['__builtins__'], ConfigureSandbox.BUILTINS) + + exec_(textwrap.dedent(''' + @template + @imports('sys') + def foo(): + @depends(when=True) + def bar(): + return sys + return bar + bar = foo()'''), + sandbox + ) + + with self.assertRaises(NameError) as e: + sandbox._depends[sandbox['bar']].result + + self.assertEquals(e.exception.message, + "global name 'sys' is not defined") + + def test_apply_imports(self): + imports = [] + + class CountApplyImportsSandbox(ConfigureSandbox): + def _apply_imports(self, *args, **kwargs): + imports.append((args, kwargs)) + super(CountApplyImportsSandbox, self)._apply_imports( + *args, **kwargs) + + config = {} + out = StringIO() + sandbox = CountApplyImportsSandbox(config, {}, [], out, out) + + exec_(textwrap.dedent(''' + @template + @imports('sys') + def foo(): + return sys + foo() + foo()'''), + sandbox + ) + + self.assertEquals(len(imports), 1) + + def test_os_path(self): + config = self.get_config(['--with-imports=%s' % __file__]) + self.assertIn('HAS_ABSPATH', config) + self.assertEquals(config['HAS_ABSPATH'], True) + self.assertIn('HAS_GETATIME', config) + self.assertEquals(config['HAS_GETATIME'], True) + self.assertIn('HAS_GETATIME2', config) + self.assertEquals(config['HAS_GETATIME2'], False) + + def test_template_call(self): + config = self.get_config(env={'CC': 'gcc'}) + self.assertIn('TEMPLATE_VALUE', config) + self.assertEquals(config['TEMPLATE_VALUE'], 42) + self.assertIn('TEMPLATE_VALUE_2', config) + self.assertEquals(config['TEMPLATE_VALUE_2'], 21) + + def test_template_imports(self): + config = self.get_config(['--enable-imports-in-template']) + self.assertIn('PLATFORM', config) + self.assertEquals(config['PLATFORM'], sys.platform) + + def test_decorators(self): + config = {} + out = StringIO() + sandbox = ConfigureSandbox(config, {}, [], out, out) + + sandbox.include_file(mozpath.join(test_data_path, 'decorators.configure')) + + self.assertNotIn('FOO', sandbox) + self.assertNotIn('BAR', sandbox) + self.assertNotIn('QUX', sandbox) + + def test_set_config(self): + def get_config(*args): + return self.get_config(*args, configure='set_config.configure') + + help, config = get_config(['--help']) + self.assertEquals(config, {}) + + config = get_config(['--set-foo']) + self.assertIn('FOO', config) + self.assertEquals(config['FOO'], True) + + config = get_config(['--set-bar']) + self.assertNotIn('FOO', config) + self.assertIn('BAR', config) + self.assertEquals(config['BAR'], True) + + config = get_config(['--set-value=qux']) + self.assertIn('VALUE', config) + self.assertEquals(config['VALUE'], 'qux') + + config = get_config(['--set-name=hoge']) + self.assertIn('hoge', config) + self.assertEquals(config['hoge'], True) + + config = get_config([]) + self.assertEquals(config, {'BAR': False}) + + with self.assertRaises(ConfigureError): + # Both --set-foo and --set-name=FOO are going to try to + # set_config('FOO'...) + get_config(['--set-foo', '--set-name=FOO']) + + def test_set_config_when(self): + with self.moz_configure(''' + option('--with-qux', help='qux') + set_config('FOO', 'foo', when=True) + set_config('BAR', 'bar', when=False) + set_config('QUX', 'qux', when='--with-qux') + '''): + config = self.get_config() + self.assertEquals(config, { + 'FOO': 'foo', + }) + config = self.get_config(['--with-qux']) + self.assertEquals(config, { + 'FOO': 'foo', + 'QUX': 'qux', + }) + + def test_set_define(self): + def get_config(*args): + return self.get_config(*args, configure='set_define.configure') + + help, config = get_config(['--help']) + self.assertEquals(config, {'DEFINES': {}}) + + config = get_config(['--set-foo']) + self.assertIn('FOO', config['DEFINES']) + self.assertEquals(config['DEFINES']['FOO'], True) + + config = get_config(['--set-bar']) + self.assertNotIn('FOO', config['DEFINES']) + self.assertIn('BAR', config['DEFINES']) + self.assertEquals(config['DEFINES']['BAR'], True) + + config = get_config(['--set-value=qux']) + self.assertIn('VALUE', config['DEFINES']) + self.assertEquals(config['DEFINES']['VALUE'], 'qux') + + config = get_config(['--set-name=hoge']) + self.assertIn('hoge', config['DEFINES']) + self.assertEquals(config['DEFINES']['hoge'], True) + + config = get_config([]) + self.assertEquals(config['DEFINES'], {'BAR': False}) + + with self.assertRaises(ConfigureError): + # Both --set-foo and --set-name=FOO are going to try to + # set_define('FOO'...) + get_config(['--set-foo', '--set-name=FOO']) + + def test_set_define_when(self): + with self.moz_configure(''' + option('--with-qux', help='qux') + set_define('FOO', 'foo', when=True) + set_define('BAR', 'bar', when=False) + set_define('QUX', 'qux', when='--with-qux') + '''): + config = self.get_config() + self.assertEquals(config['DEFINES'], { + 'FOO': 'foo', + }) + config = self.get_config(['--with-qux']) + self.assertEquals(config['DEFINES'], { + 'FOO': 'foo', + 'QUX': 'qux', + }) + + def test_imply_option_simple(self): + def get_config(*args): + return self.get_config( + *args, configure='imply_option/simple.configure') + + help, config = get_config(['--help']) + self.assertEquals(config, {}) + + config = get_config([]) + self.assertEquals(config, {}) + + config = get_config(['--enable-foo']) + self.assertIn('BAR', config) + self.assertEquals(config['BAR'], PositiveOptionValue()) + + with self.assertRaises(InvalidOptionError) as e: + get_config(['--enable-foo', '--disable-bar']) + + self.assertEquals( + e.exception.message, + "'--enable-bar' implied by '--enable-foo' conflicts with " + "'--disable-bar' from the command-line") + + def test_imply_option_negative(self): + def get_config(*args): + return self.get_config( + *args, configure='imply_option/negative.configure') + + help, config = get_config(['--help']) + self.assertEquals(config, {}) + + config = get_config([]) + self.assertEquals(config, {}) + + config = get_config(['--enable-foo']) + self.assertIn('BAR', config) + self.assertEquals(config['BAR'], NegativeOptionValue()) + + with self.assertRaises(InvalidOptionError) as e: + get_config(['--enable-foo', '--enable-bar']) + + self.assertEquals( + e.exception.message, + "'--disable-bar' implied by '--enable-foo' conflicts with " + "'--enable-bar' from the command-line") + + config = get_config(['--disable-hoge']) + self.assertIn('BAR', config) + self.assertEquals(config['BAR'], NegativeOptionValue()) + + with self.assertRaises(InvalidOptionError) as e: + get_config(['--disable-hoge', '--enable-bar']) + + self.assertEquals( + e.exception.message, + "'--disable-bar' implied by '--disable-hoge' conflicts with " + "'--enable-bar' from the command-line") + + def test_imply_option_values(self): + def get_config(*args): + return self.get_config( + *args, configure='imply_option/values.configure') + + help, config = get_config(['--help']) + self.assertEquals(config, {}) + + config = get_config([]) + self.assertEquals(config, {}) + + config = get_config(['--enable-foo=a']) + self.assertIn('BAR', config) + self.assertEquals(config['BAR'], PositiveOptionValue(('a',))) + + config = get_config(['--enable-foo=a,b']) + self.assertIn('BAR', config) + self.assertEquals(config['BAR'], PositiveOptionValue(('a','b'))) + + with self.assertRaises(InvalidOptionError) as e: + get_config(['--enable-foo=a,b', '--disable-bar']) + + self.assertEquals( + e.exception.message, + "'--enable-bar=a,b' implied by '--enable-foo' conflicts with " + "'--disable-bar' from the command-line") + + def test_imply_option_infer(self): + def get_config(*args): + return self.get_config( + *args, configure='imply_option/infer.configure') + + help, config = get_config(['--help']) + self.assertEquals(config, {}) + + config = get_config([]) + self.assertEquals(config, {}) + + with self.assertRaises(InvalidOptionError) as e: + get_config(['--enable-foo', '--disable-bar']) + + self.assertEquals( + e.exception.message, + "'--enable-bar' implied by '--enable-foo' conflicts with " + "'--disable-bar' from the command-line") + + with self.assertRaises(ConfigureError) as e: + self.get_config([], configure='imply_option/infer_ko.configure') + + self.assertEquals( + e.exception.message, + "Cannot infer what implies '--enable-bar'. Please add a `reason` " + "to the `imply_option` call.") + + def test_imply_option_immediate_value(self): + def get_config(*args): + return self.get_config( + *args, configure='imply_option/imm.configure') + + help, config = get_config(['--help']) + self.assertEquals(config, {}) + + config = get_config([]) + self.assertEquals(config, {}) + + config_path = mozpath.abspath( + mozpath.join(test_data_path, 'imply_option', 'imm.configure')) + + with self.assertRaisesRegexp(InvalidOptionError, + "--enable-foo' implied by 'imply_option at %s:7' conflicts with " + "'--disable-foo' from the command-line" % config_path): + get_config(['--disable-foo']) + + with self.assertRaisesRegexp(InvalidOptionError, + "--enable-bar=foo,bar' implied by 'imply_option at %s:16' conflicts" + " with '--enable-bar=a,b,c' from the command-line" % config_path): + get_config(['--enable-bar=a,b,c']) + + with self.assertRaisesRegexp(InvalidOptionError, + "--enable-baz=BAZ' implied by 'imply_option at %s:25' conflicts" + " with '--enable-baz=QUUX' from the command-line" % config_path): + get_config(['--enable-baz=QUUX']) + + def test_imply_option_failures(self): + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + imply_option('--with-foo', ('a',), 'bar') + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "`--with-foo`, emitted from `%s` line 2, is unknown." + % mozpath.join(test_data_path, 'moz.configure')) + + with self.assertRaises(TypeError) as e: + with self.moz_configure(''' + imply_option('--with-foo', 42, 'bar') + + option('--with-foo', help='foo') + @depends('--with-foo') + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "Unexpected type: 'int'") + + def test_imply_option_when(self): + with self.moz_configure(''' + option('--with-foo', help='foo') + imply_option('--with-qux', True, when='--with-foo') + option('--with-qux', help='qux') + set_config('QUX', depends('--with-qux')(lambda x: x)) + '''): + config = self.get_config() + self.assertEquals(config, { + 'QUX': NegativeOptionValue(), + }) + + config = self.get_config(['--with-foo']) + self.assertEquals(config, { + 'QUX': PositiveOptionValue(), + }) + + def test_option_failures(self): + with self.assertRaises(ConfigureError) as e: + with self.moz_configure('option("--with-foo", help="foo")'): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Option `--with-foo` is not handled ; reference it with a @depends' + ) + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option("--with-foo", help="foo") + option("--with-foo", help="foo") + '''): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Option `--with-foo` already defined' + ) + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option(env="MOZ_FOO", help="foo") + option(env="MOZ_FOO", help="foo") + '''): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Option `MOZ_FOO` already defined' + ) + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option('--with-foo', env="MOZ_FOO", help="foo") + option(env="MOZ_FOO", help="foo") + '''): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Option `MOZ_FOO` already defined' + ) + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option(env="MOZ_FOO", help="foo") + option('--with-foo', env="MOZ_FOO", help="foo") + '''): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Option `MOZ_FOO` already defined' + ) + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option('--with-foo', env="MOZ_FOO", help="foo") + option('--with-foo', help="foo") + '''): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Option `--with-foo` already defined' + ) + + def test_option_when(self): + with self.moz_configure(''' + option('--with-foo', help='foo', when=True) + option('--with-bar', help='bar', when=False) + option('--with-qux', env="QUX", help='qux', when='--with-foo') + + set_config('FOO', depends('--with-foo', when=True)(lambda x: x)) + set_config('BAR', depends('--with-bar', when=False)(lambda x: x)) + set_config('QUX', depends('--with-qux', when='--with-foo')(lambda x: x)) + '''): + config = self.get_config() + self.assertEquals(config, { + 'FOO': NegativeOptionValue(), + }) + + config = self.get_config(['--with-foo']) + self.assertEquals(config, { + 'FOO': PositiveOptionValue(), + 'QUX': NegativeOptionValue(), + }) + + config = self.get_config(['--with-foo', '--with-qux']) + self.assertEquals(config, { + 'FOO': PositiveOptionValue(), + 'QUX': PositiveOptionValue(), + }) + + with self.assertRaises(InvalidOptionError) as e: + self.get_config(['--with-bar']) + + self.assertEquals( + e.exception.message, + '--with-bar is not available in this configuration' + ) + + with self.assertRaises(InvalidOptionError) as e: + self.get_config(['--with-qux']) + + self.assertEquals( + e.exception.message, + '--with-qux is not available in this configuration' + ) + + with self.assertRaises(InvalidOptionError) as e: + self.get_config(['QUX=1']) + + self.assertEquals( + e.exception.message, + 'QUX is not available in this configuration' + ) + + config = self.get_config(env={'QUX': '1'}) + self.assertEquals(config, { + 'FOO': NegativeOptionValue(), + }) + + help, config = self.get_config(['--help']) + self.assertEquals(help, textwrap.dedent('''\ + Usage: configure [options] + + Options: [defaults in brackets after descriptions] + --help print this message + --with-foo foo + + Environment variables: + ''')) + + help, config = self.get_config(['--help', '--with-foo']) + self.assertEquals(help, textwrap.dedent('''\ + Usage: configure [options] + + Options: [defaults in brackets after descriptions] + --help print this message + --with-foo foo + --with-qux qux + + Environment variables: + ''')) + + with self.moz_configure(''' + option('--with-foo', help='foo', when=True) + set_config('FOO', depends('--with-foo')(lambda x: x)) + '''): + with self.assertRaises(ConfigureError) as e: + self.get_config() + + self.assertEquals(e.exception.message, + '@depends function needs the same `when` as ' + 'options it depends on') + + with self.moz_configure(''' + @depends(when=True) + def always(): + return True + @depends(when=True) + def always2(): + return True + option('--with-foo', help='foo', when=always) + set_config('FOO', depends('--with-foo', when=always2)(lambda x: x)) + '''): + with self.assertRaises(ConfigureError) as e: + self.get_config() + + self.assertEquals(e.exception.message, + '@depends function needs the same `when` as ' + 'options it depends on') + + def test_include_failures(self): + with self.assertRaises(ConfigureError) as e: + with self.moz_configure('include("../foo.configure")'): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Cannot include `%s` because it is not in a subdirectory of `%s`' + % (mozpath.normpath(mozpath.join(test_data_path, '..', + 'foo.configure')), + mozpath.normsep(test_data_path)) + ) + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + include('extra.configure') + include('extra.configure') + '''): + self.get_config() + + self.assertEquals( + e.exception.message, + 'Cannot include `%s` because it was included already.' + % mozpath.normpath(mozpath.join(test_data_path, + 'extra.configure')) + ) + + with self.assertRaises(TypeError) as e: + with self.moz_configure(''' + include(42) + '''): + self.get_config() + + self.assertEquals(e.exception.message, "Unexpected type: 'int'") + + def test_include_when(self): + with MockedOpen({ + os.path.join(test_data_path, 'moz.configure'): textwrap.dedent(''' + option('--with-foo', help='foo') + + include('always.configure', when=True) + include('never.configure', when=False) + include('foo.configure', when='--with-foo') + + set_config('FOO', foo) + set_config('BAR', bar) + set_config('QUX', qux) + '''), + os.path.join(test_data_path, 'always.configure'): textwrap.dedent(''' + option('--with-bar', help='bar') + @depends('--with-bar') + def bar(x): + if x: + return 'bar' + '''), + os.path.join(test_data_path, 'never.configure'): textwrap.dedent(''' + option('--with-qux', help='qux') + @depends('--with-qux') + def qux(x): + if x: + return 'qux' + '''), + os.path.join(test_data_path, 'foo.configure'): textwrap.dedent(''' + option('--with-foo-really', help='really foo') + @depends('--with-foo-really') + def foo(x): + if x: + return 'foo' + + include('foo2.configure', when='--with-foo-really') + '''), + os.path.join(test_data_path, 'foo2.configure'): textwrap.dedent(''' + set_config('FOO2', True) + '''), + }): + config = self.get_config() + self.assertEquals(config, {}) + + config = self.get_config(['--with-foo']) + self.assertEquals(config, {}) + + config = self.get_config(['--with-bar']) + self.assertEquals(config, { + 'BAR': 'bar', + }) + + with self.assertRaises(InvalidOptionError) as e: + self.get_config(['--with-qux']) + + self.assertEquals( + e.exception.message, + '--with-qux is not available in this configuration' + ) + + config = self.get_config(['--with-foo', '--with-foo-really']) + self.assertEquals(config, { + 'FOO': 'foo', + 'FOO2': True, + }) + + def test_sandbox_failures(self): + with self.assertRaises(KeyError) as e: + with self.moz_configure(''' + include = 42 + '''): + self.get_config() + + self.assertEquals(e.exception.message, 'Cannot reassign builtins') + + with self.assertRaises(KeyError) as e: + with self.moz_configure(''' + foo = 42 + '''): + self.get_config() + + self.assertEquals(e.exception.message, + 'Cannot assign `foo` because it is neither a ' + '@depends nor a @template') + + def test_depends_failures(self): + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + @depends() + def foo(): + return + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "@depends needs at least one argument") + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + @depends('--with-foo') + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "'--with-foo' is not a known option. Maybe it's " + "declared too late?") + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + @depends('--with-foo=42') + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "Option must not contain an '='") + + with self.assertRaises(TypeError) as e: + with self.moz_configure(''' + @depends(42) + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "Cannot use object of type 'int' as argument " + "to @depends") + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + @depends('--help') + def foo(value): + yield + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "Cannot decorate generator functions with @depends") + + with self.assertRaises(TypeError) as e: + with self.moz_configure(''' + depends('--help')(42) + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "Unexpected type: 'int'") + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option('--foo', help='foo') + @depends('--foo') + def foo(value): + return value + + foo() + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "The `foo` function may not be called") + + with self.assertRaises(TypeError) as e: + with self.moz_configure(''' + @depends('--help', foo=42) + def foo(_): + return + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "depends_impl() got an unexpected keyword argument 'foo'") + + def test_depends_when(self): + with self.moz_configure(''' + @depends(when=True) + def foo(): + return 'foo' + + set_config('FOO', foo) + + @depends(when=False) + def bar(): + return 'bar' + + set_config('BAR', bar) + + option('--with-qux', help='qux') + @depends(when='--with-qux') + def qux(): + return 'qux' + + set_config('QUX', qux) + '''): + config = self.get_config() + self.assertEquals(config, { + 'FOO': 'foo', + }) + + config = self.get_config(['--with-qux']) + self.assertEquals(config, { + 'FOO': 'foo', + 'QUX': 'qux', + }) + + def test_imports_failures(self): + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + @imports('os') + @template + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, + '@imports must appear after @template') + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option('--foo', help='foo') + @imports('os') + @depends('--foo') + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, + '@imports must appear after @depends') + + for import_ in ( + "42", + "_from=42, _import='os'", + "_from='os', _import='path', _as=42", + ): + with self.assertRaises(TypeError) as e: + with self.moz_configure(''' + @imports(%s) + @template + def foo(value): + return value + ''' % import_): + self.get_config() + + self.assertEquals(e.exception.message, "Unexpected type: 'int'") + + with self.assertRaises(TypeError) as e: + with self.moz_configure(''' + @imports('os', 42) + @template + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, "Unexpected type: 'int'") + + with self.assertRaises(ValueError) as e: + with self.moz_configure(''' + @imports('os*') + def foo(value): + return value + '''): + self.get_config() + + self.assertEquals(e.exception.message, + "Invalid argument to @imports: 'os*'") + + def test_only_when(self): + moz_configure = ''' + option('--enable-when', help='when') + @depends('--enable-when', '--help') + def when(value, _): + return bool(value) + + with only_when(when): + option('--foo', nargs='*', help='foo') + @depends('--foo') + def foo(value): + return value + + set_config('FOO', foo) + set_define('FOO', foo) + + # It is possible to depend on a function defined in a only_when + # block. It then resolves to `None`. + set_config('BAR', depends(foo)(lambda x: x)) + set_define('BAR', depends(foo)(lambda x: x)) + ''' + + with self.moz_configure(moz_configure): + config = self.get_config() + self.assertEqual(config, { + 'DEFINES': {}, + }) + + config = self.get_config(['--enable-when']) + self.assertEqual(config, { + 'BAR': NegativeOptionValue(), + 'FOO': NegativeOptionValue(), + 'DEFINES': { + 'BAR': NegativeOptionValue(), + 'FOO': NegativeOptionValue(), + }, + }) + + config = self.get_config(['--enable-when', '--foo=bar']) + self.assertEqual(config, { + 'BAR': PositiveOptionValue(['bar']), + 'FOO': PositiveOptionValue(['bar']), + 'DEFINES': { + 'BAR': PositiveOptionValue(['bar']), + 'FOO': PositiveOptionValue(['bar']), + }, + }) + + # The --foo option doesn't exist when --enable-when is not given. + with self.assertRaises(InvalidOptionError) as e: + self.get_config(['--foo']) + + self.assertEquals(e.exception.message, + '--foo is not available in this configuration') + + # Cannot depend on an option defined in a only_when block, because we + # don't know what OptionValue would make sense. + with self.moz_configure(moz_configure + ''' + set_config('QUX', depends('--foo')(lambda x: x)) + '''): + with self.assertRaises(ConfigureError) as e: + self.get_config() + + self.assertEquals(e.exception.message, + '@depends function needs the same `when` as ' + 'options it depends on') + + with self.moz_configure(moz_configure + ''' + set_config('QUX', depends('--foo', when=when)(lambda x: x)) + '''): + self.get_config(['--enable-when']) + + # Using imply_option for an option defined in a only_when block fails + # similarly if the imply_option happens outside the block. + with self.moz_configure(''' + imply_option('--foo', True) + ''' + moz_configure): + with self.assertRaises(InvalidOptionError) as e: + self.get_config() + + self.assertEquals(e.exception.message, + '--foo is not available in this configuration') + + # And similarly doesn't fail when the condition is true. + with self.moz_configure(''' + imply_option('--foo', True) + ''' + moz_configure): + self.get_config(['--enable-when']) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_lint.py b/python/mozbuild/mozbuild/test/configure/test_lint.py new file mode 100644 index 000000000..6ac2bb356 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_lint.py @@ -0,0 +1,132 @@ +# 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 textwrap +import unittest + +from mozunit import ( + main, + MockedOpen, +) + +from mozbuild.configure import ConfigureError +from mozbuild.configure.lint import LintSandbox + +import mozpack.path as mozpath + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data') + + +class TestLint(unittest.TestCase): + def lint_test(self, options=[], env={}): + sandbox = LintSandbox(env, ['configure'] + options) + + sandbox.run(mozpath.join(test_data_path, 'moz.configure')) + + def moz_configure(self, source): + return MockedOpen({ + os.path.join(test_data_path, + 'moz.configure'): textwrap.dedent(source) + }) + + def test_depends_failures(self): + with self.moz_configure(''' + option('--foo', help='foo') + @depends('--foo') + def foo(value): + return value + + @depends('--help', foo) + def bar(help, foo): + return + '''): + self.lint_test() + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option('--foo', help='foo') + @depends('--foo') + @imports('os') + def foo(value): + return value + + @depends('--help', foo) + def bar(help, foo): + return + '''): + self.lint_test() + + self.assertEquals(e.exception.message, + "`bar` depends on '--help' and `foo`. " + "`foo` must depend on '--help'") + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + @template + def tmpl(): + qux = 42 + + option('--foo', help='foo') + @depends('--foo') + def foo(value): + qux + return value + + @depends('--help', foo) + def bar(help, foo): + return + tmpl() + '''): + self.lint_test() + + self.assertEquals(e.exception.message, + "`bar` depends on '--help' and `foo`. " + "`foo` must depend on '--help'") + + with self.moz_configure(''' + option('--foo', help='foo') + @depends('--foo') + def foo(value): + return value + + include(foo) + '''): + self.lint_test() + + with self.assertRaises(ConfigureError) as e: + with self.moz_configure(''' + option('--foo', help='foo') + @depends('--foo') + @imports('os') + def foo(value): + return value + + include(foo) + '''): + self.lint_test() + + self.assertEquals(e.exception.message, + "Missing @depends for `foo`: '--help'") + + # There is a default restricted `os` module when there is no explicit + # @imports, and it's fine to use it without a dependency on --help. + with self.moz_configure(''' + option('--foo', help='foo') + @depends('--foo') + def foo(value): + os + return value + + include(foo) + '''): + self.lint_test() + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_moz_configure.py b/python/mozbuild/mozbuild/test/configure/test_moz_configure.py new file mode 100644 index 000000000..7c318adef --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_moz_configure.py @@ -0,0 +1,93 @@ +# 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 mozunit import main +from mozpack import path as mozpath + +from common import BaseConfigureTest + + +class TestMozConfigure(BaseConfigureTest): + def test_moz_configure_options(self): + def get_value_for(args=[], environ={}, mozconfig=''): + sandbox = self.get_sandbox({}, {}, args, environ, mozconfig) + + # Add a fake old-configure option + sandbox.option_impl('--with-foo', nargs='*', + help='Help missing for old configure options') + + result = sandbox._value_for(sandbox['all_configure_options']) + shell = mozpath.abspath('/bin/sh') + return result.replace('CONFIG_SHELL=%s ' % shell, '') + + self.assertEquals('--enable-application=browser', + get_value_for(['--enable-application=browser'])) + + self.assertEquals('--enable-application=browser ' + 'MOZ_PROFILING=1', + get_value_for(['--enable-application=browser', + 'MOZ_PROFILING=1'])) + + value = get_value_for( + environ={'MOZ_PROFILING': '1'}, + mozconfig='ac_add_options --enable-project=js') + + self.assertEquals('--enable-project=js MOZ_PROFILING=1', + value) + + # --disable-js-shell is the default, so it's filtered out. + self.assertEquals('--enable-application=browser', + get_value_for(['--enable-application=browser', + '--disable-js-shell'])) + + # Normally, --without-foo would be filtered out because that's the + # default, but since it is a (fake) old-configure option, it always + # appears. + self.assertEquals('--enable-application=browser --without-foo', + get_value_for(['--enable-application=browser', + '--without-foo'])) + self.assertEquals('--enable-application=browser --with-foo', + get_value_for(['--enable-application=browser', + '--with-foo'])) + + self.assertEquals("--enable-application=browser '--with-foo=foo bar'", + get_value_for(['--enable-application=browser', + '--with-foo=foo bar'])) + + def test_nsis_version(self): + this = self + + class FakeNSIS(object): + def __init__(self, version): + self.version = version + + def __call__(self, stdin, args): + this.assertEquals(args, ('-version',)) + return 0, self.version, '' + + def check_nsis_version(version): + sandbox = self.get_sandbox( + {'/usr/bin/makensis': FakeNSIS(version)}, {}, [], + {'PATH': '/usr/bin', 'MAKENSISU': '/usr/bin/makensis'}) + return sandbox._value_for(sandbox['nsis_version']) + + with self.assertRaises(SystemExit) as e: + check_nsis_version('v2.5') + + with self.assertRaises(SystemExit) as e: + check_nsis_version('v3.0a2') + + self.assertEquals(check_nsis_version('v3.0b1'), '3.0b1') + self.assertEquals(check_nsis_version('v3.0b2'), '3.0b2') + self.assertEquals(check_nsis_version('v3.0rc1'), '3.0rc1') + self.assertEquals(check_nsis_version('v3.0'), '3.0') + self.assertEquals(check_nsis_version('v3.0-2'), '3.0') + self.assertEquals(check_nsis_version('v3.0.1'), '3.0') + self.assertEquals(check_nsis_version('v3.1'), '3.1') + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_options.py b/python/mozbuild/mozbuild/test/configure/test_options.py new file mode 100644 index 000000000..e504f9e05 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_options.py @@ -0,0 +1,852 @@ +# 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 + +import unittest + +from mozunit import main + +from mozbuild.configure.options import ( + CommandLineHelper, + ConflictingOptionError, + InvalidOptionError, + NegativeOptionValue, + Option, + PositiveOptionValue, +) + + +class Option(Option): + def __init__(self, *args, **kwargs): + kwargs['help'] = 'Dummy help' + super(Option, self).__init__(*args, **kwargs) + + +class TestOption(unittest.TestCase): + def test_option(self): + option = Option('--option') + self.assertEquals(option.prefix, '') + self.assertEquals(option.name, 'option') + self.assertEquals(option.env, None) + self.assertFalse(option.default) + + option = Option('--enable-option') + self.assertEquals(option.prefix, 'enable') + self.assertEquals(option.name, 'option') + self.assertEquals(option.env, None) + self.assertFalse(option.default) + + option = Option('--disable-option') + self.assertEquals(option.prefix, 'disable') + self.assertEquals(option.name, 'option') + self.assertEquals(option.env, None) + self.assertTrue(option.default) + + option = Option('--with-option') + self.assertEquals(option.prefix, 'with') + self.assertEquals(option.name, 'option') + self.assertEquals(option.env, None) + self.assertFalse(option.default) + + option = Option('--without-option') + self.assertEquals(option.prefix, 'without') + self.assertEquals(option.name, 'option') + self.assertEquals(option.env, None) + self.assertTrue(option.default) + + option = Option('--without-option-foo', env='MOZ_OPTION') + self.assertEquals(option.env, 'MOZ_OPTION') + + option = Option(env='MOZ_OPTION') + self.assertEquals(option.prefix, '') + self.assertEquals(option.name, None) + self.assertEquals(option.env, 'MOZ_OPTION') + self.assertFalse(option.default) + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=0, default=('a',)) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=1, default=()) + self.assertEquals( + e.exception.message, + 'default must be a bool, a string or a tuple of strings') + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=1, default=True) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=1, default=('a', 'b')) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=2, default=()) + self.assertEquals( + e.exception.message, + 'default must be a bool, a string or a tuple of strings') + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=2, default=True) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=2, default=('a',)) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs='?', default=('a', 'b')) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs='+', default=()) + self.assertEquals( + e.exception.message, + 'default must be a bool, a string or a tuple of strings') + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs='+', default=True) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + # --disable options with a nargs value that requires at least one + # argument need to be given a default. + with self.assertRaises(InvalidOptionError) as e: + Option('--disable-option', nargs=1) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option('--disable-option', nargs='+') + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + # Test nargs inference from default value + option = Option('--with-foo', default=True) + self.assertEquals(option.nargs, 0) + + option = Option('--with-foo', default=False) + self.assertEquals(option.nargs, 0) + + option = Option('--with-foo', default='a') + self.assertEquals(option.nargs, '?') + + option = Option('--with-foo', default=('a',)) + self.assertEquals(option.nargs, '?') + + option = Option('--with-foo', default=('a', 'b')) + self.assertEquals(option.nargs, '*') + + option = Option(env='FOO', default=True) + self.assertEquals(option.nargs, 0) + + option = Option(env='FOO', default=False) + self.assertEquals(option.nargs, 0) + + option = Option(env='FOO', default='a') + self.assertEquals(option.nargs, '?') + + option = Option(env='FOO', default=('a',)) + self.assertEquals(option.nargs, '?') + + option = Option(env='FOO', default=('a', 'b')) + self.assertEquals(option.nargs, '*') + + def test_option_option(self): + for option in ( + '--option', + '--enable-option', + '--disable-option', + '--with-option', + '--without-option', + ): + self.assertEquals(Option(option).option, option) + self.assertEquals(Option(option, env='FOO').option, option) + + opt = Option(option, default=False) + self.assertEquals(opt.option, + option.replace('-disable-', '-enable-') + .replace('-without-', '-with-')) + + opt = Option(option, default=True) + self.assertEquals(opt.option, + option.replace('-enable-', '-disable-') + .replace('-with-', '-without-')) + + self.assertEquals(Option(env='FOO').option, 'FOO') + + def test_option_choices(self): + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=3, choices=('a', 'b')) + self.assertEquals(e.exception.message, + 'Not enough `choices` for `nargs`') + + with self.assertRaises(InvalidOptionError) as e: + Option('--without-option', nargs=1, choices=('a', 'b')) + self.assertEquals(e.exception.message, + 'A `default` must be given along with `choices`') + + with self.assertRaises(InvalidOptionError) as e: + Option('--without-option', nargs='+', choices=('a', 'b')) + self.assertEquals(e.exception.message, + 'A `default` must be given along with `choices`') + + with self.assertRaises(InvalidOptionError) as e: + Option('--without-option', default='c', choices=('a', 'b')) + self.assertEquals(e.exception.message, + "The `default` value must be one of 'a', 'b'") + + with self.assertRaises(InvalidOptionError) as e: + Option('--without-option', default=('a', 'c',), choices=('a', 'b')) + self.assertEquals(e.exception.message, + "The `default` value must be one of 'a', 'b'") + + with self.assertRaises(InvalidOptionError) as e: + Option('--without-option', default=('c',), choices=('a', 'b')) + self.assertEquals(e.exception.message, + "The `default` value must be one of 'a', 'b'") + + option = Option('--with-option', nargs='+', choices=('a', 'b')) + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--with-option=c') + self.assertEquals(e.exception.message, "'c' is not one of 'a', 'b'") + + value = option.get_value('--with-option=b,a') + self.assertTrue(value) + self.assertEquals(PositiveOptionValue(('b', 'a')), value) + + option = Option('--without-option', nargs='*', default='a', + choices=('a', 'b')) + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--with-option=c') + self.assertEquals(e.exception.message, "'c' is not one of 'a', 'b'") + + value = option.get_value('--with-option=b,a') + self.assertTrue(value) + self.assertEquals(PositiveOptionValue(('b', 'a')), value) + + # Test nargs inference from choices + option = Option('--with-option', choices=('a', 'b')) + self.assertEqual(option.nargs, 1) + + # Test "relative" values + option = Option('--with-option', nargs='*', default=('b', 'c'), + choices=('a', 'b', 'c', 'd')) + + value = option.get_value('--with-option=+d') + self.assertEquals(PositiveOptionValue(('b', 'c', 'd')), value) + + value = option.get_value('--with-option=-b') + self.assertEquals(PositiveOptionValue(('c',)), value) + + value = option.get_value('--with-option=-b,+d') + self.assertEquals(PositiveOptionValue(('c','d')), value) + + # Adding something that is in the default is fine + value = option.get_value('--with-option=+b') + self.assertEquals(PositiveOptionValue(('b', 'c')), value) + + # Removing something that is not in the default is fine, as long as it + # is one of the choices + value = option.get_value('--with-option=-a') + self.assertEquals(PositiveOptionValue(('b', 'c')), value) + + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--with-option=-e') + self.assertEquals(e.exception.message, + "'e' is not one of 'a', 'b', 'c', 'd'") + + # Other "not a choice" errors. + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--with-option=+e') + self.assertEquals(e.exception.message, + "'e' is not one of 'a', 'b', 'c', 'd'") + + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--with-option=e') + self.assertEquals(e.exception.message, + "'e' is not one of 'a', 'b', 'c', 'd'") + + def test_option_value_format(self): + val = PositiveOptionValue() + self.assertEquals('--with-value', val.format('--with-value')) + self.assertEquals('--with-value', val.format('--without-value')) + self.assertEquals('--enable-value', val.format('--enable-value')) + self.assertEquals('--enable-value', val.format('--disable-value')) + self.assertEquals('--value', val.format('--value')) + self.assertEquals('VALUE=1', val.format('VALUE')) + + val = PositiveOptionValue(('a',)) + self.assertEquals('--with-value=a', val.format('--with-value')) + self.assertEquals('--with-value=a', val.format('--without-value')) + self.assertEquals('--enable-value=a', val.format('--enable-value')) + self.assertEquals('--enable-value=a', val.format('--disable-value')) + self.assertEquals('--value=a', val.format('--value')) + self.assertEquals('VALUE=a', val.format('VALUE')) + + val = PositiveOptionValue(('a', 'b')) + self.assertEquals('--with-value=a,b', val.format('--with-value')) + self.assertEquals('--with-value=a,b', val.format('--without-value')) + self.assertEquals('--enable-value=a,b', val.format('--enable-value')) + self.assertEquals('--enable-value=a,b', val.format('--disable-value')) + self.assertEquals('--value=a,b', val.format('--value')) + self.assertEquals('VALUE=a,b', val.format('VALUE')) + + val = NegativeOptionValue() + self.assertEquals('--without-value', val.format('--with-value')) + self.assertEquals('--without-value', val.format('--without-value')) + self.assertEquals('--disable-value', val.format('--enable-value')) + self.assertEquals('--disable-value', val.format('--disable-value')) + self.assertEquals('', val.format('--value')) + self.assertEquals('VALUE=', val.format('VALUE')) + + def test_option_value(self, name='option', nargs=0, default=None): + disabled = name.startswith(('disable-', 'without-')) + if disabled: + negOptionValue = PositiveOptionValue + posOptionValue = NegativeOptionValue + else: + posOptionValue = PositiveOptionValue + negOptionValue = NegativeOptionValue + defaultValue = (PositiveOptionValue(default) + if default else negOptionValue()) + + option = Option('--%s' % name, nargs=nargs, default=default) + + if nargs in (0, '?', '*') or disabled: + value = option.get_value('--%s' % name, 'option') + self.assertEquals(value, posOptionValue()) + self.assertEquals(value.origin, 'option') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--%s' % name) + if nargs == 1: + self.assertEquals(e.exception.message, + '--%s takes 1 value' % name) + elif nargs == '+': + self.assertEquals(e.exception.message, + '--%s takes 1 or more values' % name) + else: + self.assertEquals(e.exception.message, + '--%s takes 2 values' % name) + + value = option.get_value('') + self.assertEquals(value, defaultValue) + self.assertEquals(value.origin, 'default') + + value = option.get_value(None) + self.assertEquals(value, defaultValue) + self.assertEquals(value.origin, 'default') + + with self.assertRaises(AssertionError): + value = option.get_value('MOZ_OPTION=', 'environment') + + with self.assertRaises(AssertionError): + value = option.get_value('MOZ_OPTION=1', 'environment') + + with self.assertRaises(AssertionError): + value = option.get_value('--foo') + + if nargs in (1, '?', '*', '+') and not disabled: + value = option.get_value('--%s=' % name, 'option') + self.assertEquals(value, PositiveOptionValue(('',))) + self.assertEquals(value.origin, 'option') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--%s=' % name) + if disabled: + self.assertEquals(e.exception.message, + 'Cannot pass a value to --%s' % name) + else: + self.assertEquals(e.exception.message, + '--%s takes %d values' % (name, nargs)) + + if nargs in (1, '?', '*', '+') and not disabled: + value = option.get_value('--%s=foo' % name, 'option') + self.assertEquals(value, PositiveOptionValue(('foo',))) + self.assertEquals(value.origin, 'option') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--%s=foo' % name) + if disabled: + self.assertEquals(e.exception.message, + 'Cannot pass a value to --%s' % name) + else: + self.assertEquals(e.exception.message, + '--%s takes %d values' % (name, nargs)) + + if nargs in (2, '*', '+') and not disabled: + value = option.get_value('--%s=foo,bar' % name, 'option') + self.assertEquals(value, PositiveOptionValue(('foo', 'bar'))) + self.assertEquals(value.origin, 'option') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--%s=foo,bar' % name, 'option') + if disabled: + self.assertEquals(e.exception.message, + 'Cannot pass a value to --%s' % name) + elif nargs == '?': + self.assertEquals(e.exception.message, + '--%s takes 0 or 1 values' % name) + else: + self.assertEquals(e.exception.message, + '--%s takes %d value%s' + % (name, nargs, 's' if nargs != 1 else '')) + + option = Option('--%s' % name, env='MOZ_OPTION', nargs=nargs, + default=default) + if nargs in (0, '?', '*') or disabled: + value = option.get_value('--%s' % name, 'option') + self.assertEquals(value, posOptionValue()) + self.assertEquals(value.origin, 'option') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--%s' % name) + if disabled: + self.assertEquals(e.exception.message, + 'Cannot pass a value to --%s' % name) + elif nargs == '+': + self.assertEquals(e.exception.message, + '--%s takes 1 or more values' % name) + else: + self.assertEquals(e.exception.message, + '--%s takes %d value%s' + % (name, nargs, 's' if nargs != 1 else '')) + + value = option.get_value('') + self.assertEquals(value, defaultValue) + self.assertEquals(value.origin, 'default') + + value = option.get_value(None) + self.assertEquals(value, defaultValue) + self.assertEquals(value.origin, 'default') + + value = option.get_value('MOZ_OPTION=', 'environment') + self.assertEquals(value, NegativeOptionValue()) + self.assertEquals(value.origin, 'environment') + + if nargs in (0, '?', '*'): + value = option.get_value('MOZ_OPTION=1', 'environment') + self.assertEquals(value, PositiveOptionValue()) + self.assertEquals(value.origin, 'environment') + elif nargs in (1, '+'): + value = option.get_value('MOZ_OPTION=1', 'environment') + self.assertEquals(value, PositiveOptionValue(('1',))) + self.assertEquals(value.origin, 'environment') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('MOZ_OPTION=1', 'environment') + self.assertEquals(e.exception.message, 'MOZ_OPTION takes 2 values') + + if nargs in (1, '?', '*', '+') and not disabled: + value = option.get_value('--%s=' % name, 'option') + self.assertEquals(value, PositiveOptionValue(('',))) + self.assertEquals(value.origin, 'option') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--%s=' % name, 'option') + if disabled: + self.assertEquals(e.exception.message, + 'Cannot pass a value to --%s' % name) + else: + self.assertEquals(e.exception.message, + '--%s takes %d values' % (name, nargs)) + + with self.assertRaises(AssertionError): + value = option.get_value('--foo', 'option') + + if nargs in (1, '?', '*', '+'): + value = option.get_value('MOZ_OPTION=foo', 'environment') + self.assertEquals(value, PositiveOptionValue(('foo',))) + self.assertEquals(value.origin, 'environment') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('MOZ_OPTION=foo', 'environment') + self.assertEquals(e.exception.message, + 'MOZ_OPTION takes %d values' % nargs) + + if nargs in (2, '*', '+'): + value = option.get_value('MOZ_OPTION=foo,bar', 'environment') + self.assertEquals(value, PositiveOptionValue(('foo', 'bar'))) + self.assertEquals(value.origin, 'environment') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('MOZ_OPTION=foo,bar', 'environment') + if nargs == '?': + self.assertEquals(e.exception.message, + 'MOZ_OPTION takes 0 or 1 values') + else: + self.assertEquals(e.exception.message, + 'MOZ_OPTION takes %d value%s' + % (nargs, 's' if nargs != 1 else '')) + + if disabled: + return option + + env_option = Option(env='MOZ_OPTION', nargs=nargs, default=default) + with self.assertRaises(AssertionError): + env_option.get_value('--%s' % name) + + value = env_option.get_value('') + self.assertEquals(value, defaultValue) + self.assertEquals(value.origin, 'default') + + value = env_option.get_value('MOZ_OPTION=', 'environment') + self.assertEquals(value, negOptionValue()) + self.assertEquals(value.origin, 'environment') + + if nargs in (0, '?', '*'): + value = env_option.get_value('MOZ_OPTION=1', 'environment') + self.assertEquals(value, posOptionValue()) + self.assertTrue(value) + self.assertEquals(value.origin, 'environment') + elif nargs in (1, '+'): + value = env_option.get_value('MOZ_OPTION=1', 'environment') + self.assertEquals(value, PositiveOptionValue(('1',))) + self.assertEquals(value.origin, 'environment') + else: + with self.assertRaises(InvalidOptionError) as e: + env_option.get_value('MOZ_OPTION=1', 'environment') + self.assertEquals(e.exception.message, 'MOZ_OPTION takes 2 values') + + with self.assertRaises(AssertionError) as e: + env_option.get_value('--%s' % name) + + with self.assertRaises(AssertionError) as e: + env_option.get_value('--foo') + + if nargs in (1, '?', '*', '+'): + value = env_option.get_value('MOZ_OPTION=foo', 'environment') + self.assertEquals(value, PositiveOptionValue(('foo',))) + self.assertEquals(value.origin, 'environment') + else: + with self.assertRaises(InvalidOptionError) as e: + env_option.get_value('MOZ_OPTION=foo', 'environment') + self.assertEquals(e.exception.message, + 'MOZ_OPTION takes %d values' % nargs) + + if nargs in (2, '*', '+'): + value = env_option.get_value('MOZ_OPTION=foo,bar', 'environment') + self.assertEquals(value, PositiveOptionValue(('foo', 'bar'))) + self.assertEquals(value.origin, 'environment') + else: + with self.assertRaises(InvalidOptionError) as e: + env_option.get_value('MOZ_OPTION=foo,bar', 'environment') + if nargs == '?': + self.assertEquals(e.exception.message, + 'MOZ_OPTION takes 0 or 1 values') + else: + self.assertEquals(e.exception.message, + 'MOZ_OPTION takes %d value%s' + % (nargs, 's' if nargs != 1 else '')) + + return option + + def test_option_value_enable(self, enable='enable', disable='disable', + nargs=0, default=None): + option = self.test_option_value('%s-option' % enable, nargs=nargs, + default=default) + + value = option.get_value('--%s-option' % disable, 'option') + self.assertEquals(value, NegativeOptionValue()) + self.assertEquals(value.origin, 'option') + + option = self.test_option_value('%s-option' % disable, nargs=nargs, + default=default) + + if nargs in (0, '?', '*'): + value = option.get_value('--%s-option' % enable, 'option') + self.assertEquals(value, PositiveOptionValue()) + self.assertEquals(value.origin, 'option') + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value('--%s-option' % enable, 'option') + if nargs == 1: + self.assertEquals(e.exception.message, + '--%s-option takes 1 value' % enable) + elif nargs == '+': + self.assertEquals(e.exception.message, + '--%s-option takes 1 or more values' + % enable) + else: + self.assertEquals(e.exception.message, + '--%s-option takes 2 values' % enable) + + def test_option_value_with(self): + self.test_option_value_enable('with', 'without') + + def test_option_value_invalid_nargs(self): + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs='foo') + self.assertEquals(e.exception.message, + "nargs must be a positive integer, '?', '*' or '+'") + + with self.assertRaises(InvalidOptionError) as e: + Option('--option', nargs=-2) + self.assertEquals(e.exception.message, + "nargs must be a positive integer, '?', '*' or '+'") + + def test_option_value_nargs_1(self): + self.test_option_value(nargs=1) + self.test_option_value(nargs=1, default=('a',)) + self.test_option_value_enable(nargs=1, default=('a',)) + + # A default is required + with self.assertRaises(InvalidOptionError) as e: + Option('--disable-option', nargs=1) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + def test_option_value_nargs_2(self): + self.test_option_value(nargs=2) + self.test_option_value(nargs=2, default=('a', 'b')) + self.test_option_value_enable(nargs=2, default=('a', 'b')) + + # A default is required + with self.assertRaises(InvalidOptionError) as e: + Option('--disable-option', nargs=2) + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + def test_option_value_nargs_0_or_1(self): + self.test_option_value(nargs='?') + self.test_option_value(nargs='?', default=('a',)) + self.test_option_value_enable(nargs='?') + self.test_option_value_enable(nargs='?', default=('a',)) + + def test_option_value_nargs_0_or_more(self): + self.test_option_value(nargs='*') + self.test_option_value(nargs='*', default=('a',)) + self.test_option_value(nargs='*', default=('a', 'b')) + self.test_option_value_enable(nargs='*') + self.test_option_value_enable(nargs='*', default=('a',)) + self.test_option_value_enable(nargs='*', default=('a', 'b')) + + def test_option_value_nargs_1_or_more(self): + self.test_option_value(nargs='+') + self.test_option_value(nargs='+', default=('a',)) + self.test_option_value(nargs='+', default=('a', 'b')) + self.test_option_value_enable(nargs='+', default=('a',)) + self.test_option_value_enable(nargs='+', default=('a', 'b')) + + # A default is required + with self.assertRaises(InvalidOptionError) as e: + Option('--disable-option', nargs='+') + self.assertEquals(e.exception.message, + "The given `default` doesn't satisfy `nargs`") + + +class TestCommandLineHelper(unittest.TestCase): + def test_basic(self): + helper = CommandLineHelper({}, ['cmd', '--foo', '--bar']) + + self.assertEquals(['--foo', '--bar'], list(helper)) + + helper.add('--enable-qux') + + self.assertEquals(['--foo', '--bar', '--enable-qux'], list(helper)) + + value, option = helper.handle(Option('--bar')) + self.assertEquals(['--foo', '--enable-qux'], list(helper)) + self.assertEquals(PositiveOptionValue(), value) + self.assertEquals('--bar', option) + + value, option = helper.handle(Option('--baz')) + self.assertEquals(['--foo', '--enable-qux'], list(helper)) + self.assertEquals(NegativeOptionValue(), value) + self.assertEquals(None, option) + + def test_precedence(self): + foo = Option('--with-foo', nargs='*') + helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b']) + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b')), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--with-foo=a,b', option) + + helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b', + '--without-foo']) + value, option = helper.handle(foo) + self.assertEquals(NegativeOptionValue(), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--without-foo', option) + + helper = CommandLineHelper({}, ['cmd', '--without-foo', + '--with-foo=a,b']) + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b')), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--with-foo=a,b', option) + + foo = Option('--with-foo', env='FOO', nargs='*') + helper = CommandLineHelper({'FOO': ''}, ['cmd', '--with-foo=a,b']) + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b')), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--with-foo=a,b', option) + + helper = CommandLineHelper({'FOO': 'a,b'}, ['cmd', '--without-foo']) + value, option = helper.handle(foo) + self.assertEquals(NegativeOptionValue(), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--without-foo', option) + + helper = CommandLineHelper({'FOO': ''}, ['cmd', '--with-bar=a,b']) + value, option = helper.handle(foo) + self.assertEquals(NegativeOptionValue(), value) + self.assertEquals('environment', value.origin) + self.assertEquals('FOO=', option) + + helper = CommandLineHelper({'FOO': 'a,b'}, ['cmd', '--without-bar']) + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b')), value) + self.assertEquals('environment', value.origin) + self.assertEquals('FOO=a,b', option) + + helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b', 'FOO=']) + value, option = helper.handle(foo) + self.assertEquals(NegativeOptionValue(), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('FOO=', option) + + helper = CommandLineHelper({}, ['cmd', '--without-foo', 'FOO=a,b']) + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b')), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('FOO=a,b', option) + + helper = CommandLineHelper({}, ['cmd', 'FOO=', '--with-foo=a,b']) + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b')), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--with-foo=a,b', option) + + helper = CommandLineHelper({}, ['cmd', 'FOO=a,b', '--without-foo']) + value, option = helper.handle(foo) + self.assertEquals(NegativeOptionValue(), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--without-foo', option) + + def test_extra_args(self): + foo = Option('--with-foo', env='FOO', nargs='*') + helper = CommandLineHelper({}, ['cmd']) + helper.add('FOO=a,b,c', 'other-origin') + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) + self.assertEquals('other-origin', value.origin) + self.assertEquals('FOO=a,b,c', option) + + helper = CommandLineHelper({}, ['cmd']) + helper.add('FOO=a,b,c', 'other-origin') + helper.add('--with-foo=a,b,c', 'other-origin') + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) + self.assertEquals('other-origin', value.origin) + self.assertEquals('--with-foo=a,b,c', option) + + # Adding conflicting options is not allowed. + helper = CommandLineHelper({}, ['cmd']) + helper.add('FOO=a,b,c', 'other-origin') + with self.assertRaises(ConflictingOptionError) as cm: + helper.add('FOO=', 'other-origin') + self.assertEqual('FOO=', cm.exception.arg) + self.assertEqual('other-origin', cm.exception.origin) + self.assertEqual('FOO=a,b,c', cm.exception.old_arg) + self.assertEqual('other-origin', cm.exception.old_origin) + with self.assertRaises(ConflictingOptionError) as cm: + helper.add('FOO=a,b', 'other-origin') + self.assertEqual('FOO=a,b', cm.exception.arg) + self.assertEqual('other-origin', cm.exception.origin) + self.assertEqual('FOO=a,b,c', cm.exception.old_arg) + self.assertEqual('other-origin', cm.exception.old_origin) + # But adding the same is allowed. + helper.add('FOO=a,b,c', 'other-origin') + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) + self.assertEquals('other-origin', value.origin) + self.assertEquals('FOO=a,b,c', option) + + # The same rule as above applies when using the option form vs. the + # variable form. But we can't detect it when .add is called. + helper = CommandLineHelper({}, ['cmd']) + helper.add('FOO=a,b,c', 'other-origin') + helper.add('--without-foo', 'other-origin') + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual('--without-foo', cm.exception.arg) + self.assertEqual('other-origin', cm.exception.origin) + self.assertEqual('FOO=a,b,c', cm.exception.old_arg) + self.assertEqual('other-origin', cm.exception.old_origin) + helper = CommandLineHelper({}, ['cmd']) + helper.add('FOO=a,b,c', 'other-origin') + helper.add('--with-foo=a,b', 'other-origin') + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual('--with-foo=a,b', cm.exception.arg) + self.assertEqual('other-origin', cm.exception.origin) + self.assertEqual('FOO=a,b,c', cm.exception.old_arg) + self.assertEqual('other-origin', cm.exception.old_origin) + helper = CommandLineHelper({}, ['cmd']) + helper.add('FOO=a,b,c', 'other-origin') + helper.add('--with-foo=a,b,c', 'other-origin') + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) + self.assertEquals('other-origin', value.origin) + self.assertEquals('--with-foo=a,b,c', option) + + # Conflicts are also not allowed against what is in the + # environment/on the command line. + helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b']) + helper.add('FOO=a,b,c', 'other-origin') + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual('FOO=a,b,c', cm.exception.arg) + self.assertEqual('other-origin', cm.exception.origin) + self.assertEqual('--with-foo=a,b', cm.exception.old_arg) + self.assertEqual('command-line', cm.exception.old_origin) + + helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b']) + helper.add('--without-foo', 'other-origin') + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual('--without-foo', cm.exception.arg) + self.assertEqual('other-origin', cm.exception.origin) + self.assertEqual('--with-foo=a,b', cm.exception.old_arg) + self.assertEqual('command-line', cm.exception.old_origin) + + def test_possible_origins(self): + with self.assertRaises(InvalidOptionError): + Option('--foo', possible_origins='command-line') + + helper = CommandLineHelper({'BAZ': '1'}, ['cmd', '--foo', '--bar']) + foo = Option('--foo', + possible_origins=('command-line',)) + value, option = helper.handle(foo) + self.assertEquals(PositiveOptionValue(), value) + self.assertEquals('command-line', value.origin) + self.assertEquals('--foo', option) + + bar = Option('--bar', + possible_origins=('mozconfig',)) + with self.assertRaisesRegexp(InvalidOptionError, + "--bar can not be set by command-line. Values are accepted from: mozconfig"): + helper.handle(bar) + + baz = Option(env='BAZ', + possible_origins=('implied',)) + with self.assertRaisesRegexp(InvalidOptionError, + "BAZ=1 can not be set by environment. Values are accepted from: implied"): + helper.handle(baz) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py b/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py new file mode 100644 index 000000000..2ef93792b --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py @@ -0,0 +1,1271 @@ +# 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 + +import logging +import os + +from StringIO import StringIO + +from mozunit import main + +from common import BaseConfigureTest +from mozbuild.configure.util import Version +from mozbuild.util import memoize +from mozpack import path as mozpath +from test_toolchain_helpers import ( + FakeCompiler, + CompilerResult, +) + + +DEFAULT_C99 = { + '__STDC_VERSION__': '199901L', +} + +DEFAULT_C11 = { + '__STDC_VERSION__': '201112L', +} + +DEFAULT_CXX_97 = { + '__cplusplus': '199711L', +} + +DEFAULT_CXX_11 = { + '__cplusplus': '201103L', +} + +DEFAULT_CXX_14 = { + '__cplusplus': '201402L', +} + +SUPPORTS_GNU99 = { + '-std=gnu99': DEFAULT_C99, +} + +SUPPORTS_GNUXX11 = { + '-std=gnu++11': DEFAULT_CXX_11, +} + +SUPPORTS_CXX14 = { + '-std=c++14': DEFAULT_CXX_14, +} + + +@memoize +def GCC_BASE(version): + version = Version(version) + return FakeCompiler({ + '__GNUC__': version.major, + '__GNUC_MINOR__': version.minor, + '__GNUC_PATCHLEVEL__': version.patch, + '__STDC__': 1, + '__ORDER_LITTLE_ENDIAN__': 1234, + '__ORDER_BIG_ENDIAN__': 4321, + }) + + +@memoize +def GCC(version): + return GCC_BASE(version) + SUPPORTS_GNU99 + + +@memoize +def GXX(version): + return GCC_BASE(version) + DEFAULT_CXX_97 + SUPPORTS_GNUXX11 + + +GCC_4_7 = GCC('4.7.3') +GXX_4_7 = GXX('4.7.3') +GCC_4_9 = GCC('4.9.3') +GXX_4_9 = GXX('4.9.3') +GCC_5 = GCC('5.2.1') + DEFAULT_C11 +GXX_5 = GXX('5.2.1') + +GCC_PLATFORM_LITTLE_ENDIAN = { + '__BYTE_ORDER__': 1234, +} + +GCC_PLATFORM_BIG_ENDIAN = { + '__BYTE_ORDER__': 4321, +} + +GCC_PLATFORM_X86 = FakeCompiler(GCC_PLATFORM_LITTLE_ENDIAN) + { + None: { + '__i386__': 1, + }, + '-m64': { + '__i386__': False, + '__x86_64__': 1, + }, +} + +GCC_PLATFORM_X86_64 = FakeCompiler(GCC_PLATFORM_LITTLE_ENDIAN) + { + None: { + '__x86_64__': 1, + }, + '-m32': { + '__x86_64__': False, + '__i386__': 1, + }, +} + +GCC_PLATFORM_ARM = FakeCompiler(GCC_PLATFORM_LITTLE_ENDIAN) + { + '__arm__': 1, +} + +GCC_PLATFORM_LINUX = { + '__linux__': 1, +} + +GCC_PLATFORM_DARWIN = { + '__APPLE__': 1, +} + +GCC_PLATFORM_WIN = { + '_WIN32': 1, + 'WINNT': 1, +} + +GCC_PLATFORM_X86_LINUX = FakeCompiler(GCC_PLATFORM_X86, GCC_PLATFORM_LINUX) +GCC_PLATFORM_X86_64_LINUX = FakeCompiler(GCC_PLATFORM_X86_64, + GCC_PLATFORM_LINUX) +GCC_PLATFORM_ARM_LINUX = FakeCompiler(GCC_PLATFORM_ARM, GCC_PLATFORM_LINUX) +GCC_PLATFORM_X86_OSX = FakeCompiler(GCC_PLATFORM_X86, GCC_PLATFORM_DARWIN) +GCC_PLATFORM_X86_64_OSX = FakeCompiler(GCC_PLATFORM_X86_64, + GCC_PLATFORM_DARWIN) +GCC_PLATFORM_X86_WIN = FakeCompiler(GCC_PLATFORM_X86, GCC_PLATFORM_WIN) +GCC_PLATFORM_X86_64_WIN = FakeCompiler(GCC_PLATFORM_X86_64, GCC_PLATFORM_WIN) + + +@memoize +def CLANG_BASE(version): + version = Version(version) + return FakeCompiler({ + '__clang__': 1, + '__clang_major__': version.major, + '__clang_minor__': version.minor, + '__clang_patchlevel__': version.patch, + }) + + +@memoize +def CLANG(version): + return GCC_BASE('4.2.1') + CLANG_BASE(version) + SUPPORTS_GNU99 + + +@memoize +def CLANGXX(version): + return (GCC_BASE('4.2.1') + CLANG_BASE(version) + DEFAULT_CXX_97 + + SUPPORTS_GNUXX11) + + +CLANG_3_3 = CLANG('3.3.0') + DEFAULT_C99 +CLANGXX_3_3 = CLANGXX('3.3.0') +CLANG_3_6 = CLANG('3.6.2') + DEFAULT_C11 +CLANGXX_3_6 = CLANGXX('3.6.2') + { + '-std=gnu++11': { + '__has_feature(cxx_alignof)': '1', + }, +} + + +def CLANG_PLATFORM(gcc_platform): + base = { + '--target=x86_64-linux-gnu': GCC_PLATFORM_X86_64_LINUX[None], + '--target=x86_64-darwin11.2.0': GCC_PLATFORM_X86_64_OSX[None], + '--target=i686-linux-gnu': GCC_PLATFORM_X86_LINUX[None], + '--target=i686-darwin11.2.0': GCC_PLATFORM_X86_OSX[None], + '--target=arm-linux-gnu': GCC_PLATFORM_ARM_LINUX[None], + } + undo_gcc_platform = { + k: {symbol: False for symbol in gcc_platform[None]} + for k in base + } + return FakeCompiler(gcc_platform, undo_gcc_platform, base) + + +CLANG_PLATFORM_X86_LINUX = CLANG_PLATFORM(GCC_PLATFORM_X86_LINUX) +CLANG_PLATFORM_X86_64_LINUX = CLANG_PLATFORM(GCC_PLATFORM_X86_64_LINUX) +CLANG_PLATFORM_X86_OSX = CLANG_PLATFORM(GCC_PLATFORM_X86_OSX) +CLANG_PLATFORM_X86_64_OSX = CLANG_PLATFORM(GCC_PLATFORM_X86_64_OSX) +CLANG_PLATFORM_X86_WIN = CLANG_PLATFORM(GCC_PLATFORM_X86_WIN) +CLANG_PLATFORM_X86_64_WIN = CLANG_PLATFORM(GCC_PLATFORM_X86_64_WIN) + + +@memoize +def VS(version): + version = Version(version) + return FakeCompiler({ + None: { + '_MSC_VER': '%02d%02d' % (version.major, version.minor), + '_MSC_FULL_VER': '%02d%02d%05d' % (version.major, version.minor, + version.patch), + }, + '*.cpp': DEFAULT_CXX_97, + }) + + +VS_2013u2 = VS('18.00.30501') +VS_2013u3 = VS('18.00.30723') +VS_2015 = VS('19.00.23026') +VS_2015u1 = VS('19.00.23506') +VS_2015u2 = VS('19.00.23918') +VS_2015u3 = VS('19.00.24213') + +VS_PLATFORM_X86 = { + '_M_IX86': 600, + '_WIN32': 1, +} + +VS_PLATFORM_X86_64 = { + '_M_X64': 100, + '_WIN32': 1, + '_WIN64': 1, +} + +# Note: In reality, the -std=gnu* options are only supported when preceded by +# -Xclang. +CLANG_CL_3_9 = (CLANG_BASE('3.9.0') + VS('18.00.00000') + DEFAULT_C11 + + SUPPORTS_GNU99 + SUPPORTS_GNUXX11 + SUPPORTS_CXX14) + { + '*.cpp': { + '__STDC_VERSION__': False, + '__cplusplus': '201103L', + }, + '-fms-compatibility-version=19.00.24213': VS('19.00.24213')[None], +} + +CLANG_CL_PLATFORM_X86 = FakeCompiler(VS_PLATFORM_X86, GCC_PLATFORM_X86[None]) +CLANG_CL_PLATFORM_X86_64 = FakeCompiler(VS_PLATFORM_X86_64, GCC_PLATFORM_X86_64[None]) + + +class BaseToolchainTest(BaseConfigureTest): + def setUp(self): + super(BaseToolchainTest, self).setUp() + self.out = StringIO() + self.logger = logging.getLogger('BaseToolchainTest') + self.logger.setLevel(logging.ERROR) + self.handler = logging.StreamHandler(self.out) + self.logger.addHandler(self.handler) + + def tearDown(self): + self.logger.removeHandler(self.handler) + del self.handler + del self.out + super(BaseToolchainTest, self).tearDown() + + def do_toolchain_test(self, paths, results, args=[], environ={}): + '''Helper to test the toolchain checks from toolchain.configure. + + - `paths` is a dict associating compiler paths to FakeCompiler + definitions from above. + - `results` is a dict associating result variable names from + toolchain.configure (c_compiler, cxx_compiler, host_c_compiler, + host_cxx_compiler) with a result. + The result can either be an error string, or a CompilerResult + corresponding to the object returned by toolchain.configure checks. + When the results for host_c_compiler are identical to c_compiler, + they can be omitted. Likewise for host_cxx_compiler vs. + cxx_compiler. + ''' + environ = dict(environ) + if 'PATH' not in environ: + environ['PATH'] = os.pathsep.join( + mozpath.abspath(p) for p in ('/bin', '/usr/bin')) + + sandbox = self.get_sandbox(paths, {}, args, environ, + logger=self.logger) + + for var in ('c_compiler', 'cxx_compiler', 'host_c_compiler', + 'host_cxx_compiler'): + if var in results: + result = results[var] + elif var.startswith('host_'): + result = results.get(var[5:], {}) + else: + result = {} + try: + self.out.truncate(0) + compiler = sandbox._value_for(sandbox[var]) + # Add var on both ends to make it clear which of the + # variables is failing the test when that happens. + self.assertEquals((var, compiler), (var, result)) + except SystemExit: + self.assertEquals((var, result), + (var, self.out.getvalue().strip())) + return + + +class LinuxToolchainTest(BaseToolchainTest): + PATHS = { + '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_64_LINUX, + '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_LINUX, + '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_64_LINUX, + '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_LINUX, + '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_LINUX, + '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_64_LINUX, + } + GCC_4_7_RESULT = ('Only GCC 4.8 or newer is supported ' + '(found version 4.7.3).') + GXX_4_7_RESULT = GCC_4_7_RESULT + GCC_4_9_RESULT = CompilerResult( + flags=['-std=gnu99'], + version='4.9.3', + type='gcc', + compiler='/usr/bin/gcc', + language='C', + ) + GXX_4_9_RESULT = CompilerResult( + flags=['-std=gnu++11'], + version='4.9.3', + type='gcc', + compiler='/usr/bin/g++', + language='C++', + ) + GCC_5_RESULT = CompilerResult( + flags=['-std=gnu99'], + version='5.2.1', + type='gcc', + compiler='/usr/bin/gcc-5', + language='C', + ) + GXX_5_RESULT = CompilerResult( + flags=['-std=gnu++11'], + version='5.2.1', + type='gcc', + compiler='/usr/bin/g++-5', + language='C++', + ) + CLANG_3_3_RESULT = CompilerResult( + flags=[], + version='3.3.0', + type='clang', + compiler='/usr/bin/clang-3.3', + language='C', + ) + CLANGXX_3_3_RESULT = 'Only clang/llvm 3.6 or newer is supported.' + CLANG_3_6_RESULT = CompilerResult( + flags=['-std=gnu99'], + version='3.6.2', + type='clang', + compiler='/usr/bin/clang', + language='C', + ) + CLANGXX_3_6_RESULT = CompilerResult( + flags=['-std=gnu++11'], + version='3.6.2', + type='clang', + compiler='/usr/bin/clang++', + language='C++', + ) + + def test_gcc(self): + # We'll try gcc and clang, and find gcc first. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': self.GXX_4_9_RESULT, + }) + + def test_unsupported_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_7_RESULT, + }, environ={ + 'CC': 'gcc-4.7', + 'CXX': 'g++-4.7', + }) + + # Maybe this should be reporting the mismatched version instead. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': self.GXX_4_7_RESULT, + }, environ={ + 'CXX': 'g++-4.7', + }) + + def test_overridden_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_5_RESULT, + 'cxx_compiler': self.GXX_5_RESULT, + }, environ={ + 'CC': 'gcc-5', + 'CXX': 'g++-5', + }) + + def test_guess_cxx(self): + # When CXX is not set, we guess it from CC. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_5_RESULT, + 'cxx_compiler': self.GXX_5_RESULT, + }, environ={ + 'CC': 'gcc-5', + }) + + def test_mismatched_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': ( + 'The target C compiler is version 4.9.3, while the target ' + 'C++ compiler is version 5.2.1. Need to use the same compiler ' + 'version.'), + }, environ={ + 'CXX': 'g++-5', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': self.GXX_4_9_RESULT, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': ( + 'The host C compiler is version 4.9.3, while the host ' + 'C++ compiler is version 5.2.1. Need to use the same compiler ' + 'version.'), + }, environ={ + 'HOST_CXX': 'g++-5', + }) + + def test_mismatched_compiler(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': ( + 'The target C compiler is gcc, while the target C++ compiler ' + 'is clang. Need to use the same compiler suite.'), + }, environ={ + 'CXX': 'clang++', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': self.GXX_4_9_RESULT, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': ( + 'The host C compiler is gcc, while the host C++ compiler ' + 'is clang. Need to use the same compiler suite.'), + }, environ={ + 'HOST_CXX': 'clang++', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': '`%s` is not a C compiler.' + % mozpath.abspath('/usr/bin/g++'), + }, environ={ + 'CC': 'g++', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': '`%s` is not a C++ compiler.' + % mozpath.abspath('/usr/bin/gcc'), + }, environ={ + 'CXX': 'gcc', + }) + + def test_clang(self): + # We'll try gcc and clang, but since there is no gcc (gcc-x.y doesn't + # count), find clang. + paths = { + k: v for k, v in self.PATHS.iteritems() + if os.path.basename(k) not in ('gcc', 'g++') + } + self.do_toolchain_test(paths, { + 'c_compiler': self.CLANG_3_6_RESULT, + 'cxx_compiler': self.CLANGXX_3_6_RESULT, + }) + + def test_guess_cxx_clang(self): + # When CXX is not set, we guess it from CC. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_6_RESULT + { + 'compiler': '/usr/bin/clang-3.6', + }, + 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'compiler': '/usr/bin/clang++-3.6', + }, + }, environ={ + 'CC': 'clang-3.6', + }) + + def test_unsupported_clang(self): + # clang 3.3 C compiler is perfectly fine, but we need more for C++. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_3_RESULT, + 'cxx_compiler': self.CLANGXX_3_3_RESULT, + }, environ={ + 'CC': 'clang-3.3', + 'CXX': 'clang++-3.3', + }) + + def test_no_supported_compiler(self): + # Even if there are gcc-x.y or clang-x.y compilers available, we + # don't try them. This could be considered something to improve. + paths = { + k: v for k, v in self.PATHS.iteritems() + if os.path.basename(k) not in ('gcc', 'g++', 'clang', 'clang++') + } + self.do_toolchain_test(paths, { + 'c_compiler': 'Cannot find the target C compiler', + }) + + def test_absolute_path(self): + paths = dict(self.PATHS) + paths.update({ + '/opt/clang/bin/clang': paths['/usr/bin/clang'], + '/opt/clang/bin/clang++': paths['/usr/bin/clang++'], + }) + result = { + 'c_compiler': self.CLANG_3_6_RESULT + { + 'compiler': '/opt/clang/bin/clang', + }, + 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'compiler': '/opt/clang/bin/clang++' + }, + } + self.do_toolchain_test(paths, result, environ={ + 'CC': '/opt/clang/bin/clang', + 'CXX': '/opt/clang/bin/clang++', + }) + # With CXX guess too. + self.do_toolchain_test(paths, result, environ={ + 'CC': '/opt/clang/bin/clang', + }) + + def test_atypical_name(self): + paths = dict(self.PATHS) + paths.update({ + '/usr/bin/afl-clang-fast': paths['/usr/bin/clang'], + '/usr/bin/afl-clang-fast++': paths['/usr/bin/clang++'], + }) + self.do_toolchain_test(paths, { + 'c_compiler': self.CLANG_3_6_RESULT + { + 'compiler': '/usr/bin/afl-clang-fast', + }, + 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'compiler': '/usr/bin/afl-clang-fast++', + }, + }, environ={ + 'CC': 'afl-clang-fast', + 'CXX': 'afl-clang-fast++', + }) + + def test_mixed_compilers(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_6_RESULT, + 'cxx_compiler': self.CLANGXX_3_6_RESULT, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }, environ={ + 'CC': 'clang', + 'HOST_CC': 'gcc', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_6_RESULT, + 'cxx_compiler': self.CLANGXX_3_6_RESULT, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }, environ={ + 'CC': 'clang', + 'CXX': 'clang++', + 'HOST_CC': 'gcc', + }) + + +class LinuxSimpleCrossToolchainTest(BaseToolchainTest): + TARGET = 'i686-pc-linux-gnu' + PATHS = LinuxToolchainTest.PATHS + GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT + GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT + CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT + CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + + def test_cross_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT + { + 'flags': ['-m32'] + }, + 'cxx_compiler': self.GXX_4_9_RESULT + { + 'flags': ['-m32'] + }, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }) + + def test_cross_clang(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_6_RESULT + { + 'flags': ['--target=i686-linux-gnu'], + }, + 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'flags': ['--target=i686-linux-gnu'], + }, + 'host_c_compiler': self.CLANG_3_6_RESULT, + 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + }, environ={ + 'CC': 'clang', + }) + + +class LinuxX86_64CrossToolchainTest(BaseToolchainTest): + HOST = 'i686-pc-linux-gnu' + TARGET = 'x86_64-pc-linux-gnu' + PATHS = { + '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_LINUX, + '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_LINUX, + '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_LINUX, + '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_LINUX, + } + GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT + GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT + CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT + CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + + def test_cross_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_9_RESULT + { + 'flags': ['-m64'] + }, + 'cxx_compiler': self.GXX_4_9_RESULT + { + 'flags': ['-m64'] + }, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }) + + def test_cross_clang(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_6_RESULT + { + 'flags': ['--target=x86_64-linux-gnu'], + }, + 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'flags': ['--target=x86_64-linux-gnu'], + }, + 'host_c_compiler': self.CLANG_3_6_RESULT, + 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + }, environ={ + 'CC': 'clang', + }) + + +class OSXToolchainTest(BaseToolchainTest): + HOST = 'x86_64-apple-darwin11.2.0' + PATHS = { + '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_64_OSX, + '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_OSX, + '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_64_OSX, + '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_OSX, + '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_OSX, + '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_64_OSX, + } + CLANG_3_3_RESULT = LinuxToolchainTest.CLANG_3_3_RESULT + CLANGXX_3_3_RESULT = LinuxToolchainTest.CLANGXX_3_3_RESULT + CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT + CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + GCC_4_7_RESULT = LinuxToolchainTest.GCC_4_7_RESULT + GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT + GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT + + def test_clang(self): + # We only try clang because gcc is known not to work. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_6_RESULT, + 'cxx_compiler': self.CLANGXX_3_6_RESULT, + }) + + def test_not_gcc(self): + # We won't pick GCC if it's the only thing available. + paths = { + k: v for k, v in self.PATHS.iteritems() + if os.path.basename(k) not in ('clang', 'clang++') + } + self.do_toolchain_test(paths, { + 'c_compiler': 'Cannot find the target C compiler', + }) + + def test_unsupported_clang(self): + # clang 3.3 C compiler is perfectly fine, but we need more for C++. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_3_RESULT, + 'cxx_compiler': self.CLANGXX_3_3_RESULT, + }, environ={ + 'CC': 'clang-3.3', + 'CXX': 'clang++-3.3', + }) + + def test_forced_gcc(self): + # GCC can still be forced if the user really wants it. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_5_RESULT, + 'cxx_compiler': self.GXX_5_RESULT, + }, environ={ + 'CC': 'gcc-5', + 'CXX': 'g++-5', + }) + + def test_forced_unsupported_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_7_RESULT, + }, environ={ + 'CC': 'gcc-4.7', + 'CXX': 'g++-4.7', + }) + + +class WindowsToolchainTest(BaseToolchainTest): + HOST = 'i686-pc-mingw32' + + # For the purpose of this test, it doesn't matter that the paths are not + # real Windows paths. + PATHS = { + '/opt/VS_2013u2/bin/cl': VS_2013u2 + VS_PLATFORM_X86, + '/opt/VS_2013u3/bin/cl': VS_2013u3 + VS_PLATFORM_X86, + '/opt/VS_2015/bin/cl': VS_2015 + VS_PLATFORM_X86, + '/opt/VS_2015u1/bin/cl': VS_2015u1 + VS_PLATFORM_X86, + '/opt/VS_2015u2/bin/cl': VS_2015u2 + VS_PLATFORM_X86, + '/usr/bin/cl': VS_2015u3 + VS_PLATFORM_X86, + '/usr/bin/clang-cl': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86, + '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_WIN, + '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_WIN, + '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_WIN, + '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_WIN, + '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_WIN, + '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_WIN, + '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_WIN, + '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_WIN, + '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_WIN, + '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_WIN, + '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_WIN, + '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_WIN, + } + + VS_2013u2_RESULT = ( + 'This version (18.00.30501) of the MSVC compiler is not supported.\n' + 'You must install Visual C++ 2015 Update 3 or newer in order to build.\n' + 'See https://developer.mozilla.org/en/Windows_Build_Prerequisites') + VS_2013u3_RESULT = ( + 'This version (18.00.30723) of the MSVC compiler is not supported.\n' + 'You must install Visual C++ 2015 Update 3 or newer in order to build.\n' + 'See https://developer.mozilla.org/en/Windows_Build_Prerequisites') + VS_2015_RESULT = ( + 'This version (19.00.23026) of the MSVC compiler is not supported.\n' + 'You must install Visual C++ 2015 Update 3 or newer in order to build.\n' + 'See https://developer.mozilla.org/en/Windows_Build_Prerequisites') + VS_2015u1_RESULT = ( + 'This version (19.00.23506) of the MSVC compiler is not supported.\n' + 'You must install Visual C++ 2015 Update 3 or newer in order to build.\n' + 'See https://developer.mozilla.org/en/Windows_Build_Prerequisites') + VS_2015u2_RESULT = ( + 'This version (19.00.23918) of the MSVC compiler is not supported.\n' + 'You must install Visual C++ 2015 Update 3 or newer in order to build.\n' + 'See https://developer.mozilla.org/en/Windows_Build_Prerequisites') + VS_2015u3_RESULT = CompilerResult( + flags=[], + version='19.00.24213', + type='msvc', + compiler='/usr/bin/cl', + language='C', + ) + VSXX_2015u3_RESULT = CompilerResult( + flags=[], + version='19.00.24213', + type='msvc', + compiler='/usr/bin/cl', + language='C++', + ) + CLANG_CL_3_9_RESULT = CompilerResult( + flags=['-Xclang', '-std=gnu99', + '-fms-compatibility-version=19.00.24213', '-fallback'], + version='19.00.24213', + type='clang-cl', + compiler='/usr/bin/clang-cl', + language='C', + ) + CLANGXX_CL_3_9_RESULT = CompilerResult( + flags=['-Xclang', '-std=c++14', + '-fms-compatibility-version=19.00.24213', '-fallback'], + version='19.00.24213', + type='clang-cl', + compiler='/usr/bin/clang-cl', + language='C++', + ) + CLANG_3_3_RESULT = LinuxToolchainTest.CLANG_3_3_RESULT + CLANGXX_3_3_RESULT = LinuxToolchainTest.CLANGXX_3_3_RESULT + CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT + CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + GCC_4_7_RESULT = LinuxToolchainTest.GCC_4_7_RESULT + GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT + GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT + GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT + GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT + + # VS2015u3 or greater is required. + def test_msvc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.VS_2015u3_RESULT, + 'cxx_compiler': self.VSXX_2015u3_RESULT, + }) + + def test_unsupported_msvc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.VS_2015u2_RESULT, + }, environ={ + 'CC': '/opt/VS_2015u2/bin/cl', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.VS_2015u1_RESULT, + }, environ={ + 'CC': '/opt/VS_2015u1/bin/cl', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.VS_2015_RESULT, + }, environ={ + 'CC': '/opt/VS_2015/bin/cl', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.VS_2013u3_RESULT, + }, environ={ + 'CC': '/opt/VS_2013u3/bin/cl', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.VS_2013u2_RESULT, + }, environ={ + 'CC': '/opt/VS_2013u2/bin/cl', + }) + + def test_clang_cl(self): + # We'll pick clang-cl if msvc can't be found. + paths = { + k: v for k, v in self.PATHS.iteritems() + if os.path.basename(k) != 'cl' + } + self.do_toolchain_test(paths, { + 'c_compiler': self.CLANG_CL_3_9_RESULT, + 'cxx_compiler': self.CLANGXX_CL_3_9_RESULT, + }) + + def test_gcc(self): + # We'll pick GCC if msvc and clang-cl can't be found. + paths = { + k: v for k, v in self.PATHS.iteritems() + if os.path.basename(k) not in ('cl', 'clang-cl') + } + self.do_toolchain_test(paths, { + 'c_compiler': self.GCC_4_9_RESULT, + 'cxx_compiler': self.GXX_4_9_RESULT, + }) + + def test_overridden_unsupported_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.GCC_4_7_RESULT, + }, environ={ + 'CC': 'gcc-4.7', + 'CXX': 'g++-4.7', + }) + + def test_clang(self): + # We'll pick clang if nothing else is found. + paths = { + k: v for k, v in self.PATHS.iteritems() + if os.path.basename(k) not in ('cl', 'clang-cl', 'gcc') + } + self.do_toolchain_test(paths, { + 'c_compiler': self.CLANG_3_6_RESULT, + 'cxx_compiler': self.CLANGXX_3_6_RESULT, + }) + + def test_overridden_unsupported_clang(self): + # clang 3.3 C compiler is perfectly fine, but we need more for C++. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_3_RESULT, + 'cxx_compiler': self.CLANGXX_3_3_RESULT, + }, environ={ + 'CC': 'clang-3.3', + 'CXX': 'clang++-3.3', + }) + + def test_cannot_cross(self): + paths = { + '/usr/bin/cl': VS_2015u3 + VS_PLATFORM_X86_64, + } + self.do_toolchain_test(paths, { + 'c_compiler': ('Target C compiler target CPU (x86_64) ' + 'does not match --target CPU (i686)'), + }) + + +class Windows64ToolchainTest(WindowsToolchainTest): + HOST = 'x86_64-pc-mingw32' + + # For the purpose of this test, it doesn't matter that the paths are not + # real Windows paths. + PATHS = { + '/opt/VS_2013u2/bin/cl': VS_2013u2 + VS_PLATFORM_X86_64, + '/opt/VS_2013u3/bin/cl': VS_2013u3 + VS_PLATFORM_X86_64, + '/opt/VS_2015/bin/cl': VS_2015 + VS_PLATFORM_X86_64, + '/opt/VS_2015u1/bin/cl': VS_2015u1 + VS_PLATFORM_X86_64, + '/opt/VS_2015u2/bin/cl': VS_2015u2 + VS_PLATFORM_X86_64, + '/usr/bin/cl': VS_2015u3 + VS_PLATFORM_X86_64, + '/usr/bin/clang-cl': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86_64, + '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_64_WIN, + '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_WIN, + '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_64_WIN, + '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_WIN, + '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_WIN, + '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_64_WIN, + } + + def test_cannot_cross(self): + paths = { + '/usr/bin/cl': VS_2015u3 + VS_PLATFORM_X86, + } + self.do_toolchain_test(paths, { + 'c_compiler': ('Target C compiler target CPU (x86) ' + 'does not match --target CPU (x86_64)'), + }) + + +class LinuxCrossCompileToolchainTest(BaseToolchainTest): + TARGET = 'arm-unknown-linux-gnu' + PATHS = { + '/usr/bin/arm-linux-gnu-gcc': GCC_4_9 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-g++': GXX_4_9 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-gcc-4.7': GCC_4_7 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-g++-4.7': GXX_4_7 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-gcc-5': GCC_5 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-g++-5': GXX_5 + GCC_PLATFORM_ARM_LINUX, + } + PATHS.update(LinuxToolchainTest.PATHS) + ARM_GCC_4_7_RESULT = LinuxToolchainTest.GXX_4_7_RESULT + ARM_GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT + { + 'compiler': '/usr/bin/arm-linux-gnu-gcc-5', + } + ARM_GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT + { + 'compiler': '/usr/bin/arm-linux-gnu-g++-5', + } + CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT + CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT + GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT + + little_endian = FakeCompiler(GCC_PLATFORM_LINUX, + GCC_PLATFORM_LITTLE_ENDIAN) + big_endian = FakeCompiler(GCC_PLATFORM_LINUX, GCC_PLATFORM_BIG_ENDIAN) + + PLATFORMS = { + 'i686-pc-linux-gnu': GCC_PLATFORM_X86_LINUX, + 'x86_64-pc-linux-gnu': GCC_PLATFORM_X86_64_LINUX, + 'arm-unknown-linux-gnu': GCC_PLATFORM_ARM_LINUX, + 'aarch64-unknown-linux-gnu': little_endian + { + '__aarch64__': 1, + }, + 'ia64-unknown-linux-gnu': little_endian + { + '__ia64__': 1, + }, + 's390x-unknown-linux-gnu': big_endian + { + '__s390x__': 1, + '__s390__': 1, + }, + 's390-unknown-linux-gnu': big_endian + { + '__s390__': 1, + }, + 'powerpc64-unknown-linux-gnu': big_endian + { + None: { + '__powerpc64__': 1, + '__powerpc__': 1, + }, + '-m32': { + '__powerpc64__': False, + }, + }, + 'powerpc-unknown-linux-gnu': big_endian + { + None: { + '__powerpc__': 1, + }, + '-m64': { + '__powerpc64__': 1, + }, + }, + 'alpha-unknown-linux-gnu': little_endian + { + '__alpha__': 1, + }, + 'hppa-unknown-linux-gnu': big_endian + { + '__hppa__': 1, + }, + 'sparc64-unknown-linux-gnu': big_endian + { + None: { + '__arch64__': 1, + '__sparc__': 1, + }, + '-m32': { + '__arch64__': False, + }, + }, + 'sparc-unknown-linux-gnu': big_endian + { + None: { + '__sparc__': 1, + }, + '-m64': { + '__arch64__': 1, + }, + }, + 'mips64-unknown-linux-gnuabi64': big_endian + { + '__mips64': 1, + '__mips__': 1, + }, + 'mips-unknown-linux-gnu': big_endian + { + '__mips__': 1, + }, + } + + PLATFORMS['powerpc64le-unknown-linux-gnu'] = \ + PLATFORMS['powerpc64-unknown-linux-gnu'] + GCC_PLATFORM_LITTLE_ENDIAN + PLATFORMS['mips64el-unknown-linux-gnuabi64'] = \ + PLATFORMS['mips64-unknown-linux-gnuabi64'] + GCC_PLATFORM_LITTLE_ENDIAN + PLATFORMS['mipsel-unknown-linux-gnu'] = \ + PLATFORMS['mips-unknown-linux-gnu'] + GCC_PLATFORM_LITTLE_ENDIAN + + def do_test_cross_gcc_32_64(self, host, target): + self.HOST = host + self.TARGET = target + paths = { + '/usr/bin/gcc': GCC_4_9 + self.PLATFORMS[host], + '/usr/bin/g++': GXX_4_9 + self.PLATFORMS[host], + } + cross_flags = { + 'flags': ['-m64' if '64' in target else '-m32'] + } + self.do_toolchain_test(paths, { + 'c_compiler': self.GCC_4_9_RESULT + cross_flags, + 'cxx_compiler': self.GXX_4_9_RESULT + cross_flags, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }) + self.HOST = LinuxCrossCompileToolchainTest.HOST + self.TARGET = LinuxCrossCompileToolchainTest.TARGET + + def test_cross_x86_x64(self): + self.do_test_cross_gcc_32_64( + 'i686-pc-linux-gnu', 'x86_64-pc-linux-gnu') + self.do_test_cross_gcc_32_64( + 'x86_64-pc-linux-gnu', 'i686-pc-linux-gnu') + + def test_cross_sparc_sparc64(self): + self.do_test_cross_gcc_32_64( + 'sparc-unknown-linux-gnu', 'sparc64-unknown-linux-gnu') + self.do_test_cross_gcc_32_64( + 'sparc64-unknown-linux-gnu', 'sparc-unknown-linux-gnu') + + def test_cross_ppc_ppc64(self): + self.do_test_cross_gcc_32_64( + 'powerpc-unknown-linux-gnu', 'powerpc64-unknown-linux-gnu') + self.do_test_cross_gcc_32_64( + 'powerpc64-unknown-linux-gnu', 'powerpc-unknown-linux-gnu') + + def do_test_cross_gcc(self, host, target): + self.HOST = host + self.TARGET = target + host_cpu = host.split('-')[0] + cpu, manufacturer, os = target.split('-', 2) + toolchain_prefix = '/usr/bin/%s-%s' % (cpu, os) + paths = { + '/usr/bin/gcc': GCC_4_9 + self.PLATFORMS[host], + '/usr/bin/g++': GXX_4_9 + self.PLATFORMS[host], + } + self.do_toolchain_test(paths, { + 'c_compiler': ('Target C compiler target CPU (%s) ' + 'does not match --target CPU (%s)' + % (host_cpu, cpu)), + }) + + paths.update({ + '%s-gcc' % toolchain_prefix: GCC_4_9 + self.PLATFORMS[target], + '%s-g++' % toolchain_prefix: GXX_4_9 + self.PLATFORMS[target], + }) + self.do_toolchain_test(paths, { + 'c_compiler': self.GCC_4_9_RESULT + { + 'compiler': '%s-gcc' % toolchain_prefix, + }, + 'cxx_compiler': self.GXX_4_9_RESULT + { + 'compiler': '%s-g++' % toolchain_prefix, + }, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }) + self.HOST = LinuxCrossCompileToolchainTest.HOST + self.TARGET = LinuxCrossCompileToolchainTest.TARGET + + def test_cross_gcc_misc(self): + for target in self.PLATFORMS: + if not target.endswith('-pc-linux-gnu'): + self.do_test_cross_gcc('x86_64-pc-linux-gnu', target) + + def test_cannot_cross(self): + self.TARGET = 'mipsel-unknown-linux-gnu' + + paths = { + '/usr/bin/gcc': GCC_4_9 + self.PLATFORMS['mips-unknown-linux-gnu'], + '/usr/bin/g++': GXX_4_9 + self.PLATFORMS['mips-unknown-linux-gnu'], + } + self.do_toolchain_test(paths, { + 'c_compiler': ('Target C compiler target endianness (big) ' + 'does not match --target endianness (little)'), + }) + self.TARGET = LinuxCrossCompileToolchainTest.TARGET + + def test_overridden_cross_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.ARM_GCC_5_RESULT, + 'cxx_compiler': self.ARM_GXX_5_RESULT, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }, environ={ + 'CC': 'arm-linux-gnu-gcc-5', + 'CXX': 'arm-linux-gnu-g++-5', + }) + + def test_overridden_unsupported_cross_gcc(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.ARM_GCC_4_7_RESULT, + }, environ={ + 'CC': 'arm-linux-gnu-gcc-4.7', + 'CXX': 'arm-linux-gnu-g++-4.7', + }) + + def test_guess_cross_cxx(self): + # When CXX is not set, we guess it from CC. + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.ARM_GCC_5_RESULT, + 'cxx_compiler': self.ARM_GXX_5_RESULT, + 'host_c_compiler': self.GCC_4_9_RESULT, + 'host_cxx_compiler': self.GXX_4_9_RESULT, + }, environ={ + 'CC': 'arm-linux-gnu-gcc-5', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.ARM_GCC_5_RESULT, + 'cxx_compiler': self.ARM_GXX_5_RESULT, + 'host_c_compiler': self.CLANG_3_6_RESULT, + 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + }, environ={ + 'CC': 'arm-linux-gnu-gcc-5', + 'HOST_CC': 'clang', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.ARM_GCC_5_RESULT, + 'cxx_compiler': self.ARM_GXX_5_RESULT, + 'host_c_compiler': self.CLANG_3_6_RESULT, + 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + }, environ={ + 'CC': 'arm-linux-gnu-gcc-5', + 'CXX': 'arm-linux-gnu-g++-5', + 'HOST_CC': 'clang', + }) + + def test_cross_clang(self): + cross_clang_result = self.CLANG_3_6_RESULT + { + 'flags': ['--target=arm-linux-gnu'], + } + cross_clangxx_result = self.CLANGXX_3_6_RESULT + { + 'flags': ['--target=arm-linux-gnu'], + } + self.do_toolchain_test(self.PATHS, { + 'c_compiler': cross_clang_result, + 'cxx_compiler': cross_clangxx_result, + 'host_c_compiler': self.CLANG_3_6_RESULT, + 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + }, environ={ + 'CC': 'clang', + 'HOST_CC': 'clang', + }) + + self.do_toolchain_test(self.PATHS, { + 'c_compiler': cross_clang_result, + 'cxx_compiler': cross_clangxx_result, + 'host_c_compiler': self.CLANG_3_6_RESULT, + 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + }, environ={ + 'CC': 'clang', + }) + + def test_cross_atypical_clang(self): + paths = dict(self.PATHS) + paths.update({ + '/usr/bin/afl-clang-fast': paths['/usr/bin/clang'], + '/usr/bin/afl-clang-fast++': paths['/usr/bin/clang++'], + }) + afl_clang_result = self.CLANG_3_6_RESULT + { + 'compiler': '/usr/bin/afl-clang-fast', + } + afl_clangxx_result = self.CLANGXX_3_6_RESULT + { + 'compiler': '/usr/bin/afl-clang-fast++', + } + self.do_toolchain_test(paths, { + 'c_compiler': afl_clang_result + { + 'flags': ['--target=arm-linux-gnu'], + }, + 'cxx_compiler': afl_clangxx_result + { + 'flags': ['--target=arm-linux-gnu'], + }, + 'host_c_compiler': afl_clang_result, + 'host_cxx_compiler': afl_clangxx_result, + }, environ={ + 'CC': 'afl-clang-fast', + 'CXX': 'afl-clang-fast++', + }) + + +class OSXCrossToolchainTest(BaseToolchainTest): + TARGET = 'i686-apple-darwin11.2.0' + PATHS = LinuxToolchainTest.PATHS + CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT + CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + + def test_osx_cross(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': self.CLANG_3_6_RESULT + { + 'flags': ['--target=i686-darwin11.2.0'], + }, + 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'flags': ['--target=i686-darwin11.2.0'], + }, + 'host_c_compiler': self.CLANG_3_6_RESULT, + 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + }, environ={ + 'CC': 'clang', + }) + + def test_cannot_osx_cross(self): + self.do_toolchain_test(self.PATHS, { + 'c_compiler': 'Target C compiler target kernel (Linux) does not ' + 'match --target kernel (Darwin)', + }, environ={ + 'CC': 'gcc', + }) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py b/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py new file mode 100644 index 000000000..8ec33a8b7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py @@ -0,0 +1,437 @@ +# 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 + +import copy +import re +import types +import unittest + +from fnmatch import fnmatch +from StringIO import StringIO +from textwrap import dedent + +from mozunit import ( + main, + MockedOpen, +) + +from mozbuild.preprocessor import Preprocessor +from mozbuild.util import ReadOnlyNamespace +from mozpack import path as mozpath + + +class CompilerPreprocessor(Preprocessor): + # The C preprocessor only expands macros when they are not in C strings. + # For now, we don't look very hard for C strings because they don't matter + # that much for our unit tests, but we at least avoid expanding in the + # simple "FOO" case. + VARSUBST = re.compile('(?<!")(?P<VAR>\w+)(?!")', re.U) + NON_WHITESPACE = re.compile('\S') + HAS_FEATURE = re.compile('(__has_feature)\(([^\)]*)\)') + + def __init__(self, *args, **kwargs): + Preprocessor.__init__(self, *args, **kwargs) + self.do_filter('c_substitution') + self.setMarker('#\s*') + + def do_if(self, expression, **kwargs): + # The C preprocessor handles numbers following C rules, which is a + # different handling than what our Preprocessor does out of the box. + # Hack around it enough that the configure tests work properly. + context = self.context + def normalize_numbers(value): + if isinstance(value, types.StringTypes): + if value[-1:] == 'L' and value[:-1].isdigit(): + value = int(value[:-1]) + return value + # Our Preprocessor doesn't handle macros with parameters, so we hack + # around that for __has_feature()-like things. + def normalize_has_feature(expr): + return self.HAS_FEATURE.sub(r'\1\2', expr) + self.context = self.Context( + (normalize_has_feature(k), normalize_numbers(v)) + for k, v in context.iteritems() + ) + try: + return Preprocessor.do_if(self, normalize_has_feature(expression), + **kwargs) + finally: + self.context = context + + class Context(dict): + def __missing__(self, key): + return None + + def filter_c_substitution(self, line): + def repl(matchobj): + varname = matchobj.group('VAR') + if varname in self.context: + result = str(self.context[varname]) + # The C preprocessor inserts whitespaces around expanded + # symbols. + start, end = matchobj.span('VAR') + if self.NON_WHITESPACE.match(line[start-1:start]): + result = ' ' + result + if self.NON_WHITESPACE.match(line[end:end+1]): + result = result + ' ' + return result + return matchobj.group(0) + return self.VARSUBST.sub(repl, line) + + +class TestCompilerPreprocessor(unittest.TestCase): + def test_expansion(self): + pp = CompilerPreprocessor({ + 'A': 1, + 'B': '2', + 'C': 'c', + 'D': 'd' + }) + pp.out = StringIO() + input = StringIO('A.B.C "D"') + input.name = 'foo' + pp.do_include(input) + + self.assertEquals(pp.out.getvalue(), '1 . 2 . c "D"') + + def test_condition(self): + pp = CompilerPreprocessor({ + 'A': 1, + 'B': '2', + 'C': '0L', + }) + pp.out = StringIO() + input = StringIO(dedent('''\ + #ifdef A + IFDEF_A + #endif + #if A + IF_A + #endif + # if B + IF_B + # else + IF_NOT_B + # endif + #if !C + IF_NOT_C + #else + IF_C + #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(dict): + '''Defines a fake compiler for use in toolchain tests below. + + 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`). + { + None: { '__GNUC__': 4, '__STDC__': 1, '__STRICT_ANSI__': 1 }, + '-std=gnu99': { '__STDC_VERSION__': '199901L', + '__STRICT_ANSI__': False }, + } + With the dict above, invoking the preprocessor with no additional flags + would define __GNUC__, __STDC__ and __STRICT_ANSI__, and with -std=gnu99, + __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, *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[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.iteritems(): + if glob and not glob.startswith('-') and fnmatch(file, glob): + apply_defn(defn) + + for flag in flags: + apply_defn(self.get(flag, {})) + + pp.out = StringIO() + pp.do_include(file) + return 0, pp.out.getvalue(), '' + elif '-c' in flags: + if '-funknown-flag' in flags: + return 1, '', '' + return 0, '', '' + + 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({ + 'A': '1', + 'B': '2', + }) + self.assertEquals(compiler(None, ['-E', 'file']), + (0, '1 2 C', '')) + + compiler = FakeCompiler({ + None: { + 'A': '1', + 'B': '2', + }, + '-foo': { + 'C': 'foo', + }, + '-bar': { + 'B': 'bar', + 'C': 'bar', + }, + '-qux': { + 'B': False, + }, + '*.c': { + 'B': '42', + }, + }) + self.assertEquals(compiler(None, ['-E', 'file']), + (0, '1 2 C', '')) + self.assertEquals(compiler(None, ['-E', '-foo', 'file']), + (0, '1 2 foo', '')) + self.assertEquals(compiler(None, ['-E', '-bar', 'file']), + (0, '1 bar bar', '')) + self.assertEquals(compiler(None, ['-E', '-qux', 'file']), + (0, '1 B C', '')) + self.assertEquals(compiler(None, ['-E', '-foo', '-bar', 'file']), + (0, '1 bar bar', '')) + self.assertEquals(compiler(None, ['-E', '-bar', '-foo', 'file']), + (0, '1 bar foo', '')) + self.assertEquals(compiler(None, ['-E', '-bar', '-qux', 'file']), + (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, + }, + }) + + +class CompilerResult(ReadOnlyNamespace): + '''Helper of convenience to manipulate toolchain results in unit tests + + When adding a dict, the result is a new CompilerResult with the values + from the dict replacing those from the CompilerResult, except for `flags`, + where the value from the dict extends the `flags` in `self`. + ''' + + def __init__(self, wrapper=None, compiler='', version='', type='', + language='', flags=None): + if flags is None: + flags = [] + if wrapper is None: + wrapper = [] + super(CompilerResult, self).__init__( + flags=flags, + version=version, + type=type, + compiler=mozpath.abspath(compiler), + wrapper=wrapper, + language=language, + ) + + def __add__(self, other): + assert isinstance(other, dict) + result = copy.deepcopy(self.__dict__) + for k, v in other.iteritems(): + if k == 'flags': + result.setdefault(k, []).extend(v) + else: + result[k] = v + return CompilerResult(**result) + + +class TestCompilerResult(unittest.TestCase): + def test_compiler_result(self): + result = CompilerResult() + self.assertEquals(result.__dict__, { + 'wrapper': [], + 'compiler': mozpath.abspath(''), + 'version': '', + 'type': '', + 'language': '', + 'flags': [], + }) + + result = CompilerResult( + compiler='/usr/bin/gcc', + version='4.2.1', + type='gcc', + language='C', + flags=['-std=gnu99'], + ) + self.assertEquals(result.__dict__, { + 'wrapper': [], + 'compiler': mozpath.abspath('/usr/bin/gcc'), + 'version': '4.2.1', + 'type': 'gcc', + 'language': 'C', + 'flags': ['-std=gnu99'], + }) + + result2 = result + {'flags': ['-m32']} + self.assertEquals(result2.__dict__, { + 'wrapper': [], + 'compiler': mozpath.abspath('/usr/bin/gcc'), + 'version': '4.2.1', + 'type': 'gcc', + 'language': 'C', + 'flags': ['-std=gnu99', '-m32'], + }) + # Original flags are untouched. + self.assertEquals(result.flags, ['-std=gnu99']) + + result3 = result + { + 'compiler': '/usr/bin/gcc-4.7', + 'version': '4.7.3', + 'flags': ['-m32'], + } + self.assertEquals(result3.__dict__, { + 'wrapper': [], + 'compiler': mozpath.abspath('/usr/bin/gcc-4.7'), + 'version': '4.7.3', + 'type': 'gcc', + 'language': 'C', + 'flags': ['-std=gnu99', '-m32'], + }) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py b/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py new file mode 100644 index 000000000..30dc022b7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py @@ -0,0 +1,67 @@ +# 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 + +import os + +from buildconfig import topsrcdir +from common import BaseConfigureTest +from mozunit import main + + +class TestToolkitMozConfigure(BaseConfigureTest): + def test_necko_protocols(self): + def get_value(arg): + sandbox = self.get_sandbox({}, {}, [arg]) + return sandbox._value_for(sandbox['necko_protocols']) + + default_protocols = get_value('') + self.assertNotEqual(default_protocols, ()) + + # Backwards compatibility + self.assertEqual(get_value('--enable-necko-protocols'), + default_protocols) + + self.assertEqual(get_value('--enable-necko-protocols=yes'), + default_protocols) + + self.assertEqual(get_value('--enable-necko-protocols=all'), + default_protocols) + + self.assertEqual(get_value('--enable-necko-protocols=default'), + default_protocols) + + self.assertEqual(get_value('--enable-necko-protocols='), ()) + + self.assertEqual(get_value('--enable-necko-protocols=no'), ()) + + self.assertEqual(get_value('--enable-necko-protocols=none'), ()) + + self.assertEqual(get_value('--disable-necko-protocols'), ()) + + self.assertEqual(get_value('--enable-necko-protocols=http'), + ('http',)) + + self.assertEqual(get_value('--enable-necko-protocols=http,about'), + ('about', 'http')) + + self.assertEqual(get_value('--enable-necko-protocols=http,none'), ()) + + self.assertEqual(get_value('--enable-necko-protocols=-http'), ()) + + self.assertEqual(get_value('--enable-necko-protocols=none,http'), + ('http',)) + + self.assertEqual( + get_value('--enable-necko-protocols=all,-http,-about'), + tuple(p for p in default_protocols if p not in ('http', 'about'))) + + self.assertEqual( + get_value('--enable-necko-protocols=default,-http,-about'), + tuple(p for p in default_protocols if p not in ('http', 'about'))) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/configure/test_util.py b/python/mozbuild/mozbuild/test/configure/test_util.py new file mode 100644 index 000000000..38b3c636e --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_util.py @@ -0,0 +1,558 @@ +# 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 + +import logging +import os +import tempfile +import textwrap +import unittest +import sys + +from StringIO import StringIO + +from mozunit import main +from mozpack import path as mozpath + +from mozbuild.configure.util import ( + ConfigureOutputHandler, + getpreferredencoding, + LineIO, + Version, +) + +from mozbuild.configure import ( + ConfigureSandbox, +) + +from mozbuild.util import exec_ + +from buildconfig import topsrcdir +from common import ConfigureTestSandbox + + +class TestConfigureOutputHandler(unittest.TestCase): + def test_separation(self): + out = StringIO() + err = StringIO() + name = '%s.test_separation' % self.__class__.__name__ + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + logger.addHandler(ConfigureOutputHandler(out, err)) + + logger.error('foo') + logger.warning('bar') + logger.info('baz') + # DEBUG level is not printed out by this handler + logger.debug('qux') + + self.assertEqual(out.getvalue(), 'baz\n') + self.assertEqual(err.getvalue(), 'foo\nbar\n') + + def test_format(self): + out = StringIO() + err = StringIO() + name = '%s.test_format' % self.__class__.__name__ + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + handler = ConfigureOutputHandler(out, err) + handler.setFormatter(logging.Formatter('%(levelname)s:%(message)s')) + logger.addHandler(handler) + + logger.error('foo') + logger.warning('bar') + logger.info('baz') + # DEBUG level is not printed out by this handler + logger.debug('qux') + + self.assertEqual(out.getvalue(), 'baz\n') + self.assertEqual( + err.getvalue(), + 'ERROR:foo\n' + 'WARNING:bar\n' + ) + + def test_continuation(self): + out = StringIO() + name = '%s.test_continuation' % self.__class__.__name__ + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + handler = ConfigureOutputHandler(out, out) + handler.setFormatter(logging.Formatter('%(levelname)s:%(message)s')) + logger.addHandler(handler) + + logger.info('foo') + logger.info('checking bar... ') + logger.info('yes') + logger.info('qux') + + self.assertEqual( + out.getvalue(), + 'foo\n' + 'checking bar... yes\n' + 'qux\n' + ) + + out.seek(0) + out.truncate() + + logger.info('foo') + logger.info('checking bar... ') + logger.warning('hoge') + logger.info('no') + logger.info('qux') + + self.assertEqual( + out.getvalue(), + 'foo\n' + 'checking bar... \n' + 'WARNING:hoge\n' + ' ... no\n' + 'qux\n' + ) + + out.seek(0) + out.truncate() + + logger.info('foo') + logger.info('checking bar... ') + logger.warning('hoge') + logger.warning('fuga') + logger.info('no') + logger.info('qux') + + self.assertEqual( + out.getvalue(), + 'foo\n' + 'checking bar... \n' + 'WARNING:hoge\n' + 'WARNING:fuga\n' + ' ... no\n' + 'qux\n' + ) + + out.seek(0) + out.truncate() + err = StringIO() + + logger.removeHandler(handler) + handler = ConfigureOutputHandler(out, err) + handler.setFormatter(logging.Formatter('%(levelname)s:%(message)s')) + logger.addHandler(handler) + + logger.info('foo') + logger.info('checking bar... ') + logger.warning('hoge') + logger.warning('fuga') + logger.info('no') + logger.info('qux') + + self.assertEqual( + out.getvalue(), + 'foo\n' + 'checking bar... no\n' + 'qux\n' + ) + + self.assertEqual( + err.getvalue(), + 'WARNING:hoge\n' + 'WARNING:fuga\n' + ) + + def test_queue_debug(self): + out = StringIO() + name = '%s.test_queue_debug' % self.__class__.__name__ + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + handler = ConfigureOutputHandler(out, out, maxlen=3) + handler.setFormatter(logging.Formatter('%(levelname)s:%(message)s')) + logger.addHandler(handler) + + with handler.queue_debug(): + logger.info('checking bar... ') + logger.debug('do foo') + logger.info('yes') + logger.info('qux') + + self.assertEqual( + out.getvalue(), + 'checking bar... yes\n' + 'qux\n' + ) + + out.seek(0) + out.truncate() + + with handler.queue_debug(): + logger.info('checking bar... ') + logger.debug('do foo') + logger.info('no') + logger.error('fail') + + self.assertEqual( + out.getvalue(), + 'checking bar... no\n' + 'DEBUG:do foo\n' + 'ERROR:fail\n' + ) + + out.seek(0) + out.truncate() + + with handler.queue_debug(): + logger.info('checking bar... ') + logger.debug('do foo') + logger.debug('do bar') + logger.debug('do baz') + logger.info('no') + logger.error('fail') + + self.assertEqual( + out.getvalue(), + 'checking bar... no\n' + 'DEBUG:do foo\n' + 'DEBUG:do bar\n' + 'DEBUG:do baz\n' + 'ERROR:fail\n' + ) + + out.seek(0) + out.truncate() + + with handler.queue_debug(): + logger.info('checking bar... ') + logger.debug('do foo') + logger.debug('do bar') + logger.debug('do baz') + logger.debug('do qux') + logger.debug('do hoge') + logger.info('no') + logger.error('fail') + + self.assertEqual( + out.getvalue(), + 'checking bar... no\n' + 'DEBUG:<truncated - see config.log for full output>\n' + 'DEBUG:do baz\n' + 'DEBUG:do qux\n' + 'DEBUG:do hoge\n' + 'ERROR:fail\n' + ) + + out.seek(0) + out.truncate() + + try: + with handler.queue_debug(): + logger.info('checking bar... ') + logger.debug('do foo') + logger.debug('do bar') + logger.info('no') + e = Exception('fail') + raise e + except Exception as caught: + self.assertIs(caught, e) + + self.assertEqual( + out.getvalue(), + 'checking bar... no\n' + 'DEBUG:do foo\n' + 'DEBUG:do bar\n' + ) + + def test_queue_debug_reentrant(self): + out = StringIO() + name = '%s.test_queue_debug_reentrant' % self.__class__.__name__ + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + handler = ConfigureOutputHandler(out, out, maxlen=10) + handler.setFormatter(logging.Formatter('%(levelname)s| %(message)s')) + logger.addHandler(handler) + + try: + with handler.queue_debug(): + logger.info('outer info') + logger.debug('outer debug') + with handler.queue_debug(): + logger.info('inner info') + logger.debug('inner debug') + e = Exception('inner exception') + raise e + except Exception as caught: + self.assertIs(caught, e) + + self.assertEqual(out.getvalue(), + 'outer info\n' + 'inner info\n' + 'DEBUG| outer debug\n' + 'DEBUG| inner debug\n') + + out.seek(0) + out.truncate() + + try: + with handler.queue_debug(): + logger.info('outer info') + logger.debug('outer debug') + with handler.queue_debug(): + logger.info('inner info') + logger.debug('inner debug') + e = Exception('outer exception') + raise e + except Exception as caught: + self.assertIs(caught, e) + + self.assertEqual(out.getvalue(), + 'outer info\n' + 'inner info\n' + 'DEBUG| outer debug\n' + 'DEBUG| inner debug\n') + + out.seek(0) + out.truncate() + + with handler.queue_debug(): + logger.info('outer info') + logger.debug('outer debug') + with handler.queue_debug(): + logger.info('inner info') + logger.debug('inner debug') + logger.error('inner error') + self.assertEqual(out.getvalue(), + 'outer info\n' + 'inner info\n' + 'DEBUG| outer debug\n' + 'DEBUG| inner debug\n' + 'ERROR| inner error\n') + + out.seek(0) + out.truncate() + + with handler.queue_debug(): + logger.info('outer info') + logger.debug('outer debug') + with handler.queue_debug(): + logger.info('inner info') + logger.debug('inner debug') + logger.error('outer error') + self.assertEqual(out.getvalue(), + 'outer info\n' + 'inner info\n' + 'DEBUG| outer debug\n' + 'DEBUG| inner debug\n' + 'ERROR| outer error\n') + + def test_is_same_output(self): + fd1 = sys.stderr.fileno() + fd2 = os.dup(fd1) + try: + self.assertTrue(ConfigureOutputHandler._is_same_output(fd1, fd2)) + finally: + os.close(fd2) + + fd2, path = tempfile.mkstemp() + try: + self.assertFalse(ConfigureOutputHandler._is_same_output(fd1, fd2)) + + fd3 = os.dup(fd2) + try: + self.assertTrue(ConfigureOutputHandler._is_same_output(fd2, fd3)) + finally: + os.close(fd3) + + with open(path, 'a') as fh: + fd3 = fh.fileno() + self.assertTrue( + ConfigureOutputHandler._is_same_output(fd2, fd3)) + + finally: + os.close(fd2) + os.remove(path) + + +class TestLineIO(unittest.TestCase): + def test_lineio(self): + lines = [] + l = LineIO(lambda l: lines.append(l)) + + l.write('a') + self.assertEqual(lines, []) + + l.write('b') + self.assertEqual(lines, []) + + l.write('\n') + self.assertEqual(lines, ['ab']) + + l.write('cdef') + self.assertEqual(lines, ['ab']) + + l.write('\n') + self.assertEqual(lines, ['ab', 'cdef']) + + l.write('ghi\njklm') + self.assertEqual(lines, ['ab', 'cdef', 'ghi']) + + l.write('nop\nqrst\nuv\n') + self.assertEqual(lines, ['ab', 'cdef', 'ghi', 'jklmnop', 'qrst', 'uv']) + + l.write('wx\nyz') + self.assertEqual(lines, ['ab', 'cdef', 'ghi', 'jklmnop', 'qrst', 'uv', + 'wx']) + + l.close() + self.assertEqual(lines, ['ab', 'cdef', 'ghi', 'jklmnop', 'qrst', 'uv', + 'wx', 'yz']) + + def test_lineio_contextmanager(self): + lines = [] + with LineIO(lambda l: lines.append(l)) as l: + l.write('a\nb\nc') + + self.assertEqual(lines, ['a', 'b']) + + self.assertEqual(lines, ['a', 'b', 'c']) + + +class TestLogSubprocessOutput(unittest.TestCase): + + def test_non_ascii_subprocess_output(self): + out = StringIO() + sandbox = ConfigureSandbox({}, {}, [], out, out) + + sandbox.include_file(mozpath.join(topsrcdir, 'build', + 'moz.configure', 'util.configure')) + sandbox.include_file(mozpath.join(topsrcdir, 'python', 'mozbuild', + 'mozbuild', 'test', 'configure', + 'data', 'subprocess.configure')) + status = 0 + try: + sandbox.run() + except SystemExit as e: + status = e.code + + self.assertEquals(status, 0) + quote_char = "'" + if getpreferredencoding().lower() == 'utf-8': + quote_char = '\u00B4'.encode('utf-8') + self.assertEquals(out.getvalue().strip(), quote_char) + + +class TestVersion(unittest.TestCase): + def test_version_simple(self): + v = Version('1') + self.assertEqual(v, '1') + self.assertLess(v, '2') + self.assertGreater(v, '0.5') + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + + def test_version_more(self): + v = Version('1.2.3b') + self.assertLess(v, '2') + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 2) + self.assertEqual(v.patch, 3) + + def test_version_bad(self): + # A version with a letter in the middle doesn't really make sense, + # so everything after it should be ignored. + v = Version('1.2b.3') + self.assertLess(v, '2') + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 2) + self.assertEqual(v.patch, 0) + + def test_version_badder(self): + v = Version('1b.2.3') + self.assertLess(v, '2') + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + +class TestCheckCmdOutput(unittest.TestCase): + + def get_result(self, command='', paths=None): + paths = paths or {} + config = {} + out = StringIO() + sandbox = ConfigureTestSandbox(paths, config, {}, ['/bin/configure'], + out, out) + sandbox.include_file(mozpath.join(topsrcdir, 'build', + 'moz.configure', 'util.configure')) + status = 0 + try: + exec_(command, sandbox) + sandbox.run() + except SystemExit as e: + status = e.code + return config, out.getvalue(), status + + def test_simple_program(self): + def mock_simple_prog(_, args): + if len(args) == 1 and args[0] == '--help': + return 0, 'simple program help...', '' + self.fail("Unexpected arguments to mock_simple_program: %s" % + args) + prog_path = mozpath.abspath('/simple/prog') + cmd = "log.info(check_cmd_output('%s', '--help'))" % prog_path + config, out, status = self.get_result(cmd, + paths={prog_path: mock_simple_prog}) + self.assertEqual(config, {}) + self.assertEqual(status, 0) + self.assertEqual(out, 'simple program help...\n') + + def test_failing_program(self): + def mock_error_prog(_, args): + if len(args) == 1 and args[0] == '--error': + return (127, 'simple program output', + 'simple program error output') + self.fail("Unexpected arguments to mock_error_program: %s" % + args) + prog_path = mozpath.abspath('/simple/prog') + cmd = "log.info(check_cmd_output('%s', '--error'))" % prog_path + config, out, status = self.get_result(cmd, + paths={prog_path: mock_error_prog}) + self.assertEqual(config, {}) + self.assertEqual(status, 1) + self.assertEqual(out, textwrap.dedent('''\ + DEBUG: Executing: `%s --error` + DEBUG: The command returned non-zero exit status 127. + DEBUG: Its output was: + DEBUG: | simple program output + DEBUG: Its error output was: + DEBUG: | simple program error output + ERROR: Command `%s --error` failed with exit status 127. + ''' % (prog_path, prog_path))) + + def test_error_callback(self): + def mock_error_prog(_, args): + if len(args) == 1 and args[0] == '--error': + return 127, 'simple program error...', '' + self.fail("Unexpected arguments to mock_error_program: %s" % + args) + + prog_path = mozpath.abspath('/simple/prog') + cmd = textwrap.dedent('''\ + check_cmd_output('%s', '--error', + onerror=lambda: die('`prog` produced an error')) + ''' % prog_path) + config, out, status = self.get_result(cmd, + paths={prog_path: mock_error_prog}) + self.assertEqual(config, {}) + self.assertEqual(status, 1) + self.assertEqual(out, textwrap.dedent('''\ + DEBUG: Executing: `%s --error` + DEBUG: The command returned non-zero exit status 127. + DEBUG: Its output was: + DEBUG: | simple program error... + ERROR: `prog` produced an error + ''' % prog_path)) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/controller/__init__.py b/python/mozbuild/mozbuild/test/controller/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/controller/__init__.py diff --git a/python/mozbuild/mozbuild/test/controller/test_ccachestats.py b/python/mozbuild/mozbuild/test/controller/test_ccachestats.py new file mode 100644 index 000000000..7a6608ec8 --- /dev/null +++ b/python/mozbuild/mozbuild/test/controller/test_ccachestats.py @@ -0,0 +1,208 @@ +# 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 unittest + +from mozunit import main + +from mozbuild.controller.building import CCacheStats + + +class TestCcacheStats(unittest.TestCase): + STAT_GARBAGE = """A garbage line which should be failed to parse""" + + STAT0 = """ + cache directory /home/tlin/.ccache + cache hit (direct) 0 + cache hit (preprocessed) 0 + cache miss 0 + files in cache 0 + cache size 0 Kbytes + max cache size 16.0 Gbytes""" + + STAT1 = """ + cache directory /home/tlin/.ccache + cache hit (direct) 100 + cache hit (preprocessed) 200 + cache miss 2500 + called for link 180 + called for preprocessing 6 + compile failed 11 + preprocessor error 3 + bad compiler arguments 6 + unsupported source language 9 + autoconf compile/link 60 + unsupported compiler option 2 + no input file 21 + files in cache 7344 + cache size 1.9 Gbytes + max cache size 16.0 Gbytes""" + + STAT2 = """ + cache directory /home/tlin/.ccache + cache hit (direct) 1900 + cache hit (preprocessed) 300 + cache miss 2600 + called for link 361 + called for preprocessing 12 + compile failed 22 + preprocessor error 6 + bad compiler arguments 12 + unsupported source language 18 + autoconf compile/link 120 + unsupported compiler option 4 + no input file 48 + files in cache 7392 + cache size 2.0 Gbytes + max cache size 16.0 Gbytes""" + + STAT3 = """ + cache directory /Users/tlin/.ccache + primary config /Users/tlin/.ccache/ccache.conf + secondary config (readonly) /usr/local/Cellar/ccache/3.2/etc/ccache.conf + cache hit (direct) 12004 + cache hit (preprocessed) 1786 + cache miss 26348 + called for link 2338 + called for preprocessing 6313 + compile failed 399 + preprocessor error 390 + bad compiler arguments 86 + unsupported source language 66 + autoconf compile/link 2439 + unsupported compiler option 187 + no input file 1068 + files in cache 18044 + cache size 7.5 GB + max cache size 8.6 GB + """ + + STAT4 = """ + cache directory /Users/tlin/.ccache + primary config /Users/tlin/.ccache/ccache.conf + secondary config (readonly) /usr/local/Cellar/ccache/3.2.1/etc/ccache.conf + cache hit (direct) 21039 + cache hit (preprocessed) 2315 + cache miss 39370 + called for link 3651 + called for preprocessing 6693 + compile failed 723 + ccache internal error 1 + preprocessor error 588 + bad compiler arguments 128 + unsupported source language 99 + autoconf compile/link 3669 + unsupported compiler option 187 + no input file 1711 + files in cache 18313 + cache size 6.3 GB + max cache size 6.0 GB + """ + + STAT5 = """ + cache directory /Users/tlin/.ccache + primary config /Users/tlin/.ccache/ccache.conf + secondary config (readonly) /usr/local/Cellar/ccache/3.2.1/etc/ccache.conf + cache hit (direct) 21039 + cache hit (preprocessed) 2315 + cache miss 39372 + called for link 3653 + called for preprocessing 6693 + compile failed 723 + ccache internal error 1 + preprocessor error 588 + bad compiler arguments 128 + unsupported source language 99 + autoconf compile/link 3669 + unsupported compiler option 187 + no input file 1711 + files in cache 17411 + cache size 6.0 GB + max cache size 6.0 GB + """ + + STAT6 = """ + cache directory /Users/tlin/.ccache + primary config /Users/tlin/.ccache/ccache.conf + secondary config (readonly) /usr/local/Cellar/ccache/3.3.2/etc/ccache.conf + cache hit (direct) 319287 + cache hit (preprocessed) 125987 + cache miss 749959 + cache hit rate 37.25 % + called for link 87978 + called for preprocessing 418591 + multiple source files 1861 + compiler produced no output 122 + compiler produced empty output 174 + compile failed 14330 + ccache internal error 1 + preprocessor error 9459 + can't use precompiled header 4 + bad compiler arguments 2077 + unsupported source language 18195 + autoconf compile/link 51485 + unsupported compiler option 322 + no input file 309538 + cleanups performed 1 + files in cache 17358 + cache size 15.4 GB + max cache size 17.2 GB + """ + + def test_parse_garbage_stats_message(self): + self.assertRaises(ValueError, CCacheStats, self.STAT_GARBAGE) + + def test_parse_zero_stats_message(self): + stats = CCacheStats(self.STAT0) + self.assertEqual(stats.cache_dir, "/home/tlin/.ccache") + self.assertEqual(stats.hit_rates(), (0, 0, 0)) + + def test_hit_rate_of_diff_stats(self): + stats1 = CCacheStats(self.STAT1) + stats2 = CCacheStats(self.STAT2) + stats_diff = stats2 - stats1 + self.assertEqual(stats_diff.hit_rates(), (0.9, 0.05, 0.05)) + + def test_stats_contains_data(self): + stats0 = CCacheStats(self.STAT0) + stats1 = CCacheStats(self.STAT1) + stats2 = CCacheStats(self.STAT2) + stats_diff_zero = stats1 - stats1 + stats_diff_negative1 = stats0 - stats1 + stats_diff_negative2 = stats1 - stats2 + + self.assertFalse(stats0) + self.assertTrue(stats1) + self.assertTrue(stats2) + self.assertFalse(stats_diff_zero) + self.assertFalse(stats_diff_negative1) + self.assertFalse(stats_diff_negative2) + + def test_stats_version32(self): + stat2 = CCacheStats(self.STAT2) + stat3 = CCacheStats(self.STAT3) + stats_diff = stat3 - stat2 + self.assertTrue(stat3) + self.assertTrue(stats_diff) + + def test_cache_size_shrinking(self): + stat4 = CCacheStats(self.STAT4) + stat5 = CCacheStats(self.STAT5) + stats_diff = stat5 - stat4 + self.assertTrue(stat4) + self.assertTrue(stat5) + self.assertTrue(stats_diff) + + def test_stats_version33(self): + stat3 = CCacheStats(self.STAT3) + stat6 = CCacheStats(self.STAT6) + stats_diff = stat6 - stat3 + self.assertTrue(stat6) + self.assertTrue(stat3) + self.assertTrue(stats_diff) + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/controller/test_clobber.py b/python/mozbuild/mozbuild/test/controller/test_clobber.py new file mode 100644 index 000000000..997f467ec --- /dev/null +++ b/python/mozbuild/mozbuild/test/controller/test_clobber.py @@ -0,0 +1,213 @@ +# 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 shutil +import tempfile +import unittest + +from StringIO import StringIO + +from mozunit import main + +from mozbuild.controller.clobber import Clobberer +from mozbuild.controller.clobber import main as clobber + + +class TestClobberer(unittest.TestCase): + def setUp(self): + self._temp_dirs = [] + + return unittest.TestCase.setUp(self) + + def tearDown(self): + for d in self._temp_dirs: + shutil.rmtree(d, ignore_errors=True) + + return unittest.TestCase.tearDown(self) + + def get_tempdir(self): + t = tempfile.mkdtemp() + self._temp_dirs.append(t) + return t + + def get_topsrcdir(self): + t = self.get_tempdir() + p = os.path.join(t, 'CLOBBER') + with open(p, 'a'): + pass + + return t + + def test_no_objdir(self): + """If topobjdir does not exist, no clobber is needed.""" + + tmp = os.path.join(self.get_tempdir(), 'topobjdir') + self.assertFalse(os.path.exists(tmp)) + + c = Clobberer(self.get_topsrcdir(), tmp) + self.assertFalse(c.clobber_needed()) + + # Side-effect is topobjdir is created with CLOBBER file touched. + required, performed, reason = c.maybe_do_clobber(os.getcwd(), True) + self.assertFalse(required) + self.assertFalse(performed) + self.assertIsNone(reason) + + self.assertTrue(os.path.isdir(tmp)) + self.assertTrue(os.path.exists(os.path.join(tmp, 'CLOBBER'))) + + def test_objdir_no_clobber_file(self): + """If CLOBBER does not exist in topobjdir, treat as empty.""" + + c = Clobberer(self.get_topsrcdir(), self.get_tempdir()) + self.assertFalse(c.clobber_needed()) + + required, performed, reason = c.maybe_do_clobber(os.getcwd(), True) + self.assertFalse(required) + self.assertFalse(performed) + self.assertIsNone(reason) + + self.assertTrue(os.path.exists(os.path.join(c.topobjdir, 'CLOBBER'))) + + def test_objdir_clobber_newer(self): + """If CLOBBER in topobjdir is newer, do nothing.""" + + c = Clobberer(self.get_topsrcdir(), self.get_tempdir()) + with open(c.obj_clobber, 'a'): + pass + + required, performed, reason = c.maybe_do_clobber(os.getcwd(), True) + self.assertFalse(required) + self.assertFalse(performed) + self.assertIsNone(reason) + + def test_objdir_clobber_older(self): + """If CLOBBER in topobjdir is older, we clobber.""" + + c = Clobberer(self.get_topsrcdir(), self.get_tempdir()) + with open(c.obj_clobber, 'a'): + pass + + dummy_path = os.path.join(c.topobjdir, 'foo') + with open(dummy_path, 'a'): + pass + + self.assertTrue(os.path.exists(dummy_path)) + + old_time = os.path.getmtime(c.src_clobber) - 60 + os.utime(c.obj_clobber, (old_time, old_time)) + + self.assertTrue(c.clobber_needed()) + + required, performed, reason = c.maybe_do_clobber(os.getcwd(), True) + self.assertTrue(required) + self.assertTrue(performed) + + self.assertFalse(os.path.exists(dummy_path)) + self.assertTrue(os.path.exists(c.obj_clobber)) + self.assertGreaterEqual(os.path.getmtime(c.obj_clobber), + os.path.getmtime(c.src_clobber)) + + def test_objdir_is_srcdir(self): + """If topobjdir is the topsrcdir, refuse to clobber.""" + + tmp = self.get_topsrcdir() + c = Clobberer(tmp, tmp) + + self.assertFalse(c.clobber_needed()) + + def test_cwd_is_topobjdir(self): + """If cwd is topobjdir, we can still clobber.""" + c = Clobberer(self.get_topsrcdir(), self.get_tempdir()) + + with open(c.obj_clobber, 'a'): + pass + + dummy_file = os.path.join(c.topobjdir, 'dummy_file') + with open(dummy_file, 'a'): + pass + + dummy_dir = os.path.join(c.topobjdir, 'dummy_dir') + os.mkdir(dummy_dir) + + self.assertTrue(os.path.exists(dummy_file)) + self.assertTrue(os.path.isdir(dummy_dir)) + + old_time = os.path.getmtime(c.src_clobber) - 60 + os.utime(c.obj_clobber, (old_time, old_time)) + + self.assertTrue(c.clobber_needed()) + + required, performed, reason = c.maybe_do_clobber(c.topobjdir, True) + self.assertTrue(required) + self.assertTrue(performed) + + self.assertFalse(os.path.exists(dummy_file)) + self.assertFalse(os.path.exists(dummy_dir)) + + def test_cwd_under_topobjdir(self): + """If cwd is under topobjdir, we can't clobber.""" + + c = Clobberer(self.get_topsrcdir(), self.get_tempdir()) + + with open(c.obj_clobber, 'a'): + pass + + old_time = os.path.getmtime(c.src_clobber) - 60 + os.utime(c.obj_clobber, (old_time, old_time)) + + d = os.path.join(c.topobjdir, 'dummy_dir') + os.mkdir(d) + + required, performed, reason = c.maybe_do_clobber(d, True) + self.assertTrue(required) + self.assertFalse(performed) + self.assertIn('Cannot clobber while the shell is inside', reason) + + + def test_mozconfig_opt_in(self): + """Auto clobber iff AUTOCLOBBER is in the environment.""" + + topsrcdir = self.get_topsrcdir() + topobjdir = self.get_tempdir() + + obj_clobber = os.path.join(topobjdir, 'CLOBBER') + with open(obj_clobber, 'a'): + pass + + dummy_file = os.path.join(topobjdir, 'dummy_file') + with open(dummy_file, 'a'): + pass + + self.assertTrue(os.path.exists(dummy_file)) + + old_time = os.path.getmtime(os.path.join(topsrcdir, 'CLOBBER')) - 60 + os.utime(obj_clobber, (old_time, old_time)) + + # Check auto clobber is off by default + env = dict(os.environ) + if env.get('AUTOCLOBBER', False): + del env['AUTOCLOBBER'] + + s = StringIO() + status = clobber([topsrcdir, topobjdir], env, os.getcwd(), s) + self.assertEqual(status, 1) + self.assertIn('Automatic clobbering is not enabled', s.getvalue()) + self.assertTrue(os.path.exists(dummy_file)) + + # Check auto clobber opt-in works + env['AUTOCLOBBER'] = '1' + + s = StringIO() + status = clobber([topsrcdir, topobjdir], env, os.getcwd(), s) + self.assertEqual(status, 0) + self.assertIn('Successfully completed auto clobber', s.getvalue()) + self.assertFalse(os.path.exists(dummy_file)) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/data/Makefile b/python/mozbuild/mozbuild/test/data/Makefile new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/data/Makefile diff --git a/python/mozbuild/mozbuild/test/data/bad.properties b/python/mozbuild/mozbuild/test/data/bad.properties new file mode 100644 index 000000000..d4d8109b6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/data/bad.properties @@ -0,0 +1,12 @@ +# A region.properties file with invalid unicode byte sequences. The +# sequences were cribbed from Markus Kuhn's "UTF-8 decoder capability +# and stress test", available at +# http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + +# 3.5 Impossible bytes | +# | +# The following two bytes cannot appear in a correct UTF-8 string | +# | +# 3.5.1 fe = "þ" | +# 3.5.2 ff = "ÿ" | +# 3.5.3 fe fe ff ff = "þþÿÿ" | diff --git a/python/mozbuild/mozbuild/test/data/test-dir/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/Makefile new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/data/test-dir/Makefile diff --git a/python/mozbuild/mozbuild/test/data/test-dir/with/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/with/Makefile new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/data/test-dir/with/Makefile diff --git a/python/mozbuild/mozbuild/test/data/test-dir/with/without/with/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/with/without/with/Makefile new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/data/test-dir/with/without/with/Makefile diff --git a/python/mozbuild/mozbuild/test/data/test-dir/without/with/Makefile b/python/mozbuild/mozbuild/test/data/test-dir/without/with/Makefile new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/data/test-dir/without/with/Makefile diff --git a/python/mozbuild/mozbuild/test/data/valid.properties b/python/mozbuild/mozbuild/test/data/valid.properties new file mode 100644 index 000000000..db64bf2ee --- /dev/null +++ b/python/mozbuild/mozbuild/test/data/valid.properties @@ -0,0 +1,11 @@ +# A region.properties file with unicode characters. + +# Danish. +# #### ~~ Søren Munk Skrøder, sskroeder - 2009-05-30 @ #mozmae + +# Korean. +A.title=í•œë©”ì¼ + +# Russian. +list.0 = test +list.1 = Ð¯Ð½Ð´ÐµÐºÑ diff --git a/python/mozbuild/mozbuild/test/frontend/__init__.py b/python/mozbuild/mozbuild/test/frontend/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/__init__.py diff --git a/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/dir1/foo b/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/dir1/foo new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/dir1/foo diff --git a/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/moz.build b/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/moz.build new file mode 100644 index 000000000..242a3628d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +ANDROID_RES_DIRS += [ + '/dir1', + '!/dir2', + '%/dir3', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/binary-components/bar/moz.build b/python/mozbuild/mozbuild/test/frontend/data/binary-components/bar/moz.build new file mode 100644 index 000000000..2946e42aa --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/binary-components/bar/moz.build @@ -0,0 +1,2 @@ +Component('bar') +NO_COMPONENTS_MANIFEST = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/binary-components/foo/moz.build b/python/mozbuild/mozbuild/test/frontend/data/binary-components/foo/moz.build new file mode 100644 index 000000000..8611a74be --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/binary-components/foo/moz.build @@ -0,0 +1 @@ +Component('foo') diff --git a/python/mozbuild/mozbuild/test/frontend/data/binary-components/moz.build b/python/mozbuild/mozbuild/test/frontend/data/binary-components/moz.build new file mode 100644 index 000000000..1776d0514 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/binary-components/moz.build @@ -0,0 +1,10 @@ +@template +def Component(name): + LIBRARY_NAME = name + FORCE_SHARED_LIB = True + IS_COMPONENT = True + +DIRS += [ + 'foo', + 'bar', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/branding-files/bar.ico b/python/mozbuild/mozbuild/test/frontend/data/branding-files/bar.ico new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/branding-files/bar.ico diff --git a/python/mozbuild/mozbuild/test/frontend/data/branding-files/baz.png b/python/mozbuild/mozbuild/test/frontend/data/branding-files/baz.png new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/branding-files/baz.png diff --git a/python/mozbuild/mozbuild/test/frontend/data/branding-files/foo.xpm b/python/mozbuild/mozbuild/test/frontend/data/branding-files/foo.xpm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/branding-files/foo.xpm diff --git a/python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build b/python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build new file mode 100644 index 000000000..251bc53ea --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build @@ -0,0 +1,13 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +BRANDING_FILES += [ + 'bar.ico', + 'baz.png', + 'foo.xpm', +] + +BRANDING_FILES.icons += [ + 'quux.icns', +] + diff --git a/python/mozbuild/mozbuild/test/frontend/data/branding-files/quux.icns b/python/mozbuild/mozbuild/test/frontend/data/branding-files/quux.icns new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/branding-files/quux.icns diff --git a/python/mozbuild/mozbuild/test/frontend/data/config-file-substitution/moz.build b/python/mozbuild/mozbuild/test/frontend/data/config-file-substitution/moz.build new file mode 100644 index 000000000..f53dd9454 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/config-file-substitution/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +CONFIGURE_SUBST_FILES += ['foo'] +CONFIGURE_SUBST_FILES += ['bar'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/Cargo.toml new file mode 100644 index 000000000..99d10b1a6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "random-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[lib] +crate-type = ["staticlib"] + +[dependencies] +deep-crate = { version = "0.1.0", path = "the/depths" } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/moz.build b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/moz.build new file mode 100644 index 000000000..01b3a35a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate') diff --git a/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/shallow/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/shallow/Cargo.toml new file mode 100644 index 000000000..c347f8c08 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/shallow/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "shallow-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/the/depths/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/the/depths/Cargo.toml new file mode 100644 index 000000000..10a4ded0a --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/crate-dependency-path-resolution/the/depths/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "deep-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[dependencies] +shallow-crate = { path = "../../shallow" } diff --git a/python/mozbuild/mozbuild/test/frontend/data/defines/moz.build b/python/mozbuild/mozbuild/test/frontend/data/defines/moz.build new file mode 100644 index 000000000..ccb0d5e36 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/defines/moz.build @@ -0,0 +1,14 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +value = 'xyz' +DEFINES = { + 'FOO': True, +} + +DEFINES['BAZ'] = '"abcd"' +DEFINES.update({ + 'BAR': 7, + 'VALUE': value, + 'QUX': False, +}) diff --git a/python/mozbuild/mozbuild/test/frontend/data/dist-files-missing/install.rdf b/python/mozbuild/mozbuild/test/frontend/data/dist-files-missing/install.rdf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/dist-files-missing/install.rdf diff --git a/python/mozbuild/mozbuild/test/frontend/data/dist-files-missing/moz.build b/python/mozbuild/mozbuild/test/frontend/data/dist-files-missing/moz.build new file mode 100644 index 000000000..cbd2c942b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/dist-files-missing/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FINAL_TARGET_PP_FILES += [ + 'install.rdf', + 'main.js', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/dist-files/install.rdf b/python/mozbuild/mozbuild/test/frontend/data/dist-files/install.rdf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/dist-files/install.rdf diff --git a/python/mozbuild/mozbuild/test/frontend/data/dist-files/main.js b/python/mozbuild/mozbuild/test/frontend/data/dist-files/main.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/dist-files/main.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/dist-files/moz.build b/python/mozbuild/mozbuild/test/frontend/data/dist-files/moz.build new file mode 100644 index 000000000..cbd2c942b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/dist-files/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FINAL_TARGET_PP_FILES += [ + 'install.rdf', + 'main.js', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-generated/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/foo.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/foo.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-generated/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/moz.build new file mode 100644 index 000000000..259d96fcd --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/moz.build @@ -0,0 +1,8 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS.mozilla += ['mozilla1.h'] +EXPORTS.mozilla += ['!mozilla2.h'] + +GENERATED_FILES += ['mozilla2.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-generated/mozilla1.h b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/mozilla1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/mozilla1.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/foo.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/foo.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/moz.build new file mode 100644 index 000000000..e0dfce264 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS += ['!bar.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/foo.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/foo.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/moz.build new file mode 100644 index 000000000..e1f93aab5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/moz.build @@ -0,0 +1,6 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS.mozilla += ['mozilla1.h'] +EXPORTS.mozilla += ['mozilla2.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing/mozilla1.h b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/mozilla1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/mozilla1.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/bar.h b/python/mozbuild/mozbuild/test/frontend/data/exports/bar.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/bar.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/baz.h b/python/mozbuild/mozbuild/test/frontend/data/exports/baz.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/baz.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/dom1.h b/python/mozbuild/mozbuild/test/frontend/data/exports/dom1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/dom1.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/dom2.h b/python/mozbuild/mozbuild/test/frontend/data/exports/dom2.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/dom2.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/dom3.h b/python/mozbuild/mozbuild/test/frontend/data/exports/dom3.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/dom3.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports/foo.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/foo.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/gfx.h b/python/mozbuild/mozbuild/test/frontend/data/exports/gfx.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/gfx.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mem.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mem.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/mem.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mem2.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mem2.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/mem2.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports/moz.build new file mode 100644 index 000000000..666fbeb81 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/moz.build @@ -0,0 +1,13 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS += ['bar.h', 'baz.h'] +EXPORTS.mozilla += ['mozilla1.h'] +EXPORTS.mozilla += ['mozilla2.h'] +EXPORTS.mozilla.dom += ['dom1.h'] +EXPORTS.mozilla.dom += ['dom2.h', 'dom3.h'] +EXPORTS.mozilla.gfx += ['gfx.h'] +EXPORTS.vpx = ['mem.h'] +EXPORTS.vpx += ['mem2.h'] +EXPORTS.nspr.private = ['pprio.h', 'pprthred.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla1.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla1.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla2.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla2.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla2.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/pprio.h b/python/mozbuild/mozbuild/test/frontend/data/exports/pprio.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/pprio.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/pprthred.h b/python/mozbuild/mozbuild/test/frontend/data/exports/pprthred.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/pprthred.h diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/bad-assignment/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/bad-assignment/moz.build new file mode 100644 index 000000000..d6a9799b8 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/bad-assignment/moz.build @@ -0,0 +1,2 @@ +with Files('*'): + BUG_COMPONENT = 'bad value' diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/different-matchers/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/different-matchers/moz.build new file mode 100644 index 000000000..990453f7c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/different-matchers/moz.build @@ -0,0 +1,4 @@ +with Files('*.jsm'): + BUG_COMPONENT = ('Firefox', 'JS') +with Files('*.cpp'): + BUG_COMPONENT = ('Firefox', 'C++') diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/final/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/final/moz.build new file mode 100644 index 000000000..cee286445 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/final/moz.build @@ -0,0 +1,3 @@ +with Files('**/Makefile.in'): + BUG_COMPONENT = ('Core', 'Build Config') + FINAL = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/final/subcomponent/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/final/subcomponent/moz.build new file mode 100644 index 000000000..206bf661b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/final/subcomponent/moz.build @@ -0,0 +1,2 @@ +with Files('**'): + BUG_COMPONENT = ('Another', 'Component') diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/moz.build new file mode 100644 index 000000000..4ecb1112c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/moz.build @@ -0,0 +1,2 @@ +with Files('**'): + BUG_COMPONENT = ('default_product', 'default_component') diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/simple/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/simple/moz.build new file mode 100644 index 000000000..7994d4a38 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/simple/moz.build @@ -0,0 +1,2 @@ +with Files('*'): + BUG_COMPONENT = ('Core', 'Build Config') diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/static/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/static/moz.build new file mode 100644 index 000000000..0a88e09e7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/bug_component/static/moz.build @@ -0,0 +1,5 @@ +with Files('foo'): + BUG_COMPONENT = ('FooProduct', 'FooComponent') + +with Files('bar'): + BUG_COMPONENT = ('BarProduct', 'BarComponent') diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-info/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-info/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-info/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/module.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/module.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/module.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/moz.build new file mode 100644 index 000000000..8915edc12 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/moz.build @@ -0,0 +1,6 @@ +XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] +REFTEST_MANIFESTS += ['tests/reftests/reftest.list'] + +EXTRA_JS_MODULES += [ + 'module.js', +]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/reftest-stylo.list b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/reftest-stylo.list new file mode 100644 index 000000000..252a5b986 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/reftest-stylo.list @@ -0,0 +1,2 @@ +# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing +== test1.html test1.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/reftest.list b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/reftest.list new file mode 100644 index 000000000..504d45973 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/reftest.list @@ -0,0 +1 @@ +== test1.html test1-ref.html
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/test1-ref.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/test1-ref.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/test1-ref.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/test1.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/test1.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/test1.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/test_default_mod.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/test_default_mod.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/test_default_mod.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/xpcshell.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/xpcshell.ini new file mode 100644 index 000000000..55c18a250 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/xpcshell.ini @@ -0,0 +1 @@ +[test_default_mod.js]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/moz.build new file mode 100644 index 000000000..faff2a173 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/moz.build @@ -0,0 +1,4 @@ +DIRS += [ + 'default', + 'simple', +]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/base.cpp b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/base.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/base.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/browser.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/browser.ini new file mode 100644 index 000000000..f284de043 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/browser.ini @@ -0,0 +1 @@ +[test_mod.js]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/test_mod.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/test_mod.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/test_mod.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/moz.build new file mode 100644 index 000000000..cbce16e1d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/moz.build @@ -0,0 +1,22 @@ +with Files('src/*'): + IMPACTED_TESTS.files += [ + 'tests/test_general.html', + ] + +with Files('src/module.jsm'): + IMPACTED_TESTS.files += [ + 'browser/**.js', + ] + +with Files('base.cpp'): + IMPACTED_TESTS.files += [ + '/default/tests/xpcshell/test_default_mod.js', + 'tests/*', + ] + + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] +BROWSER_CHROME_MANIFESTS += ['browser/browser.ini'] + +UNIFIED_SOURCES += ['base.cpp'] +DIRS += ['src'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/module.jsm b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/module.jsm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/module.jsm diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/moz.build new file mode 100644 index 000000000..e0c49f129 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/moz.build @@ -0,0 +1,3 @@ +EXTRA_JS_MODULES += [ + 'module.jsm', +]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/mochitest.ini new file mode 100644 index 000000000..662566abd --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/mochitest.ini @@ -0,0 +1,2 @@ +[test_general.html] +[test_specific.html]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/moz.build new file mode 100644 index 000000000..8ef3a9fd8 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/moz.build @@ -0,0 +1 @@ +MOCHITEST_MANIFESTS += ['mochitest.ini']
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_general.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_general.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_general.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_specific.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_specific.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_specific.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/moz.build new file mode 100644 index 000000000..0b7ca5a2b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/moz.build @@ -0,0 +1,15 @@ +with Files('src/submodule/**'): + IMPACTED_TESTS.tags += [ + 'submodule', + ] + +with Files('src/bar.jsm'): + IMPACTED_TESTS.flavors += [ + 'browser-chrome', + ] + IMPACTED_TESTS.files += [ + '**.js', + ] + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] +XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/bar.jsm b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/bar.jsm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/bar.jsm diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/submodule/foo.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/submodule/foo.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/submodule/foo.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/mochitest.ini new file mode 100644 index 000000000..d40ca4d06 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/mochitest.ini @@ -0,0 +1,3 @@ +[test_simple.html] +[test_specific.html] +tags = submodule
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_bar.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_bar.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_bar.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_simple.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_simple.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_simple.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_specific.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_specific.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_specific.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/xpcshell.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/xpcshell.ini new file mode 100644 index 000000000..1275764c4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/xpcshell.ini @@ -0,0 +1 @@ +[test_bar.js]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/final-target-pp-files-non-srcdir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/final-target-pp-files-non-srcdir/moz.build new file mode 100644 index 000000000..73132b0cf --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/final-target-pp-files-non-srcdir/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FINAL_TARGET_PP_FILES += [ + '!foo.js', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/moz.build new file mode 100644 index 000000000..0b694ed84 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +GENERATED_FILES += ['bar.c'] + +bar = GENERATED_FILES['bar.c'] +bar.script = '/script.py:make_bar' +bar.inputs = [] diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/script.py b/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/script.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/script.py diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-method-names/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated-files-method-names/moz.build new file mode 100644 index 000000000..e080b47f9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-method-names/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +GENERATED_FILES += [ 'bar.c', 'foo.c' ] + +bar = GENERATED_FILES['bar.c'] +bar.script = 'script.py:make_bar' +bar.inputs = [] + +foo = GENERATED_FILES['foo.c'] +foo.script = 'script.py' +foo.inputs = [] diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-method-names/script.py b/python/mozbuild/mozbuild/test/frontend/data/generated-files-method-names/script.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-method-names/script.py diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-inputs/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-inputs/moz.build new file mode 100644 index 000000000..da96c5fbc --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-inputs/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +GENERATED_FILES += ['bar.c', 'foo.c'] + +foo = GENERATED_FILES['foo.c'] +foo.script = 'script.py' +foo.inputs = ['datafile'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-inputs/script.py b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-inputs/script.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-inputs/script.py diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-python-script/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-python-script/moz.build new file mode 100644 index 000000000..080cb2a4e --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-python-script/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +GENERATED_FILES += ['bar.c', 'foo.c'] + +bar = GENERATED_FILES['bar.c'] +bar.script = 'script.rb' diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-python-script/script.rb b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-python-script/script.rb new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-python-script/script.rb diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-script/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-script/moz.build new file mode 100644 index 000000000..90fa17666 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-no-script/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +GENERATED_FILES += [ 'bar.c', 'foo.c' ] + +bar = GENERATED_FILES['bar.c'] +bar.script = 'nonexistent-script.py' diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-files/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated-files/moz.build new file mode 100644 index 000000000..1c24113f3 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +GENERATED_FILES += [ 'bar.c', 'foo.c', ('xpidllex.py', 'xpidlyacc.py'), ] diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/a.cpp b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/a.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/a.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/b.cc b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/b.cc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/b.cc diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/c.cxx b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/c.cxx new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/c.cxx diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/d.c b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/d.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/d.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/e.m b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/e.m new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/e.m diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/f.mm b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/f.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/f.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/g.S b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/g.S new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/g.S diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/h.s b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/h.s new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/h.s diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/i.asm b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/i.asm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/i.asm diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated-sources/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/moz.build new file mode 100644 index 000000000..12d90b15c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated-sources/moz.build @@ -0,0 +1,37 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + +Library('dummy') + +SOURCES += [ + '!a.cpp', + '!b.cc', + '!c.cxx', +] + +SOURCES += [ + '!d.c', +] + +SOURCES += [ + '!e.m', +] + +SOURCES += [ + '!f.mm', +] + +SOURCES += [ + '!g.S', +] + +SOURCES += [ + '!h.s', + '!i.asm', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/generated_includes/moz.build b/python/mozbuild/mozbuild/test/frontend/data/generated_includes/moz.build new file mode 100644 index 000000000..14deaf8cf --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/generated_includes/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +LOCAL_INCLUDES += ['!/bar/baz', '!foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-defines/moz.build b/python/mozbuild/mozbuild/test/frontend/data/host-defines/moz.build new file mode 100644 index 000000000..37628fede --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-defines/moz.build @@ -0,0 +1,14 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +value = 'xyz' +HOST_DEFINES = { + 'FOO': True, +} + +HOST_DEFINES['BAZ'] = '"abcd"' +HOST_DEFINES.update({ + 'BAR': 7, + 'VALUE': value, + 'QUX': False, +}) diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-sources/a.cpp b/python/mozbuild/mozbuild/test/frontend/data/host-sources/a.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-sources/a.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-sources/b.cc b/python/mozbuild/mozbuild/test/frontend/data/host-sources/b.cc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-sources/b.cc diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-sources/c.cxx b/python/mozbuild/mozbuild/test/frontend/data/host-sources/c.cxx new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-sources/c.cxx diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-sources/d.c b/python/mozbuild/mozbuild/test/frontend/data/host-sources/d.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-sources/d.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-sources/e.mm b/python/mozbuild/mozbuild/test/frontend/data/host-sources/e.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-sources/e.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-sources/f.mm b/python/mozbuild/mozbuild/test/frontend/data/host-sources/f.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-sources/f.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/host-sources/moz.build b/python/mozbuild/mozbuild/test/frontend/data/host-sources/moz.build new file mode 100644 index 000000000..5a6f0acb6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/host-sources/moz.build @@ -0,0 +1,25 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def HostLibrary(name): + '''Template for libraries.''' + HOST_LIBRARY_NAME = name + +HostLibrary('dummy') + +HOST_SOURCES += [ + 'a.cpp', + 'b.cc', + 'c.cxx', +] + +HOST_SOURCES += [ + 'd.c', +] + +HOST_SOURCES += [ + 'e.mm', + 'f.mm', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-basic/included.build b/python/mozbuild/mozbuild/test/frontend/data/include-basic/included.build new file mode 100644 index 000000000..bb492a242 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-basic/included.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS += ['bar'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-basic/moz.build b/python/mozbuild/mozbuild/test/frontend/data/include-basic/moz.build new file mode 100644 index 000000000..8e6a0f338 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-basic/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo'] + +include('included.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/included-1.build b/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/included-1.build new file mode 100644 index 000000000..a6a0fd8ea --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/included-1.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('included-2.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/included-2.build b/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/included-2.build new file mode 100644 index 000000000..9bfc65481 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/included-2.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +ILLEGAL = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/moz.build b/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/moz.build new file mode 100644 index 000000000..7ba111d1f --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-file-stack/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('included-1.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-missing/moz.build b/python/mozbuild/mozbuild/test/frontend/data/include-missing/moz.build new file mode 100644 index 000000000..d72d47c46 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-missing/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('missing.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-outside-topsrcdir/relative.build b/python/mozbuild/mozbuild/test/frontend/data/include-outside-topsrcdir/relative.build new file mode 100644 index 000000000..f8084f0dd --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-outside-topsrcdir/relative.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('../moz.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/child.build b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/child.build new file mode 100644 index 000000000..446207081 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/child.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('../parent.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/child2.build b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/child2.build new file mode 100644 index 000000000..618a75ed0 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/child2.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('grandchild/grandchild.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/grandchild/grandchild.build b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/grandchild/grandchild.build new file mode 100644 index 000000000..4d721fde4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/child/grandchild/grandchild.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('../../parent.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/parent.build b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/parent.build new file mode 100644 index 000000000..a2ed3fa49 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-relative-from-child/parent.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-topsrcdir-relative/moz.build b/python/mozbuild/mozbuild/test/frontend/data/include-topsrcdir-relative/moz.build new file mode 100644 index 000000000..f9194c00e --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-topsrcdir-relative/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('/sibling.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/include-topsrcdir-relative/sibling.build b/python/mozbuild/mozbuild/test/frontend/data/include-topsrcdir-relative/sibling.build new file mode 100644 index 000000000..a2ed3fa49 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/include-topsrcdir-relative/sibling.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/bar/moz.build b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/bar/moz.build new file mode 100644 index 000000000..568f361a5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/bar/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. diff --git a/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/foo/baz/moz.build b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/foo/baz/moz.build new file mode 100644 index 000000000..a1b892e2d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/foo/baz/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_MODULE = 'baz' diff --git a/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/foo/moz.build b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/foo/moz.build new file mode 100644 index 000000000..a06f6d12d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/foo/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DIRS += ['baz'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/moz.build b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/moz.build new file mode 100644 index 000000000..2801f105d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/inheriting-variables/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_MODULE = 'foobar' +export("XPIDL_MODULE") + +DIRS += ['foo', 'bar'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/bar/moz.build b/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/bar/moz.build new file mode 100644 index 000000000..f189212fd --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/bar/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +IPDL_SOURCES += [ + 'bar.ipdl', + 'bar2.ipdlh', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/foo/moz.build b/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/foo/moz.build new file mode 100644 index 000000000..4e1554559 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/foo/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +IPDL_SOURCES += [ + 'foo.ipdl', + 'foo2.ipdlh', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/moz.build b/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/moz.build new file mode 100644 index 000000000..03cf5e236 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/ipdl_sources/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DIRS += [ + 'bar', + 'foo', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/jar-manifests-multiple-files/moz.build b/python/mozbuild/mozbuild/test/frontend/data/jar-manifests-multiple-files/moz.build new file mode 100644 index 000000000..43789914e --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/jar-manifests-multiple-files/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ['jar.mn', 'other.jar'] + diff --git a/python/mozbuild/mozbuild/test/frontend/data/jar-manifests/moz.build b/python/mozbuild/mozbuild/test/frontend/data/jar-manifests/moz.build new file mode 100644 index 000000000..aac3a838c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/jar-manifests/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ['jar.mn'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/library-defines/liba/moz.build b/python/mozbuild/mozbuild/test/frontend/data/library-defines/liba/moz.build new file mode 100644 index 000000000..5d5e78eed --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/library-defines/liba/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +Library('liba') +LIBRARY_DEFINES['IN_LIBA'] = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/library-defines/libb/moz.build b/python/mozbuild/mozbuild/test/frontend/data/library-defines/libb/moz.build new file mode 100644 index 000000000..add45f6c1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/library-defines/libb/moz.build @@ -0,0 +1,7 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +Library('libb') +FINAL_LIBRARY = 'liba' +LIBRARY_DEFINES['IN_LIBB'] = True +USE_LIBS += ['libd'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/library-defines/libc/moz.build b/python/mozbuild/mozbuild/test/frontend/data/library-defines/libc/moz.build new file mode 100644 index 000000000..cf25e2c44 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/library-defines/libc/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +Library('libc') +FINAL_LIBRARY = 'libb' diff --git a/python/mozbuild/mozbuild/test/frontend/data/library-defines/libd/moz.build b/python/mozbuild/mozbuild/test/frontend/data/library-defines/libd/moz.build new file mode 100644 index 000000000..dd057c3d7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/library-defines/libd/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +Library('libd') +FORCE_STATIC_LIB = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/library-defines/moz.build b/python/mozbuild/mozbuild/test/frontend/data/library-defines/moz.build new file mode 100644 index 000000000..5f05fcef7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/library-defines/moz.build @@ -0,0 +1,9 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + +DIRS = ['liba', 'libb', 'libc', 'libd'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/local_includes/bar/baz/dummy_file_for_nonempty_directory b/python/mozbuild/mozbuild/test/frontend/data/local_includes/bar/baz/dummy_file_for_nonempty_directory new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/local_includes/bar/baz/dummy_file_for_nonempty_directory diff --git a/python/mozbuild/mozbuild/test/frontend/data/local_includes/foo/dummy_file_for_nonempty_directory b/python/mozbuild/mozbuild/test/frontend/data/local_includes/foo/dummy_file_for_nonempty_directory new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/local_includes/foo/dummy_file_for_nonempty_directory diff --git a/python/mozbuild/mozbuild/test/frontend/data/local_includes/moz.build b/python/mozbuild/mozbuild/test/frontend/data/local_includes/moz.build new file mode 100644 index 000000000..565c2bee6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/local_includes/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +LOCAL_INCLUDES += ['/bar/baz', 'foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/missing-local-includes/moz.build b/python/mozbuild/mozbuild/test/frontend/data/missing-local-includes/moz.build new file mode 100644 index 000000000..565c2bee6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/missing-local-includes/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +LOCAL_INCLUDES += ['/bar/baz', 'foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/moz.build b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/moz.build new file mode 100644 index 000000000..b493ec5b5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/moz.build @@ -0,0 +1,27 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + +Library('test') + +DIRS += [ + 'rust1', + 'rust2', +] + +USE_LIBS += [ + 'rust1', + 'rust2', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust1/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust1/Cargo.toml new file mode 100644 index 000000000..9037d8f65 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust1/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rust1" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust1/moz.build b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust1/moz.build new file mode 100644 index 000000000..7418cca65 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust1/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +RustLibrary('rust1') diff --git a/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust2/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust2/Cargo.toml new file mode 100644 index 000000000..f2001895e --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust2/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rust2" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust2/moz.build b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust2/moz.build new file mode 100644 index 000000000..abd34e7db --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/multiple-rust-libraries/rust2/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +RustLibrary('rust2') diff --git a/python/mozbuild/mozbuild/test/frontend/data/program/moz.build b/python/mozbuild/mozbuild/test/frontend/data/program/moz.build new file mode 100644 index 000000000..4c19b90cd --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/program/moz.build @@ -0,0 +1,15 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Program(name): + PROGRAM = name + + +@template +def SimplePrograms(names): + SIMPLE_PROGRAMS += names + +Program('test_program') + +SimplePrograms([ 'test_program1', 'test_program2' ]) diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-bad-dir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-bad-dir/moz.build new file mode 100644 index 000000000..5fac39736 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-bad-dir/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-basic/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-basic/moz.build new file mode 100644 index 000000000..0a91c4692 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-basic/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +ILLEGAL = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-empty-list/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-empty-list/moz.build new file mode 100644 index 000000000..4dfba1c60 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-empty-list/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = [] diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-error-func/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-error-func/moz.build new file mode 100644 index 000000000..84b2cdea4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-error-func/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +error('Some error.') + diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-included-from/child.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-included-from/child.build new file mode 100644 index 000000000..9bfc65481 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-included-from/child.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +ILLEGAL = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-included-from/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-included-from/moz.build new file mode 100644 index 000000000..4a29cae11 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-included-from/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('child.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-missing-include/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-missing-include/moz.build new file mode 100644 index 000000000..d72d47c46 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-missing-include/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('missing.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-outside-topsrcdir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-outside-topsrcdir/moz.build new file mode 100644 index 000000000..149972edf --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-outside-topsrcdir/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +include('../include-basic/moz.build') diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-read-unknown-global/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-read-unknown-global/moz.build new file mode 100644 index 000000000..6fc10f766 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-read-unknown-global/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +l = FOO diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-repeated-dir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-repeated-dir/moz.build new file mode 100644 index 000000000..847f95167 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-repeated-dir/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo'] + +DIRS += ['foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-script-error/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-script-error/moz.build new file mode 100644 index 000000000..a91d38b41 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-script-error/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +foo = True + None diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-syntax/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-syntax/moz.build new file mode 100644 index 000000000..70a0d2c06 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-syntax/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +foo = diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-write-bad-value/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-write-bad-value/moz.build new file mode 100644 index 000000000..e3d0e656a --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-write-bad-value/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = 'dir' diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-error-write-unknown-global/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-error-write-unknown-global/moz.build new file mode 100644 index 000000000..34579849d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-error-write-unknown-global/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['dir1', 'dir2'] + +FOO = 'bar' diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/file b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/file diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/a/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/file b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/file diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/b/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/every-level/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file1 b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file1 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file2 b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file2 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/file2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/file b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/file diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/no-intermediate-moz-build/child/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/dir1/dir2/dir3/file b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/dir1/dir2/dir3/file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/dir1/dir2/dir3/file diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d1/parent-is-far/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/file b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/file diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir1/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/file b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/file diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/dir2/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/d2/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/file b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/file diff --git a/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/moz.build b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/reader-relevant-mozbuild/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-dash-folding/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/rust-library-dash-folding/Cargo.toml new file mode 100644 index 000000000..fa122b7ce --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-dash-folding/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "random-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-dash-folding/moz.build b/python/mozbuild/mozbuild/test/frontend/data/rust-library-dash-folding/moz.build new file mode 100644 index 000000000..01b3a35a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-dash-folding/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate') diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-invalid-crate-type/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/rust-library-invalid-crate-type/Cargo.toml new file mode 100644 index 000000000..26c653fde --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-invalid-crate-type/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "random-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[lib] +crate-type = ["dylib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-invalid-crate-type/moz.build b/python/mozbuild/mozbuild/test/frontend/data/rust-library-invalid-crate-type/moz.build new file mode 100644 index 000000000..01b3a35a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-invalid-crate-type/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate') diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-name-mismatch/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/rust-library-name-mismatch/Cargo.toml new file mode 100644 index 000000000..41a9a7c8f --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-name-mismatch/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "deterministic-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-name-mismatch/moz.build b/python/mozbuild/mozbuild/test/frontend/data/rust-library-name-mismatch/moz.build new file mode 100644 index 000000000..01b3a35a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-name-mismatch/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate') diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-cargo-toml/moz.build b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-cargo-toml/moz.build new file mode 100644 index 000000000..01b3a35a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-cargo-toml/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate') diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-lib-section/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-lib-section/Cargo.toml new file mode 100644 index 000000000..a20b19c62 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-lib-section/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "random-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-lib-section/moz.build b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-lib-section/moz.build new file mode 100644 index 000000000..01b3a35a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-lib-section/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate') diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-profile-section/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-profile-section/Cargo.toml new file mode 100644 index 000000000..2700849db --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-profile-section/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "random-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[lib] +crate-type = ["staticlib"] + +[profile.release] +panic = "abort" diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-profile-section/moz.build b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-profile-section/moz.build new file mode 100644 index 000000000..01b3a35a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-no-profile-section/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate') diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-non-abort-panic/Cargo.toml b/python/mozbuild/mozbuild/test/frontend/data/rust-library-non-abort-panic/Cargo.toml new file mode 100644 index 000000000..ccdd06243 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-non-abort-panic/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "random-crate" +version = "0.1.0" +authors = [ + "Nobody <nobody@mozilla.org>", +] + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "unwind" + +[profile.release] diff --git a/python/mozbuild/mozbuild/test/frontend/data/rust-library-non-abort-panic/moz.build b/python/mozbuild/mozbuild/test/frontend/data/rust-library-non-abort-panic/moz.build new file mode 100644 index 000000000..d3896decc --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/rust-library-non-abort-panic/moz.build @@ -0,0 +1,18 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +RustLibrary('random-crate')
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/sdk-files/bar.ico b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/bar.ico new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/bar.ico diff --git a/python/mozbuild/mozbuild/test/frontend/data/sdk-files/baz.png b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/baz.png new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/baz.png diff --git a/python/mozbuild/mozbuild/test/frontend/data/sdk-files/foo.xpm b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/foo.xpm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/foo.xpm diff --git a/python/mozbuild/mozbuild/test/frontend/data/sdk-files/moz.build b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/moz.build new file mode 100644 index 000000000..a2f8ddf9b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/moz.build @@ -0,0 +1,12 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +SDK_FILES += [ + 'bar.ico', + 'baz.png', + 'foo.xpm', +] + +SDK_FILES.icons += [ + 'quux.icns', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/sdk-files/quux.icns b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/quux.icns new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sdk-files/quux.icns diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/d.c b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/d.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/d.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/e.m b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/e.m new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/e.m diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/g.S b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/g.S new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/g.S diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/h.s b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/h.s new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/h.s diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/i.asm b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/i.asm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/i.asm diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/moz.build b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/moz.build new file mode 100644 index 000000000..8937fc245 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources-just-c/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + +Library('dummy') + +SOURCES += [ + 'd.c', +] + +SOURCES += [ + 'e.m', +] + +SOURCES += [ + 'g.S', +] + +SOURCES += [ + 'h.s', + 'i.asm', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/a.cpp b/python/mozbuild/mozbuild/test/frontend/data/sources/a.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/a.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/b.cc b/python/mozbuild/mozbuild/test/frontend/data/sources/b.cc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/b.cc diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/c.cxx b/python/mozbuild/mozbuild/test/frontend/data/sources/c.cxx new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/c.cxx diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/d.c b/python/mozbuild/mozbuild/test/frontend/data/sources/d.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/d.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/e.m b/python/mozbuild/mozbuild/test/frontend/data/sources/e.m new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/e.m diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/f.mm b/python/mozbuild/mozbuild/test/frontend/data/sources/f.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/f.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/g.S b/python/mozbuild/mozbuild/test/frontend/data/sources/g.S new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/g.S diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/h.s b/python/mozbuild/mozbuild/test/frontend/data/sources/h.s new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/h.s diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/i.asm b/python/mozbuild/mozbuild/test/frontend/data/sources/i.asm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/i.asm diff --git a/python/mozbuild/mozbuild/test/frontend/data/sources/moz.build b/python/mozbuild/mozbuild/test/frontend/data/sources/moz.build new file mode 100644 index 000000000..f9b453238 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/sources/moz.build @@ -0,0 +1,37 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + +Library('dummy') + +SOURCES += [ + 'a.cpp', + 'b.cc', + 'c.cxx', +] + +SOURCES += [ + 'd.c', +] + +SOURCES += [ + 'e.m', +] + +SOURCES += [ + 'f.mm', +] + +SOURCES += [ + 'g.S', +] + +SOURCES += [ + 'h.s', + 'i.asm', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/templates/templates.mozbuild b/python/mozbuild/mozbuild/test/frontend/data/templates/templates.mozbuild new file mode 100644 index 000000000..290104bc7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/templates/templates.mozbuild @@ -0,0 +1,21 @@ +@template +def Template(foo, bar=[]): + SOURCES += foo + DIRS += bar + +@template +def TemplateError(foo): + ILLEGAL = foo + +@template +def TemplateGlobalVariable(): + SOURCES += illegal + +@template +def TemplateGlobalUPPERVariable(): + SOURCES += DIRS + +@template +def TemplateInherit(foo): + USE_LIBS += ['foo'] + Template(foo) diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-harness-files-root/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files-root/moz.build new file mode 100644 index 000000000..d7f6377d0 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files-root/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +TEST_HARNESS_FILES += ["foo.py"] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/mochitest.ini new file mode 100644 index 000000000..d87114ac7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/mochitest.ini @@ -0,0 +1 @@ +# dummy file so the existence checks for TEST_HARNESS_FILES succeed diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/mochitest.py b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/mochitest.py new file mode 100644 index 000000000..d87114ac7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/mochitest.py @@ -0,0 +1 @@ +# dummy file so the existence checks for TEST_HARNESS_FILES succeed diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/moz.build new file mode 100644 index 000000000..ff3fed0ee --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/moz.build @@ -0,0 +1,7 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +TEST_HARNESS_FILES.mochitest += ["runtests.py"] +TEST_HARNESS_FILES.mochitest += ["utils.py"] +TEST_HARNESS_FILES.testing.mochitest += ["mochitest.py"] +TEST_HARNESS_FILES.testing.mochitest += ["mochitest.ini"] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/runtests.py b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/runtests.py new file mode 100644 index 000000000..d87114ac7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/runtests.py @@ -0,0 +1 @@ +# dummy file so the existence checks for TEST_HARNESS_FILES succeed diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/utils.py b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/utils.py new file mode 100644 index 000000000..d87114ac7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-harness-files/utils.py @@ -0,0 +1 @@ +# dummy file so the existence checks for TEST_HARNESS_FILES succeed diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-install-shared-lib/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-install-shared-lib/moz.build new file mode 100644 index 000000000..bdb209074 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-install-shared-lib/moz.build @@ -0,0 +1,12 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def SharedLibrary(name): + LIBRARY_NAME = name + FORCE_SHARED_LIB = True + +DIST_INSTALL = False +SharedLibrary('foo') + +TEST_HARNESS_FILES.foo.bar += ['!%sfoo%s' % (CONFIG['DLL_PREFIX'], CONFIG['DLL_SUFFIX'])] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/moz.build new file mode 100644 index 000000000..b153dd085 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/moz.build @@ -0,0 +1,11 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['one','two','three'] +@template +def SharedLibrary(name): + LIBRARY_NAME = name + FORCE_SHARED_LIB = True + +SharedLibrary('cxx_shared') +USE_LIBS += ['cxx_static'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/one/foo.cpp b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/one/foo.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/one/foo.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/one/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/one/moz.build new file mode 100644 index 000000000..f66270818 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/one/moz.build @@ -0,0 +1,9 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + LIBRARY_NAME = name + +Library('cxx_static') +SOURCES += ['foo.cpp'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/three/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/three/moz.build new file mode 100644 index 000000000..7b3497be6 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/three/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +SharedLibrary('just_c_shared') +USE_LIBS += ['just_c_static'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/two/foo.c b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/two/foo.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/two/foo.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/two/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/two/moz.build new file mode 100644 index 000000000..256642fea --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-linkables-cxx-link/two/moz.build @@ -0,0 +1,9 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + LIBRARY_NAME = name + +Library('just_c_static') +SOURCES += ['foo.c'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/absolute-support.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/absolute-support.ini new file mode 100644 index 000000000..900f42158 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/absolute-support.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = /.well-known/foo.txt + +[test_file.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/foo.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/foo.txt new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/foo.txt @@ -0,0 +1 @@ +hello diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/moz.build new file mode 100644 index 000000000..87b20c6b1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['absolute-support.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/test_file.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/test_file.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-absolute-support/test_file.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/bar.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/bar.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/bar.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/foo.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/foo.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/foo.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/mochitest.ini new file mode 100644 index 000000000..2f1fc406a --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/mochitest.ini @@ -0,0 +1,7 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +[DEFAULT] +support-files = bar.js foo.js bar.js + +[test_baz.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/moz.build new file mode 100644 index 000000000..4e7e9ff4e --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['mochitest.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/test_baz.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/test_baz.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-dupes/test_baz.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/included-reftest.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/included-reftest.list new file mode 100644 index 000000000..1caf9cc39 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/included-reftest.list @@ -0,0 +1 @@ +!= reftest2.html reftest2-ref.html
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/moz.build new file mode 100644 index 000000000..39ad44c28 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/moz.build @@ -0,0 +1 @@ +REFTEST_MANIFESTS += ['reftest.list']
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest-stylo.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest-stylo.list new file mode 100644 index 000000000..237aea0e0 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest-stylo.list @@ -0,0 +1,3 @@ +# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing +== reftest1.html reftest1.html +include included-reftest-stylo.list diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest.list new file mode 100644 index 000000000..80caf8ffa --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest.list @@ -0,0 +1,2 @@ +== reftest1.html reftest1-ref.html +include included-reftest.list diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-empty/empty.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-empty/empty.ini new file mode 100644 index 000000000..83a0cec0c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-empty/empty.ini @@ -0,0 +1,2 @@ +[DEFAULT] +foo = bar diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-empty/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-empty/moz.build new file mode 100644 index 000000000..edfaf435f --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-empty/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['empty.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-inactive-ignored/test_inactive.html b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-inactive-ignored/test_inactive.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-inactive-ignored/test_inactive.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/common.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/common.ini new file mode 100644 index 000000000..753cd0ec0 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/common.ini @@ -0,0 +1 @@ +[test_foo.html] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/mochitest.ini new file mode 100644 index 000000000..b8d4e123d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/mochitest.ini @@ -0,0 +1,4 @@ +[DEFAULT] +install-to-subdir = subdir + +[include:common.ini] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/moz.build new file mode 100644 index 000000000..4e7e9ff4e --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['mochitest.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/test_foo.html b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/test_foo.html new file mode 100644 index 000000000..18ecdcb79 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-includes/test_foo.html @@ -0,0 +1 @@ +<html></html> diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/moz.build new file mode 100644 index 000000000..9e4d7b21c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['subdir.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/subdir.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/subdir.ini new file mode 100644 index 000000000..6b320c2d5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/subdir.ini @@ -0,0 +1,5 @@ +[DEFAULT] +install-to-subdir = subdir +support-files = support.txt + +[test_foo.html] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/test_foo.html b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/test_foo.html new file mode 100644 index 000000000..18ecdcb79 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-install-subdir/test_foo.html @@ -0,0 +1 @@ +<html></html> diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/foo.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/foo.txt new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/foo.txt @@ -0,0 +1 @@ +hello diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/just-support.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/just-support.ini new file mode 100644 index 000000000..efa2d4bc0 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/just-support.ini @@ -0,0 +1,2 @@ +[DEFAULT] +support-files = foo.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/moz.build new file mode 100644 index 000000000..80a038d42 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-just-support/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['just-support.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y-support/dir1/bar b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y-support/dir1/bar new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y-support/dir1/bar diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y-support/foo b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y-support/foo new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y-support/foo diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y.ini new file mode 100644 index 000000000..9cf798918 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/a11y.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = a11y-support/** + +[test_a11y.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/browser.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/browser.ini new file mode 100644 index 000000000..a81ee3acb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/browser.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = support1 support2 + +[test_browser.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/chrome.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/chrome.ini new file mode 100644 index 000000000..1070c7853 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/chrome.ini @@ -0,0 +1,4 @@ +[DEFAULT] +skip-if = buildapp == 'b2g' + +[test_chrome.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/crashtest.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/crashtest.list new file mode 100644 index 000000000..b9d7f2685 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/crashtest.list @@ -0,0 +1 @@ +== crashtest1.html crashtest1-ref.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/metro.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/metro.ini new file mode 100644 index 000000000..a7eb6def4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/metro.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[test_metro.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/mochitest.ini new file mode 100644 index 000000000..69fd71de0 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/mochitest.ini @@ -0,0 +1,5 @@ +[DEFAULT] +support-files = external1 external2 +generated-files = external1 external2 + +[test_mochitest.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/moz.build new file mode 100644 index 000000000..33839d9e3 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/moz.build @@ -0,0 +1,12 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +A11Y_MANIFESTS += ['a11y.ini'] +BROWSER_CHROME_MANIFESTS += ['browser.ini'] +METRO_CHROME_MANIFESTS += ['metro.ini'] +MOCHITEST_MANIFESTS += ['mochitest.ini'] +MOCHITEST_CHROME_MANIFESTS += ['chrome.ini'] +XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini'] +REFTEST_MANIFESTS += ['reftest.list'] +CRASHTEST_MANIFESTS += ['crashtest.list'] +PYTHON_UNIT_TESTS += ['test_foo.py'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/reftest-stylo.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/reftest-stylo.list new file mode 100644 index 000000000..bd7b4f9cb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/reftest-stylo.list @@ -0,0 +1,2 @@ +# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing +== reftest1.html reftest1.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/reftest.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/reftest.list new file mode 100644 index 000000000..3fc25b296 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/reftest.list @@ -0,0 +1 @@ +== reftest1.html reftest1-ref.html diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_a11y.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_a11y.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_a11y.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_browser.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_browser.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_browser.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_chrome.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_chrome.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_chrome.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_foo.py b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_foo.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_foo.py diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_metro.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_metro.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_metro.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_mochitest.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_mochitest.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_mochitest.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_xpcshell.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_xpcshell.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/test_xpcshell.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/xpcshell.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/xpcshell.ini new file mode 100644 index 000000000..fb3005434 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/xpcshell.ini @@ -0,0 +1,6 @@ +[DEFAULT] +head = head1 head2 +tail = tail1 tail2 +dupe-manifest = + +[test_xpcshell.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-manifest/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-manifest/moz.build new file mode 100644 index 000000000..45edcc027 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-manifest/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +XPCSHELL_TESTS_MANIFESTS += ['does_not_exist.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file-unfiltered/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file-unfiltered/moz.build new file mode 100644 index 000000000..09c51cbb8 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file-unfiltered/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file-unfiltered/xpcshell.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file-unfiltered/xpcshell.ini new file mode 100644 index 000000000..9ab85c0ce --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file-unfiltered/xpcshell.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = support/** + +[missing.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file/mochitest.ini new file mode 100644 index 000000000..e3ef6216b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file/mochitest.ini @@ -0,0 +1 @@ +[test_missing.html] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file/moz.build new file mode 100644 index 000000000..4e7e9ff4e --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-missing-test-file/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['mochitest.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/child/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/child/mochitest.ini new file mode 100644 index 000000000..c78822429 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/child/mochitest.ini @@ -0,0 +1,4 @@ +[DEFAULT] +support-files = ../support-file.txt + +[test_foo.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/child/test_foo.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/child/test_foo.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/child/test_foo.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/moz.build new file mode 100644 index 000000000..a40e25625 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['child/mochitest.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/support-file.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/support-file.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-parent-support-files-dir/support-file.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/another-file.sjs b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/another-file.sjs new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/another-file.sjs diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/browser.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/browser.ini new file mode 100644 index 000000000..4f1335d6b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/browser.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + another-file.sjs + data/** + +[test_sub.js]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/data/one.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/data/one.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/data/one.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/data/two.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/data/two.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/data/two.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/test_sub.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/test_sub.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/child/test_sub.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/mochitest.ini new file mode 100644 index 000000000..ada59d387 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/mochitest.ini @@ -0,0 +1,9 @@ +[DEFAULT] +support-files = + support-file.txt + !/child/test_sub.js + !/child/another-file.sjs + !/child/data/** + !/does/not/exist.sjs + +[test_foo.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/moz.build new file mode 100644 index 000000000..1c1d064ea --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['mochitest.ini'] +BROWSER_CHROME_MANIFESTS += ['child/browser.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/support-file.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/support-file.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/support-file.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/test_foo.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/test_foo.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-missing/test_foo.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/another-file.sjs b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/another-file.sjs new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/another-file.sjs diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/browser.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/browser.ini new file mode 100644 index 000000000..4f1335d6b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/browser.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + another-file.sjs + data/** + +[test_sub.js]
\ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/data/one.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/data/one.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/data/one.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/data/two.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/data/two.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/data/two.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/test_sub.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/test_sub.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/child/test_sub.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/mochitest.ini new file mode 100644 index 000000000..a9860f3de --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/mochitest.ini @@ -0,0 +1,8 @@ +[DEFAULT] +support-files = + support-file.txt + !/child/test_sub.js + !/child/another-file.sjs + !/child/data/** + +[test_foo.js] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/moz.build new file mode 100644 index 000000000..1c1d064ea --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['mochitest.ini'] +BROWSER_CHROME_MANIFESTS += ['child/browser.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/support-file.txt b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/support-file.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/support-file.txt diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/test_foo.js b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/test_foo.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-shared-support/test_foo.js diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/moz.build new file mode 100644 index 000000000..281dee610 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += ['test.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/test.ini b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/test.ini new file mode 100644 index 000000000..caf391186 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/test.ini @@ -0,0 +1,4 @@ +[DEFAULT] +generated-files = does_not_exist + +[test_foo] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/test_foo b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/test_foo new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-unmatched-generated/test_foo diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-python-unit-test-missing/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-python-unit-test-missing/moz.build new file mode 100644 index 000000000..c9d769802 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-python-unit-test-missing/moz.build @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +PYTHON_UNIT_TESTS += ['test_foo.py'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir-missing-generated/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir-missing-generated/moz.build new file mode 100644 index 000000000..9d35a8ccc --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir-missing-generated/moz.build @@ -0,0 +1,10 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def SharedLibrary(name): + LIBRARY_NAME = name + FORCE_SHARED_LIB = True + +SharedLibrary('foo') +SYMBOLS_FILE = '!foo.symbols' diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir/foo.py b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir/foo.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir/foo.py diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir/moz.build new file mode 100644 index 000000000..fe227224d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file-objdir/moz.build @@ -0,0 +1,13 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def SharedLibrary(name): + LIBRARY_NAME = name + FORCE_SHARED_LIB = True + +SharedLibrary('foo') +SYMBOLS_FILE = '!foo.symbols' + +GENERATED_FILES += ['foo.symbols'] +GENERATED_FILES['foo.symbols'].script = 'foo.py' diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file/foo.symbols b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file/foo.symbols new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file/foo.symbols @@ -0,0 +1 @@ +foo diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file/moz.build new file mode 100644 index 000000000..d69333ea4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-symbols-file/moz.build @@ -0,0 +1,10 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def SharedLibrary(name): + LIBRARY_NAME = name + FORCE_SHARED_LIB = True + +SharedLibrary('foo') +SYMBOLS_FILE = 'foo.symbols' diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/moz.build new file mode 100644 index 000000000..73045dd43 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS += ['regular'] +TEST_DIRS += ['test'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/parallel/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/parallel/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/parallel/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/regular/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/regular/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/regular/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/test/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/test/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/test/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-outside-topsrcdir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-outside-topsrcdir/moz.build new file mode 100644 index 000000000..92ceb7f3b --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-outside-topsrcdir/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['../../foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/bar/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/bar/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/bar/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/foo/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/foo/moz.build new file mode 100644 index 000000000..ca1a429d9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/foo/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['../bar'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/moz.build new file mode 100644 index 000000000..5fac39736 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-relative-dirs/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/bar/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/bar/moz.build new file mode 100644 index 000000000..f06edcd36 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/bar/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['../foo'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/foo/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/foo/moz.build new file mode 100644 index 000000000..ca1a429d9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/foo/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['../bar'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/moz.build new file mode 100644 index 000000000..924f667d9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-repeated-dirs/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo', 'bar'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/bar/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/bar/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/bar/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/foo/biz/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/foo/biz/moz.build new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/foo/biz/moz.build diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/foo/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/foo/moz.build new file mode 100644 index 000000000..182541efd --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/foo/moz.build @@ -0,0 +1,2 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +DIRS = ['biz'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/moz.build new file mode 100644 index 000000000..924f667d9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-simple/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIRS = ['foo', 'bar'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/bar.cxx b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/bar.cxx new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/bar.cxx diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/c1.c b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/c1.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/c1.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/c2.c b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/c2.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/c2.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/foo.cpp b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/foo.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/foo.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/moz.build b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/moz.build new file mode 100644 index 000000000..a3660222d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/moz.build @@ -0,0 +1,28 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + +Library('dummy') + +UNIFIED_SOURCES += [ + 'bar.cxx', + 'foo.cpp', + 'quux.cc', +] + +UNIFIED_SOURCES += [ + 'objc1.mm', + 'objc2.mm', +] + +UNIFIED_SOURCES += [ + 'c1.c', + 'c2.c', +] + +FILES_PER_UNIFIED_FILE = 1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/objc1.mm b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/objc1.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/objc1.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/objc2.mm b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/objc2.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/objc2.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/quux.cc b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/quux.cc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources-non-unified/quux.cc diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/bar.cxx b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/bar.cxx new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/bar.cxx diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/c1.c b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/c1.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/c1.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/c2.c b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/c2.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/c2.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/foo.cpp b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/foo.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/foo.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/moz.build b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/moz.build new file mode 100644 index 000000000..5d1d89fb4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/moz.build @@ -0,0 +1,28 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + +Library('dummy') + +UNIFIED_SOURCES += [ + 'bar.cxx', + 'foo.cpp', + 'quux.cc', +] + +UNIFIED_SOURCES += [ + 'objc1.mm', + 'objc2.mm', +] + +UNIFIED_SOURCES += [ + 'c1.c', + 'c2.c', +] + +FILES_PER_UNIFIED_FILE = 32 diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/objc1.mm b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/objc1.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/objc1.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/objc2.mm b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/objc2.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/objc2.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/unified-sources/quux.cc b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/quux.cc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/unified-sources/quux.cc diff --git a/python/mozbuild/mozbuild/test/frontend/data/use-yasm/moz.build b/python/mozbuild/mozbuild/test/frontend/data/use-yasm/moz.build new file mode 100644 index 000000000..11f45953d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/use-yasm/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +USE_YASM = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/bans.S b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/bans.S new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/bans.S diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build new file mode 100644 index 000000000..e85e6ff5d --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build @@ -0,0 +1,25 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +DIST_INSTALL = False + +NO_VISIBILITY_FLAGS = True + +DELAYLOAD_DLLS = ['foo.dll', 'bar.dll'] + +RCFILE = 'foo.rc' +RESFILE = 'bar.res' +RCINCLUDE = 'bar.rc' +DEFFILE = 'baz.def' + +CFLAGS += ['-fno-exceptions', '-w'] +CXXFLAGS += ['-fcxx-exceptions', '-include foo.h'] +LDFLAGS += ['-framework Foo', '-x'] +HOST_CFLAGS += ['-funroll-loops', '-wall'] +HOST_CXXFLAGS += ['-funroll-loops-harder', '-wall-day-everyday'] +WIN32_EXE_LDFLAGS += ['-subsystem:console'] + +DISABLE_STL_WRAPPING = True + +ALLOW_COMPILER_WARNINGS = True diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.c b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.cpp b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.mm b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test1.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.c b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.c diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.cpp b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.cpp diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.mm b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.mm new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/test2.mm diff --git a/python/mozbuild/mozbuild/test/frontend/data/xpidl-module-no-sources/moz.build b/python/mozbuild/mozbuild/test/frontend/data/xpidl-module-no-sources/moz.build new file mode 100644 index 000000000..60f061d5c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/xpidl-module-no-sources/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +XPIDL_MODULE = 'xpidl_module' diff --git a/python/mozbuild/mozbuild/test/frontend/test_context.py b/python/mozbuild/mozbuild/test/frontend/test_context.py new file mode 100644 index 000000000..070cfad67 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/test_context.py @@ -0,0 +1,721 @@ +# 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/. + +import os +import unittest + +from mozunit import main + +from mozbuild.frontend.context import ( + AbsolutePath, + Context, + ContextDerivedTypedHierarchicalStringList, + ContextDerivedTypedList, + ContextDerivedTypedListWithItems, + ContextDerivedTypedRecord, + Files, + FUNCTIONS, + ObjDirPath, + Path, + SourcePath, + SPECIAL_VARIABLES, + SUBCONTEXTS, + VARIABLES, +) + +from mozbuild.util import StrictOrderingOnAppendListWithFlagsFactory +from mozpack import path as mozpath + + +class TestContext(unittest.TestCase): + def test_defaults(self): + test = Context({ + 'foo': (int, int, ''), + 'bar': (bool, bool, ''), + 'baz': (dict, dict, ''), + }) + + self.assertEqual(test.keys(), []) + + self.assertEqual(test['foo'], 0) + + self.assertEqual(set(test.keys()), { 'foo' }) + + self.assertEqual(test['bar'], False) + + self.assertEqual(set(test.keys()), { 'foo', 'bar' }) + + self.assertEqual(test['baz'], {}) + + self.assertEqual(set(test.keys()), { 'foo', 'bar', 'baz' }) + + with self.assertRaises(KeyError): + test['qux'] + + self.assertEqual(set(test.keys()), { 'foo', 'bar', 'baz' }) + + def test_type_check(self): + test = Context({ + 'foo': (int, int, ''), + 'baz': (dict, list, ''), + }) + + test['foo'] = 5 + + self.assertEqual(test['foo'], 5) + + with self.assertRaises(ValueError): + test['foo'] = {} + + self.assertEqual(test['foo'], 5) + + with self.assertRaises(KeyError): + test['bar'] = True + + test['baz'] = [('a', 1), ('b', 2)] + + self.assertEqual(test['baz'], { 'a': 1, 'b': 2 }) + + def test_update(self): + test = Context({ + 'foo': (int, int, ''), + 'bar': (bool, bool, ''), + 'baz': (dict, list, ''), + }) + + self.assertEqual(test.keys(), []) + + with self.assertRaises(ValueError): + test.update(bar=True, foo={}) + + self.assertEqual(test.keys(), []) + + test.update(bar=True, foo=1) + + self.assertEqual(set(test.keys()), { 'foo', 'bar' }) + self.assertEqual(test['foo'], 1) + self.assertEqual(test['bar'], True) + + test.update([('bar', False), ('foo', 2)]) + self.assertEqual(test['foo'], 2) + self.assertEqual(test['bar'], False) + + test.update([('foo', 0), ('baz', { 'a': 1, 'b': 2 })]) + self.assertEqual(test['foo'], 0) + self.assertEqual(test['baz'], { 'a': 1, 'b': 2 }) + + test.update([('foo', 42), ('baz', [('c', 3), ('d', 4)])]) + self.assertEqual(test['foo'], 42) + self.assertEqual(test['baz'], { 'c': 3, 'd': 4 }) + + def test_context_paths(self): + test = Context() + + # Newly created context has no paths. + self.assertIsNone(test.main_path) + self.assertIsNone(test.current_path) + self.assertEqual(test.all_paths, set()) + self.assertEqual(test.source_stack, []) + + foo = os.path.abspath('foo') + test.add_source(foo) + + # Adding the first source makes it the main and current path. + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, foo) + self.assertEqual(test.all_paths, set([foo])) + self.assertEqual(test.source_stack, [foo]) + + bar = os.path.abspath('bar') + test.add_source(bar) + + # Adding the second source makes leaves main and current paths alone. + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, foo) + self.assertEqual(test.all_paths, set([bar, foo])) + self.assertEqual(test.source_stack, [foo]) + + qux = os.path.abspath('qux') + test.push_source(qux) + + # Pushing a source makes it the current path + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, qux) + self.assertEqual(test.all_paths, set([bar, foo, qux])) + self.assertEqual(test.source_stack, [foo, qux]) + + hoge = os.path.abspath('hoge') + test.push_source(hoge) + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, hoge) + self.assertEqual(test.all_paths, set([bar, foo, hoge, qux])) + self.assertEqual(test.source_stack, [foo, qux, hoge]) + + fuga = os.path.abspath('fuga') + + # Adding a source after pushing doesn't change the source stack + test.add_source(fuga) + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, hoge) + self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) + self.assertEqual(test.source_stack, [foo, qux, hoge]) + + # Adding a source twice doesn't change anything + test.add_source(qux) + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, hoge) + self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) + self.assertEqual(test.source_stack, [foo, qux, hoge]) + + last = test.pop_source() + + # Popping a source returns the last pushed one, not the last added one. + self.assertEqual(last, hoge) + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, qux) + self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) + self.assertEqual(test.source_stack, [foo, qux]) + + last = test.pop_source() + self.assertEqual(last, qux) + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, foo) + self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) + self.assertEqual(test.source_stack, [foo]) + + # Popping the main path is allowed. + last = test.pop_source() + self.assertEqual(last, foo) + self.assertEqual(test.main_path, foo) + self.assertIsNone(test.current_path) + self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) + self.assertEqual(test.source_stack, []) + + # Popping past the main path asserts. + with self.assertRaises(AssertionError): + test.pop_source() + + # Pushing after the main path was popped asserts. + with self.assertRaises(AssertionError): + test.push_source(foo) + + test = Context() + test.push_source(foo) + test.push_source(bar) + + # Pushing the same file twice is allowed. + test.push_source(bar) + test.push_source(foo) + self.assertEqual(last, foo) + self.assertEqual(test.main_path, foo) + self.assertEqual(test.current_path, foo) + self.assertEqual(test.all_paths, set([bar, foo])) + self.assertEqual(test.source_stack, [foo, bar, bar, foo]) + + def test_context_dirs(self): + class Config(object): pass + config = Config() + config.topsrcdir = mozpath.abspath(os.curdir) + config.topobjdir = mozpath.abspath('obj') + test = Context(config=config) + foo = mozpath.abspath('foo') + test.push_source(foo) + + self.assertEqual(test.srcdir, config.topsrcdir) + self.assertEqual(test.relsrcdir, '') + self.assertEqual(test.objdir, config.topobjdir) + self.assertEqual(test.relobjdir, '') + + foobar = os.path.abspath('foo/bar') + test.push_source(foobar) + self.assertEqual(test.srcdir, mozpath.join(config.topsrcdir, 'foo')) + self.assertEqual(test.relsrcdir, 'foo') + self.assertEqual(test.objdir, config.topobjdir) + self.assertEqual(test.relobjdir, '') + + +class TestSymbols(unittest.TestCase): + def _verify_doc(self, doc): + # Documentation should be of the format: + # """SUMMARY LINE + # + # EXTRA PARAGRAPHS + # """ + + self.assertNotIn('\r', doc) + + lines = doc.split('\n') + + # No trailing whitespace. + for line in lines[0:-1]: + self.assertEqual(line, line.rstrip()) + + self.assertGreater(len(lines), 0) + self.assertGreater(len(lines[0].strip()), 0) + + # Last line should be empty. + self.assertEqual(lines[-1].strip(), '') + + def test_documentation_formatting(self): + for typ, inp, doc in VARIABLES.values(): + self._verify_doc(doc) + + for attr, args, doc in FUNCTIONS.values(): + self._verify_doc(doc) + + for func, typ, doc in SPECIAL_VARIABLES.values(): + self._verify_doc(doc) + + for name, cls in SUBCONTEXTS.items(): + self._verify_doc(cls.__doc__) + + for name, v in cls.VARIABLES.items(): + self._verify_doc(v[2]) + + +class TestPaths(unittest.TestCase): + @classmethod + def setUpClass(cls): + class Config(object): pass + cls.config = config = Config() + config.topsrcdir = mozpath.abspath(os.curdir) + config.topobjdir = mozpath.abspath('obj') + config.external_source_dir = None + + def test_path(self): + config = self.config + ctxt1 = Context(config=config) + ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + ctxt2 = Context(config=config) + ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) + + path1 = Path(ctxt1, 'qux') + self.assertIsInstance(path1, SourcePath) + self.assertEqual(path1, 'qux') + self.assertEqual(path1.full_path, + mozpath.join(config.topsrcdir, 'foo', 'qux')) + + path2 = Path(ctxt2, '../foo/qux') + self.assertIsInstance(path2, SourcePath) + self.assertEqual(path2, '../foo/qux') + self.assertEqual(path2.full_path, + mozpath.join(config.topsrcdir, 'foo', 'qux')) + + self.assertEqual(path1, path2) + + self.assertEqual(path1.join('../../bar/qux').full_path, + mozpath.join(config.topsrcdir, 'bar', 'qux')) + + path1 = Path(ctxt1, '/qux/qux') + self.assertIsInstance(path1, SourcePath) + self.assertEqual(path1, '/qux/qux') + self.assertEqual(path1.full_path, + mozpath.join(config.topsrcdir, 'qux', 'qux')) + + path2 = Path(ctxt2, '/qux/qux') + self.assertIsInstance(path2, SourcePath) + self.assertEqual(path2, '/qux/qux') + self.assertEqual(path2.full_path, + mozpath.join(config.topsrcdir, 'qux', 'qux')) + + self.assertEqual(path1, path2) + + path1 = Path(ctxt1, '!qux') + self.assertIsInstance(path1, ObjDirPath) + self.assertEqual(path1, '!qux') + self.assertEqual(path1.full_path, + mozpath.join(config.topobjdir, 'foo', 'qux')) + + path2 = Path(ctxt2, '!../foo/qux') + self.assertIsInstance(path2, ObjDirPath) + self.assertEqual(path2, '!../foo/qux') + self.assertEqual(path2.full_path, + mozpath.join(config.topobjdir, 'foo', 'qux')) + + self.assertEqual(path1, path2) + + path1 = Path(ctxt1, '!/qux/qux') + self.assertIsInstance(path1, ObjDirPath) + self.assertEqual(path1, '!/qux/qux') + self.assertEqual(path1.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + path2 = Path(ctxt2, '!/qux/qux') + self.assertIsInstance(path2, ObjDirPath) + self.assertEqual(path2, '!/qux/qux') + self.assertEqual(path2.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + self.assertEqual(path1, path2) + + path1 = Path(ctxt1, path1) + self.assertIsInstance(path1, ObjDirPath) + self.assertEqual(path1, '!/qux/qux') + self.assertEqual(path1.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + path2 = Path(ctxt2, path2) + self.assertIsInstance(path2, ObjDirPath) + self.assertEqual(path2, '!/qux/qux') + self.assertEqual(path2.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + self.assertEqual(path1, path2) + + path1 = Path(path1) + self.assertIsInstance(path1, ObjDirPath) + self.assertEqual(path1, '!/qux/qux') + self.assertEqual(path1.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + self.assertEqual(path1, path2) + + path2 = Path(path2) + self.assertIsInstance(path2, ObjDirPath) + self.assertEqual(path2, '!/qux/qux') + self.assertEqual(path2.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + self.assertEqual(path1, path2) + + def test_source_path(self): + config = self.config + ctxt = Context(config=config) + ctxt.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + + path = SourcePath(ctxt, 'qux') + self.assertEqual(path, 'qux') + self.assertEqual(path.full_path, + mozpath.join(config.topsrcdir, 'foo', 'qux')) + self.assertEqual(path.translated, + mozpath.join(config.topobjdir, 'foo', 'qux')) + + path = SourcePath(ctxt, '../bar/qux') + self.assertEqual(path, '../bar/qux') + self.assertEqual(path.full_path, + mozpath.join(config.topsrcdir, 'bar', 'qux')) + self.assertEqual(path.translated, + mozpath.join(config.topobjdir, 'bar', 'qux')) + + path = SourcePath(ctxt, '/qux/qux') + self.assertEqual(path, '/qux/qux') + self.assertEqual(path.full_path, + mozpath.join(config.topsrcdir, 'qux', 'qux')) + self.assertEqual(path.translated, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + with self.assertRaises(ValueError): + SourcePath(ctxt, '!../bar/qux') + + with self.assertRaises(ValueError): + SourcePath(ctxt, '!/qux/qux') + + path = SourcePath(path) + self.assertIsInstance(path, SourcePath) + self.assertEqual(path, '/qux/qux') + self.assertEqual(path.full_path, + mozpath.join(config.topsrcdir, 'qux', 'qux')) + self.assertEqual(path.translated, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + path = Path(path) + self.assertIsInstance(path, SourcePath) + + def test_objdir_path(self): + config = self.config + ctxt = Context(config=config) + ctxt.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + + path = ObjDirPath(ctxt, '!qux') + self.assertEqual(path, '!qux') + self.assertEqual(path.full_path, + mozpath.join(config.topobjdir, 'foo', 'qux')) + + path = ObjDirPath(ctxt, '!../bar/qux') + self.assertEqual(path, '!../bar/qux') + self.assertEqual(path.full_path, + mozpath.join(config.topobjdir, 'bar', 'qux')) + + path = ObjDirPath(ctxt, '!/qux/qux') + self.assertEqual(path, '!/qux/qux') + self.assertEqual(path.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + with self.assertRaises(ValueError): + path = ObjDirPath(ctxt, '../bar/qux') + + with self.assertRaises(ValueError): + path = ObjDirPath(ctxt, '/qux/qux') + + path = ObjDirPath(path) + self.assertIsInstance(path, ObjDirPath) + self.assertEqual(path, '!/qux/qux') + self.assertEqual(path.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + path = Path(path) + self.assertIsInstance(path, ObjDirPath) + + def test_absolute_path(self): + config = self.config + ctxt = Context(config=config) + ctxt.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + + path = AbsolutePath(ctxt, '%/qux') + self.assertEqual(path, '%/qux') + self.assertEqual(path.full_path, '/qux') + + with self.assertRaises(ValueError): + path = AbsolutePath(ctxt, '%qux') + + def test_path_with_mixed_contexts(self): + config = self.config + ctxt1 = Context(config=config) + ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + ctxt2 = Context(config=config) + ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) + + path1 = Path(ctxt1, 'qux') + path2 = Path(ctxt2, path1) + self.assertEqual(path2, path1) + self.assertEqual(path2, 'qux') + self.assertEqual(path2.context, ctxt1) + self.assertEqual(path2.full_path, + mozpath.join(config.topsrcdir, 'foo', 'qux')) + + path1 = Path(ctxt1, '../bar/qux') + path2 = Path(ctxt2, path1) + self.assertEqual(path2, path1) + self.assertEqual(path2, '../bar/qux') + self.assertEqual(path2.context, ctxt1) + self.assertEqual(path2.full_path, + mozpath.join(config.topsrcdir, 'bar', 'qux')) + + path1 = Path(ctxt1, '/qux/qux') + path2 = Path(ctxt2, path1) + self.assertEqual(path2, path1) + self.assertEqual(path2, '/qux/qux') + self.assertEqual(path2.context, ctxt1) + self.assertEqual(path2.full_path, + mozpath.join(config.topsrcdir, 'qux', 'qux')) + + path1 = Path(ctxt1, '!qux') + path2 = Path(ctxt2, path1) + self.assertEqual(path2, path1) + self.assertEqual(path2, '!qux') + self.assertEqual(path2.context, ctxt1) + self.assertEqual(path2.full_path, + mozpath.join(config.topobjdir, 'foo', 'qux')) + + path1 = Path(ctxt1, '!../bar/qux') + path2 = Path(ctxt2, path1) + self.assertEqual(path2, path1) + self.assertEqual(path2, '!../bar/qux') + self.assertEqual(path2.context, ctxt1) + self.assertEqual(path2.full_path, + mozpath.join(config.topobjdir, 'bar', 'qux')) + + path1 = Path(ctxt1, '!/qux/qux') + path2 = Path(ctxt2, path1) + self.assertEqual(path2, path1) + self.assertEqual(path2, '!/qux/qux') + self.assertEqual(path2.context, ctxt1) + self.assertEqual(path2.full_path, + mozpath.join(config.topobjdir, 'qux', 'qux')) + + def test_path_typed_list(self): + config = self.config + ctxt1 = Context(config=config) + ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + ctxt2 = Context(config=config) + ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) + + paths = [ + '!../bar/qux', + '!/qux/qux', + '!qux', + '../bar/qux', + '/qux/qux', + 'qux', + ] + + MyList = ContextDerivedTypedList(Path) + l = MyList(ctxt1) + l += paths + + for p_str, p_path in zip(paths, l): + self.assertEqual(p_str, p_path) + self.assertEqual(p_path, Path(ctxt1, p_str)) + self.assertEqual(p_path.join('foo'), + Path(ctxt1, mozpath.join(p_str, 'foo'))) + + l2 = MyList(ctxt2) + l2 += paths + + for p_str, p_path in zip(paths, l2): + self.assertEqual(p_str, p_path) + self.assertEqual(p_path, Path(ctxt2, p_str)) + + # Assigning with Paths from another context doesn't rebase them + l2 = MyList(ctxt2) + l2 += l + + for p_str, p_path in zip(paths, l2): + self.assertEqual(p_str, p_path) + self.assertEqual(p_path, Path(ctxt1, p_str)) + + MyListWithFlags = ContextDerivedTypedListWithItems( + Path, StrictOrderingOnAppendListWithFlagsFactory({ + 'foo': bool, + })) + l = MyListWithFlags(ctxt1) + l += paths + + for p in paths: + l[p].foo = True + + for p_str, p_path in zip(paths, l): + self.assertEqual(p_str, p_path) + self.assertEqual(p_path, Path(ctxt1, p_str)) + self.assertEqual(l[p_str].foo, True) + self.assertEqual(l[p_path].foo, True) + + def test_path_typed_hierarchy_list(self): + config = self.config + ctxt1 = Context(config=config) + ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + ctxt2 = Context(config=config) + ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) + + paths = [ + '!../bar/qux', + '!/qux/qux', + '!qux', + '../bar/qux', + '/qux/qux', + 'qux', + ] + + MyList = ContextDerivedTypedHierarchicalStringList(Path) + l = MyList(ctxt1) + l += paths + l.subdir += paths + + for _, files in l.walk(): + for p_str, p_path in zip(paths, files): + self.assertEqual(p_str, p_path) + self.assertEqual(p_path, Path(ctxt1, p_str)) + self.assertEqual(p_path.join('foo'), + Path(ctxt1, mozpath.join(p_str, 'foo'))) + + l2 = MyList(ctxt2) + l2 += paths + l2.subdir += paths + + for _, files in l2.walk(): + for p_str, p_path in zip(paths, files): + self.assertEqual(p_str, p_path) + self.assertEqual(p_path, Path(ctxt2, p_str)) + + # Assigning with Paths from another context doesn't rebase them + l2 = MyList(ctxt2) + l2 += l + + for _, files in l2.walk(): + for p_str, p_path in zip(paths, files): + self.assertEqual(p_str, p_path) + self.assertEqual(p_path, Path(ctxt1, p_str)) + + +class TestTypedRecord(unittest.TestCase): + + def test_fields(self): + T = ContextDerivedTypedRecord(('field1', unicode), + ('field2', list)) + inst = T(None) + self.assertEqual(inst.field1, '') + self.assertEqual(inst.field2, []) + + inst.field1 = 'foo' + inst.field2 += ['bar'] + + self.assertEqual(inst.field1, 'foo') + self.assertEqual(inst.field2, ['bar']) + + with self.assertRaises(AttributeError): + inst.field3 = [] + + def test_coercion(self): + T = ContextDerivedTypedRecord(('field1', unicode), + ('field2', list)) + inst = T(None) + inst.field1 = 3 + inst.field2 += ('bar',) + self.assertEqual(inst.field1, '3') + self.assertEqual(inst.field2, ['bar']) + + with self.assertRaises(TypeError): + inst.field2 = object() + + +class TestFiles(unittest.TestCase): + def test_aggregate_empty(self): + c = Context({}) + + files = {'moz.build': Files(c, pattern='**')} + + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [], + 'recommended_bug_component': None, + }) + + def test_single_bug_component(self): + c = Context({}) + f = Files(c, pattern='**') + f['BUG_COMPONENT'] = (u'Product1', u'Component1') + + files = {'moz.build': f} + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [((u'Product1', u'Component1'), 1)], + 'recommended_bug_component': (u'Product1', u'Component1'), + }) + + def test_multiple_bug_components(self): + c = Context({}) + f1 = Files(c, pattern='**') + f1['BUG_COMPONENT'] = (u'Product1', u'Component1') + + f2 = Files(c, pattern='**') + f2['BUG_COMPONENT'] = (u'Product2', u'Component2') + + files = {'a': f1, 'b': f2, 'c': f1} + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [ + ((u'Product1', u'Component1'), 2), + ((u'Product2', u'Component2'), 1), + ], + 'recommended_bug_component': (u'Product1', u'Component1'), + }) + + def test_no_recommended_bug_component(self): + """If there is no clear count winner, we don't recommend a bug component.""" + c = Context({}) + f1 = Files(c, pattern='**') + f1['BUG_COMPONENT'] = (u'Product1', u'Component1') + + f2 = Files(c, pattern='**') + f2['BUG_COMPONENT'] = (u'Product2', u'Component2') + + files = {'a': f1, 'b': f2} + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [ + ((u'Product1', u'Component1'), 1), + ((u'Product2', u'Component2'), 1), + ], + 'recommended_bug_component': None, + }) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py new file mode 100644 index 000000000..6ac4e0aac --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -0,0 +1,1172 @@ +# 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 mozunit import main + +from mozbuild.frontend.context import ( + ObjDirPath, + Path, +) +from mozbuild.frontend.data import ( + AndroidResDirs, + BrandingFiles, + ChromeManifestEntry, + ConfigFileSubstitution, + Defines, + DirectoryTraversal, + Exports, + FinalTargetPreprocessedFiles, + GeneratedFile, + GeneratedSources, + HostDefines, + HostSources, + IPDLFile, + JARManifest, + LinkageMultipleRustLibrariesError, + LocalInclude, + Program, + RustLibrary, + SdkFiles, + SharedLibrary, + SimpleProgram, + Sources, + StaticLibrary, + TestHarnessFiles, + TestManifest, + UnifiedSources, + VariablePassthru, +) +from mozbuild.frontend.emitter import TreeMetadataEmitter +from mozbuild.frontend.reader import ( + BuildReader, + BuildReaderError, + SandboxValidationError, +) +from mozpack.chrome import manifest + +from mozbuild.test.common import MockConfig + +import mozpack.path as mozpath + + +data_path = mozpath.abspath(mozpath.dirname(__file__)) +data_path = mozpath.join(data_path, 'data') + + +class TestEmitterBasic(unittest.TestCase): + def setUp(self): + self._old_env = dict(os.environ) + os.environ.pop('MOZ_OBJDIR', None) + + def tearDown(self): + os.environ.clear() + os.environ.update(self._old_env) + + def reader(self, name, enable_tests=False, extra_substs=None): + substs = dict( + ENABLE_TESTS='1' if enable_tests else '', + BIN_SUFFIX='.prog', + OS_TARGET='WINNT', + COMPILE_ENVIRONMENT='1', + ) + if extra_substs: + substs.update(extra_substs) + config = MockConfig(mozpath.join(data_path, name), extra_substs=substs) + + return BuildReader(config) + + def read_topsrcdir(self, reader, filter_common=True): + emitter = TreeMetadataEmitter(reader.config) + objs = list(emitter.emit(reader.read_topsrcdir())) + self.assertGreater(len(objs), 0) + + filtered = [] + for obj in objs: + if filter_common and isinstance(obj, DirectoryTraversal): + continue + + filtered.append(obj) + + return filtered + + def test_dirs_traversal_simple(self): + reader = self.reader('traversal-simple') + objs = self.read_topsrcdir(reader, filter_common=False) + self.assertEqual(len(objs), 4) + + for o in objs: + self.assertIsInstance(o, DirectoryTraversal) + self.assertTrue(os.path.isabs(o.context_main_path)) + self.assertEqual(len(o.context_all_paths), 1) + + reldirs = [o.relativedir for o in objs] + self.assertEqual(reldirs, ['', 'foo', 'foo/biz', 'bar']) + + dirs = [[d.full_path for d in o.dirs] for o in objs] + self.assertEqual(dirs, [ + [ + mozpath.join(reader.config.topsrcdir, 'foo'), + mozpath.join(reader.config.topsrcdir, 'bar') + ], [ + mozpath.join(reader.config.topsrcdir, 'foo', 'biz') + ], [], []]) + + def test_traversal_all_vars(self): + reader = self.reader('traversal-all-vars') + objs = self.read_topsrcdir(reader, filter_common=False) + self.assertEqual(len(objs), 2) + + for o in objs: + self.assertIsInstance(o, DirectoryTraversal) + + reldirs = set([o.relativedir for o in objs]) + self.assertEqual(reldirs, set(['', 'regular'])) + + for o in objs: + reldir = o.relativedir + + if reldir == '': + self.assertEqual([d.full_path for d in o.dirs], [ + mozpath.join(reader.config.topsrcdir, 'regular')]) + + def test_traversal_all_vars_enable_tests(self): + reader = self.reader('traversal-all-vars', enable_tests=True) + objs = self.read_topsrcdir(reader, filter_common=False) + self.assertEqual(len(objs), 3) + + for o in objs: + self.assertIsInstance(o, DirectoryTraversal) + + reldirs = set([o.relativedir for o in objs]) + self.assertEqual(reldirs, set(['', 'regular', 'test'])) + + for o in objs: + reldir = o.relativedir + + if reldir == '': + self.assertEqual([d.full_path for d in o.dirs], [ + mozpath.join(reader.config.topsrcdir, 'regular'), + mozpath.join(reader.config.topsrcdir, 'test')]) + + def test_config_file_substitution(self): + reader = self.reader('config-file-substitution') + objs = self.read_topsrcdir(reader) + self.assertEqual(len(objs), 2) + + self.assertIsInstance(objs[0], ConfigFileSubstitution) + self.assertIsInstance(objs[1], ConfigFileSubstitution) + + topobjdir = mozpath.abspath(reader.config.topobjdir) + self.assertEqual(objs[0].relpath, 'foo') + self.assertEqual(mozpath.normpath(objs[0].output_path), + mozpath.normpath(mozpath.join(topobjdir, 'foo'))) + self.assertEqual(mozpath.normpath(objs[1].output_path), + mozpath.normpath(mozpath.join(topobjdir, 'bar'))) + + def test_variable_passthru(self): + reader = self.reader('variable-passthru') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], VariablePassthru) + + wanted = { + 'ALLOW_COMPILER_WARNINGS': True, + 'DISABLE_STL_WRAPPING': True, + 'NO_DIST_INSTALL': True, + 'VISIBILITY_FLAGS': '', + 'RCFILE': 'foo.rc', + 'RESFILE': 'bar.res', + 'RCINCLUDE': 'bar.rc', + 'DEFFILE': 'baz.def', + 'MOZBUILD_CFLAGS': ['-fno-exceptions', '-w'], + 'MOZBUILD_CXXFLAGS': ['-fcxx-exceptions', '-include foo.h'], + 'MOZBUILD_LDFLAGS': ['-framework Foo', '-x', '-DELAYLOAD:foo.dll', + '-DELAYLOAD:bar.dll'], + 'MOZBUILD_HOST_CFLAGS': ['-funroll-loops', '-wall'], + 'MOZBUILD_HOST_CXXFLAGS': ['-funroll-loops-harder', + '-wall-day-everyday'], + 'WIN32_EXE_LDFLAGS': ['-subsystem:console'], + } + + variables = objs[0].variables + maxDiff = self.maxDiff + self.maxDiff = None + self.assertEqual(wanted, variables) + self.maxDiff = maxDiff + + def test_use_yasm(self): + # When yasm is not available, this should raise. + reader = self.reader('use-yasm') + with self.assertRaisesRegexp(SandboxValidationError, + 'yasm is not available'): + self.read_topsrcdir(reader) + + # When yasm is available, this should work. + reader = self.reader('use-yasm', + extra_substs=dict( + YASM='yasm', + YASM_ASFLAGS='-foo', + )) + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], VariablePassthru) + maxDiff = self.maxDiff + self.maxDiff = None + self.assertEqual(objs[0].variables, + {'AS': 'yasm', + 'ASFLAGS': '-foo', + 'AS_DASH_C_FLAG': ''}) + self.maxDiff = maxDiff + + + def test_generated_files(self): + reader = self.reader('generated-files') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 3) + for o in objs: + self.assertIsInstance(o, GeneratedFile) + + expected = ['bar.c', 'foo.c', ('xpidllex.py', 'xpidlyacc.py'), ] + for o, f in zip(objs, expected): + expected_filename = f if isinstance(f, tuple) else (f,) + self.assertEqual(o.outputs, expected_filename) + self.assertEqual(o.script, None) + self.assertEqual(o.method, None) + self.assertEqual(o.inputs, []) + + def test_generated_files_method_names(self): + reader = self.reader('generated-files-method-names') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 2) + for o in objs: + self.assertIsInstance(o, GeneratedFile) + + expected = ['bar.c', 'foo.c'] + expected_method_names = ['make_bar', 'main'] + for o, expected_filename, expected_method in zip(objs, expected, expected_method_names): + self.assertEqual(o.outputs, (expected_filename,)) + self.assertEqual(o.method, expected_method) + self.assertEqual(o.inputs, []) + + def test_generated_files_absolute_script(self): + reader = self.reader('generated-files-absolute-script') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + + o = objs[0] + self.assertIsInstance(o, GeneratedFile) + self.assertEqual(o.outputs, ('bar.c',)) + self.assertRegexpMatches(o.script, 'script.py$') + self.assertEqual(o.method, 'make_bar') + self.assertEqual(o.inputs, []) + + def test_generated_files_no_script(self): + reader = self.reader('generated-files-no-script') + with self.assertRaisesRegexp(SandboxValidationError, + 'Script for generating bar.c does not exist'): + self.read_topsrcdir(reader) + + def test_generated_files_no_inputs(self): + reader = self.reader('generated-files-no-inputs') + with self.assertRaisesRegexp(SandboxValidationError, + 'Input for generating foo.c does not exist'): + self.read_topsrcdir(reader) + + def test_generated_files_no_python_script(self): + reader = self.reader('generated-files-no-python-script') + with self.assertRaisesRegexp(SandboxValidationError, + 'Script for generating bar.c does not end in .py'): + self.read_topsrcdir(reader) + + def test_exports(self): + reader = self.reader('exports') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], Exports) + + expected = [ + ('', ['foo.h', 'bar.h', 'baz.h']), + ('mozilla', ['mozilla1.h', 'mozilla2.h']), + ('mozilla/dom', ['dom1.h', 'dom2.h', 'dom3.h']), + ('mozilla/gfx', ['gfx.h']), + ('nspr/private', ['pprio.h', 'pprthred.h']), + ('vpx', ['mem.h', 'mem2.h']), + ] + for (expect_path, expect_headers), (actual_path, actual_headers) in \ + zip(expected, [(path, list(seq)) for path, seq in objs[0].files.walk()]): + self.assertEqual(expect_path, actual_path) + self.assertEqual(expect_headers, actual_headers) + + def test_exports_missing(self): + ''' + Missing files in EXPORTS is an error. + ''' + reader = self.reader('exports-missing') + with self.assertRaisesRegexp(SandboxValidationError, + 'File listed in EXPORTS does not exist:'): + self.read_topsrcdir(reader) + + def test_exports_missing_generated(self): + ''' + An objdir file in EXPORTS that is not in GENERATED_FILES is an error. + ''' + reader = self.reader('exports-missing-generated') + with self.assertRaisesRegexp(SandboxValidationError, + 'Objdir file listed in EXPORTS not in GENERATED_FILES:'): + self.read_topsrcdir(reader) + + def test_exports_generated(self): + reader = self.reader('exports-generated') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 2) + self.assertIsInstance(objs[0], GeneratedFile) + self.assertIsInstance(objs[1], Exports) + exports = [(path, list(seq)) for path, seq in objs[1].files.walk()] + self.assertEqual(exports, + [('', ['foo.h']), + ('mozilla', ['mozilla1.h', '!mozilla2.h'])]) + path, files = exports[1] + self.assertIsInstance(files[1], ObjDirPath) + + def test_test_harness_files(self): + reader = self.reader('test-harness-files') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], TestHarnessFiles) + + expected = { + 'mochitest': ['runtests.py', 'utils.py'], + 'testing/mochitest': ['mochitest.py', 'mochitest.ini'], + } + + for path, strings in objs[0].files.walk(): + self.assertTrue(path in expected) + basenames = sorted(mozpath.basename(s) for s in strings) + self.assertEqual(sorted(expected[path]), basenames) + + def test_test_harness_files_root(self): + reader = self.reader('test-harness-files-root') + with self.assertRaisesRegexp(SandboxValidationError, + 'Cannot install files to the root of TEST_HARNESS_FILES'): + self.read_topsrcdir(reader) + + def test_branding_files(self): + reader = self.reader('branding-files') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], BrandingFiles) + + files = objs[0].files + + self.assertEqual(files._strings, ['bar.ico', 'baz.png', 'foo.xpm']) + + self.assertIn('icons', files._children) + icons = files._children['icons'] + + self.assertEqual(icons._strings, ['quux.icns']) + + def test_sdk_files(self): + reader = self.reader('sdk-files') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], SdkFiles) + + files = objs[0].files + + self.assertEqual(files._strings, ['bar.ico', 'baz.png', 'foo.xpm']) + + self.assertIn('icons', files._children) + icons = files._children['icons'] + + self.assertEqual(icons._strings, ['quux.icns']) + + def test_program(self): + reader = self.reader('program') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 3) + self.assertIsInstance(objs[0], Program) + self.assertIsInstance(objs[1], SimpleProgram) + self.assertIsInstance(objs[2], SimpleProgram) + + self.assertEqual(objs[0].program, 'test_program.prog') + self.assertEqual(objs[1].program, 'test_program1.prog') + self.assertEqual(objs[2].program, 'test_program2.prog') + + def test_test_manifest_missing_manifest(self): + """A missing manifest file should result in an error.""" + reader = self.reader('test-manifest-missing-manifest') + + with self.assertRaisesRegexp(BuildReaderError, 'IOError: Missing files'): + self.read_topsrcdir(reader) + + def test_empty_test_manifest_rejected(self): + """A test manifest without any entries is rejected.""" + reader = self.reader('test-manifest-empty') + + with self.assertRaisesRegexp(SandboxValidationError, 'Empty test manifest'): + self.read_topsrcdir(reader) + + + def test_test_manifest_just_support_files(self): + """A test manifest with no tests but support-files is not supported.""" + reader = self.reader('test-manifest-just-support') + + with self.assertRaisesRegexp(SandboxValidationError, 'Empty test manifest'): + self.read_topsrcdir(reader) + + def test_test_manifest_dupe_support_files(self): + """A test manifest with dupe support-files in a single test is not + supported. + """ + reader = self.reader('test-manifest-dupes') + + with self.assertRaisesRegexp(SandboxValidationError, 'bar.js appears multiple times ' + 'in a test manifest under a support-files field, please omit the duplicate entry.'): + self.read_topsrcdir(reader) + + def test_test_manifest_absolute_support_files(self): + """Support files starting with '/' are placed relative to the install root""" + reader = self.reader('test-manifest-absolute-support') + + objs = self.read_topsrcdir(reader) + self.assertEqual(len(objs), 1) + o = objs[0] + self.assertEqual(len(o.installs), 3) + expected = [ + mozpath.normpath(mozpath.join(o.install_prefix, "../.well-known/foo.txt")), + mozpath.join(o.install_prefix, "absolute-support.ini"), + mozpath.join(o.install_prefix, "test_file.js"), + ] + paths = sorted([v[0] for v in o.installs.values()]) + self.assertEqual(paths, expected) + + @unittest.skip('Bug 1304316 - Items in the second set but not the first') + def test_test_manifest_shared_support_files(self): + """Support files starting with '!' are given separate treatment, so their + installation can be resolved when running tests. + """ + reader = self.reader('test-manifest-shared-support') + supported, child = self.read_topsrcdir(reader) + + expected_deferred_installs = { + '!/child/test_sub.js', + '!/child/another-file.sjs', + '!/child/data/**', + } + + self.assertEqual(len(supported.installs), 3) + self.assertEqual(set(supported.deferred_installs), + expected_deferred_installs) + self.assertEqual(len(child.installs), 3) + self.assertEqual(len(child.pattern_installs), 1) + + def test_test_manifest_deffered_install_missing(self): + """A non-existent shared support file reference produces an error.""" + reader = self.reader('test-manifest-shared-missing') + + with self.assertRaisesRegexp(SandboxValidationError, + 'entry in support-files not present in the srcdir'): + self.read_topsrcdir(reader) + + def test_test_manifest_install_to_subdir(self): + """ """ + reader = self.reader('test-manifest-install-subdir') + + objs = self.read_topsrcdir(reader) + self.assertEqual(len(objs), 1) + o = objs[0] + self.assertEqual(len(o.installs), 3) + self.assertEqual(o.manifest_relpath, "subdir.ini") + self.assertEqual(o.manifest_obj_relpath, "subdir/subdir.ini") + expected = [ + mozpath.normpath(mozpath.join(o.install_prefix, "subdir/subdir.ini")), + mozpath.normpath(mozpath.join(o.install_prefix, "subdir/support.txt")), + mozpath.normpath(mozpath.join(o.install_prefix, "subdir/test_foo.html")), + ] + paths = sorted([v[0] for v in o.installs.values()]) + self.assertEqual(paths, expected) + + def test_test_manifest_install_includes(self): + """Ensure that any [include:foo.ini] are copied to the objdir.""" + reader = self.reader('test-manifest-install-includes') + + objs = self.read_topsrcdir(reader) + self.assertEqual(len(objs), 1) + o = objs[0] + self.assertEqual(len(o.installs), 3) + self.assertEqual(o.manifest_relpath, "mochitest.ini") + self.assertEqual(o.manifest_obj_relpath, "subdir/mochitest.ini") + expected = [ + mozpath.normpath(mozpath.join(o.install_prefix, "subdir/common.ini")), + mozpath.normpath(mozpath.join(o.install_prefix, "subdir/mochitest.ini")), + mozpath.normpath(mozpath.join(o.install_prefix, "subdir/test_foo.html")), + ] + paths = sorted([v[0] for v in o.installs.values()]) + self.assertEqual(paths, expected) + + def test_test_manifest_includes(self): + """Ensure that manifest objects from the emitter list a correct manifest. + """ + reader = self.reader('test-manifest-emitted-includes') + [obj] = self.read_topsrcdir(reader) + + # Expected manifest leafs for our tests. + expected_manifests = { + 'reftest1.html': 'reftest.list', + 'reftest1-ref.html': 'reftest.list', + 'reftest2.html': 'included-reftest.list', + 'reftest2-ref.html': 'included-reftest.list', + } + + for t in obj.tests: + self.assertTrue(t['manifest'].endswith(expected_manifests[t['name']])) + + def test_python_unit_test_missing(self): + """Missing files in PYTHON_UNIT_TESTS should raise.""" + reader = self.reader('test-python-unit-test-missing') + with self.assertRaisesRegexp(SandboxValidationError, + 'Path specified in PYTHON_UNIT_TESTS does not exist:'): + self.read_topsrcdir(reader) + + def test_test_manifest_keys_extracted(self): + """Ensure all metadata from test manifests is extracted.""" + reader = self.reader('test-manifest-keys-extracted') + + objs = [o for o in self.read_topsrcdir(reader) + if isinstance(o, TestManifest)] + + self.assertEqual(len(objs), 9) + + metadata = { + 'a11y.ini': { + 'flavor': 'a11y', + 'installs': { + 'a11y.ini': False, + 'test_a11y.js': True, + }, + 'pattern-installs': 1, + }, + 'browser.ini': { + 'flavor': 'browser-chrome', + 'installs': { + 'browser.ini': False, + 'test_browser.js': True, + 'support1': False, + 'support2': False, + }, + }, + 'metro.ini': { + 'flavor': 'metro-chrome', + 'installs': { + 'metro.ini': False, + 'test_metro.js': True, + }, + }, + 'mochitest.ini': { + 'flavor': 'mochitest', + 'installs': { + 'mochitest.ini': False, + 'test_mochitest.js': True, + }, + 'external': { + 'external1', + 'external2', + }, + }, + 'chrome.ini': { + 'flavor': 'chrome', + 'installs': { + 'chrome.ini': False, + 'test_chrome.js': True, + }, + }, + 'xpcshell.ini': { + 'flavor': 'xpcshell', + 'dupe': True, + 'installs': { + 'xpcshell.ini': False, + 'test_xpcshell.js': True, + 'head1': False, + 'head2': False, + 'tail1': False, + 'tail2': False, + }, + }, + 'reftest.list': { + 'flavor': 'reftest', + 'installs': {}, + }, + 'crashtest.list': { + 'flavor': 'crashtest', + 'installs': {}, + }, + 'moz.build': { + 'flavor': 'python', + 'installs': {}, + } + } + + for o in objs: + m = metadata[mozpath.basename(o.manifest_relpath)] + + self.assertTrue(o.path.startswith(o.directory)) + self.assertEqual(o.flavor, m['flavor']) + self.assertEqual(o.dupe_manifest, m.get('dupe', False)) + + external_normalized = set(mozpath.basename(p) for p in + o.external_installs) + self.assertEqual(external_normalized, m.get('external', set())) + + self.assertEqual(len(o.installs), len(m['installs'])) + for path in o.installs.keys(): + self.assertTrue(path.startswith(o.directory)) + relpath = path[len(o.directory)+1:] + + self.assertIn(relpath, m['installs']) + self.assertEqual(o.installs[path][1], m['installs'][relpath]) + + if 'pattern-installs' in m: + self.assertEqual(len(o.pattern_installs), m['pattern-installs']) + + def test_test_manifest_unmatched_generated(self): + reader = self.reader('test-manifest-unmatched-generated') + + with self.assertRaisesRegexp(SandboxValidationError, + 'entry in generated-files not present elsewhere'): + self.read_topsrcdir(reader), + + def test_test_manifest_parent_support_files_dir(self): + """support-files referencing a file in a parent directory works.""" + reader = self.reader('test-manifest-parent-support-files-dir') + + objs = [o for o in self.read_topsrcdir(reader) + if isinstance(o, TestManifest)] + + self.assertEqual(len(objs), 1) + + o = objs[0] + + expected = mozpath.join(o.srcdir, 'support-file.txt') + self.assertIn(expected, o.installs) + self.assertEqual(o.installs[expected], + ('testing/mochitest/tests/child/support-file.txt', False)) + + def test_test_manifest_missing_test_error(self): + """Missing test files should result in error.""" + reader = self.reader('test-manifest-missing-test-file') + + with self.assertRaisesRegexp(SandboxValidationError, + 'lists test that does not exist: test_missing.html'): + self.read_topsrcdir(reader) + + def test_test_manifest_missing_test_error_unfiltered(self): + """Missing test files should result in error, even when the test list is not filtered.""" + reader = self.reader('test-manifest-missing-test-file-unfiltered') + + with self.assertRaisesRegexp(SandboxValidationError, + 'lists test that does not exist: missing.js'): + self.read_topsrcdir(reader) + + def test_ipdl_sources(self): + reader = self.reader('ipdl_sources') + objs = self.read_topsrcdir(reader) + + ipdls = [] + for o in objs: + if isinstance(o, IPDLFile): + ipdls.append('%s/%s' % (o.relativedir, o.basename)) + + expected = [ + 'bar/bar.ipdl', + 'bar/bar2.ipdlh', + 'foo/foo.ipdl', + 'foo/foo2.ipdlh', + ] + + self.assertEqual(ipdls, expected) + + def test_local_includes(self): + """Test that LOCAL_INCLUDES is emitted correctly.""" + reader = self.reader('local_includes') + objs = self.read_topsrcdir(reader) + + local_includes = [o.path for o in objs if isinstance(o, LocalInclude)] + expected = [ + '/bar/baz', + 'foo', + ] + + self.assertEqual(local_includes, expected) + + local_includes = [o.path.full_path + for o in objs if isinstance(o, LocalInclude)] + expected = [ + mozpath.join(reader.config.topsrcdir, 'bar/baz'), + mozpath.join(reader.config.topsrcdir, 'foo'), + ] + + self.assertEqual(local_includes, expected) + + def test_generated_includes(self): + """Test that GENERATED_INCLUDES is emitted correctly.""" + reader = self.reader('generated_includes') + objs = self.read_topsrcdir(reader) + + generated_includes = [o.path for o in objs if isinstance(o, LocalInclude)] + expected = [ + '!/bar/baz', + '!foo', + ] + + self.assertEqual(generated_includes, expected) + + generated_includes = [o.path.full_path + for o in objs if isinstance(o, LocalInclude)] + expected = [ + mozpath.join(reader.config.topobjdir, 'bar/baz'), + mozpath.join(reader.config.topobjdir, 'foo'), + ] + + self.assertEqual(generated_includes, expected) + + def test_defines(self): + reader = self.reader('defines') + objs = self.read_topsrcdir(reader) + + defines = {} + for o in objs: + if isinstance(o, Defines): + defines = o.defines + + expected = { + 'BAR': 7, + 'BAZ': '"abcd"', + 'FOO': True, + 'VALUE': 'xyz', + 'QUX': False, + } + + self.assertEqual(defines, expected) + + def test_host_defines(self): + reader = self.reader('host-defines') + objs = self.read_topsrcdir(reader) + + defines = {} + for o in objs: + if isinstance(o, HostDefines): + defines = o.defines + + expected = { + 'BAR': 7, + 'BAZ': '"abcd"', + 'FOO': True, + 'VALUE': 'xyz', + 'QUX': False, + } + + self.assertEqual(defines, expected) + + def test_jar_manifests(self): + reader = self.reader('jar-manifests') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + for obj in objs: + self.assertIsInstance(obj, JARManifest) + self.assertIsInstance(obj.path, Path) + + def test_jar_manifests_multiple_files(self): + with self.assertRaisesRegexp(SandboxValidationError, 'limited to one value'): + reader = self.reader('jar-manifests-multiple-files') + self.read_topsrcdir(reader) + + def test_xpidl_module_no_sources(self): + """XPIDL_MODULE without XPIDL_SOURCES should be rejected.""" + with self.assertRaisesRegexp(SandboxValidationError, 'XPIDL_MODULE ' + 'cannot be defined'): + reader = self.reader('xpidl-module-no-sources') + self.read_topsrcdir(reader) + + def test_missing_local_includes(self): + """LOCAL_INCLUDES containing non-existent directories should be rejected.""" + with self.assertRaisesRegexp(SandboxValidationError, 'Path specified in ' + 'LOCAL_INCLUDES does not exist'): + reader = self.reader('missing-local-includes') + self.read_topsrcdir(reader) + + def test_library_defines(self): + """Test that LIBRARY_DEFINES is propagated properly.""" + reader = self.reader('library-defines') + objs = self.read_topsrcdir(reader) + + libraries = [o for o in objs if isinstance(o,StaticLibrary)] + expected = { + 'liba': '-DIN_LIBA', + 'libb': '-DIN_LIBA -DIN_LIBB', + 'libc': '-DIN_LIBA -DIN_LIBB', + 'libd': '' + } + defines = {} + for lib in libraries: + defines[lib.basename] = ' '.join(lib.lib_defines.get_defines()) + self.assertEqual(expected, defines) + + def test_sources(self): + """Test that SOURCES works properly.""" + reader = self.reader('sources') + objs = self.read_topsrcdir(reader) + + # The last object is a Linkable. + linkable = objs.pop() + self.assertTrue(linkable.cxx_link) + self.assertEqual(len(objs), 6) + for o in objs: + self.assertIsInstance(o, Sources) + + suffix_map = {obj.canonical_suffix: obj for obj in objs} + self.assertEqual(len(suffix_map), 6) + + expected = { + '.cpp': ['a.cpp', 'b.cc', 'c.cxx'], + '.c': ['d.c'], + '.m': ['e.m'], + '.mm': ['f.mm'], + '.S': ['g.S'], + '.s': ['h.s', 'i.asm'], + } + for suffix, files in expected.items(): + sources = suffix_map[suffix] + self.assertEqual( + sources.files, + [mozpath.join(reader.config.topsrcdir, f) for f in files]) + + def test_sources_just_c(self): + """Test that a linkable with no C++ sources doesn't have cxx_link set.""" + reader = self.reader('sources-just-c') + objs = self.read_topsrcdir(reader) + + # The last object is a Linkable. + linkable = objs.pop() + self.assertFalse(linkable.cxx_link) + + def test_linkables_cxx_link(self): + """Test that linkables transitively set cxx_link properly.""" + reader = self.reader('test-linkables-cxx-link') + got_results = 0 + for obj in self.read_topsrcdir(reader): + if isinstance(obj, SharedLibrary): + if obj.basename == 'cxx_shared': + self.assertTrue(obj.cxx_link) + got_results += 1 + elif obj.basename == 'just_c_shared': + self.assertFalse(obj.cxx_link) + got_results += 1 + self.assertEqual(got_results, 2) + + def test_generated_sources(self): + """Test that GENERATED_SOURCES works properly.""" + reader = self.reader('generated-sources') + objs = self.read_topsrcdir(reader) + + # The last object is a Linkable. + linkable = objs.pop() + self.assertTrue(linkable.cxx_link) + self.assertEqual(len(objs), 6) + + generated_sources = [o for o in objs if isinstance(o, GeneratedSources)] + self.assertEqual(len(generated_sources), 6) + + suffix_map = {obj.canonical_suffix: obj for obj in generated_sources} + self.assertEqual(len(suffix_map), 6) + + expected = { + '.cpp': ['a.cpp', 'b.cc', 'c.cxx'], + '.c': ['d.c'], + '.m': ['e.m'], + '.mm': ['f.mm'], + '.S': ['g.S'], + '.s': ['h.s', 'i.asm'], + } + for suffix, files in expected.items(): + sources = suffix_map[suffix] + self.assertEqual( + sources.files, + [mozpath.join(reader.config.topobjdir, f) for f in files]) + + def test_host_sources(self): + """Test that HOST_SOURCES works properly.""" + reader = self.reader('host-sources') + objs = self.read_topsrcdir(reader) + + # The last object is a Linkable + linkable = objs.pop() + self.assertTrue(linkable.cxx_link) + self.assertEqual(len(objs), 3) + for o in objs: + self.assertIsInstance(o, HostSources) + + suffix_map = {obj.canonical_suffix: obj for obj in objs} + self.assertEqual(len(suffix_map), 3) + + expected = { + '.cpp': ['a.cpp', 'b.cc', 'c.cxx'], + '.c': ['d.c'], + '.mm': ['e.mm', 'f.mm'], + } + for suffix, files in expected.items(): + sources = suffix_map[suffix] + self.assertEqual( + sources.files, + [mozpath.join(reader.config.topsrcdir, f) for f in files]) + + def test_unified_sources(self): + """Test that UNIFIED_SOURCES works properly.""" + reader = self.reader('unified-sources') + objs = self.read_topsrcdir(reader) + + # The last object is a Linkable, ignore it + objs = objs[:-1] + self.assertEqual(len(objs), 3) + for o in objs: + self.assertIsInstance(o, UnifiedSources) + + suffix_map = {obj.canonical_suffix: obj for obj in objs} + self.assertEqual(len(suffix_map), 3) + + expected = { + '.cpp': ['bar.cxx', 'foo.cpp', 'quux.cc'], + '.mm': ['objc1.mm', 'objc2.mm'], + '.c': ['c1.c', 'c2.c'], + } + for suffix, files in expected.items(): + sources = suffix_map[suffix] + self.assertEqual( + sources.files, + [mozpath.join(reader.config.topsrcdir, f) for f in files]) + self.assertTrue(sources.have_unified_mapping) + + def test_unified_sources_non_unified(self): + """Test that UNIFIED_SOURCES with FILES_PER_UNIFIED_FILE=1 works properly.""" + reader = self.reader('unified-sources-non-unified') + objs = self.read_topsrcdir(reader) + + # The last object is a Linkable, ignore it + objs = objs[:-1] + self.assertEqual(len(objs), 3) + for o in objs: + self.assertIsInstance(o, UnifiedSources) + + suffix_map = {obj.canonical_suffix: obj for obj in objs} + self.assertEqual(len(suffix_map), 3) + + expected = { + '.cpp': ['bar.cxx', 'foo.cpp', 'quux.cc'], + '.mm': ['objc1.mm', 'objc2.mm'], + '.c': ['c1.c', 'c2.c'], + } + for suffix, files in expected.items(): + sources = suffix_map[suffix] + self.assertEqual( + sources.files, + [mozpath.join(reader.config.topsrcdir, f) for f in files]) + self.assertFalse(sources.have_unified_mapping) + + def test_final_target_pp_files(self): + """Test that FINAL_TARGET_PP_FILES works properly.""" + reader = self.reader('dist-files') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], FinalTargetPreprocessedFiles) + + # Ideally we'd test hierarchies, but that would just be testing + # the HierarchicalStringList class, which we test separately. + for path, files in objs[0].files.walk(): + self.assertEqual(path, '') + self.assertEqual(len(files), 2) + + expected = {'install.rdf', 'main.js'} + for f in files: + self.assertTrue(unicode(f) in expected) + + def test_missing_final_target_pp_files(self): + """Test that FINAL_TARGET_PP_FILES with missing files throws errors.""" + with self.assertRaisesRegexp(SandboxValidationError, 'File listed in ' + 'FINAL_TARGET_PP_FILES does not exist'): + reader = self.reader('dist-files-missing') + self.read_topsrcdir(reader) + + def test_final_target_pp_files_non_srcdir(self): + '''Test that non-srcdir paths in FINAL_TARGET_PP_FILES throws errors.''' + reader = self.reader('final-target-pp-files-non-srcdir') + with self.assertRaisesRegexp(SandboxValidationError, + 'Only source directory paths allowed in FINAL_TARGET_PP_FILES:'): + self.read_topsrcdir(reader) + + def test_rust_library_no_cargo_toml(self): + '''Test that defining a RustLibrary without a Cargo.toml fails.''' + reader = self.reader('rust-library-no-cargo-toml') + with self.assertRaisesRegexp(SandboxValidationError, + 'No Cargo.toml file found'): + self.read_topsrcdir(reader) + + def test_rust_library_name_mismatch(self): + '''Test that defining a RustLibrary that doesn't match Cargo.toml fails.''' + reader = self.reader('rust-library-name-mismatch') + with self.assertRaisesRegexp(SandboxValidationError, + 'library.*does not match Cargo.toml-defined package'): + self.read_topsrcdir(reader) + + def test_rust_library_no_lib_section(self): + '''Test that a RustLibrary Cargo.toml with no [lib] section fails.''' + reader = self.reader('rust-library-no-lib-section') + with self.assertRaisesRegexp(SandboxValidationError, + 'Cargo.toml for.* has no \\[lib\\] section'): + self.read_topsrcdir(reader) + + def test_rust_library_no_profile_section(self): + '''Test that a RustLibrary Cargo.toml with no [profile] section fails.''' + reader = self.reader('rust-library-no-profile-section') + with self.assertRaisesRegexp(SandboxValidationError, + 'Cargo.toml for.* has no \\[profile\\.dev\\] section'): + self.read_topsrcdir(reader) + + def test_rust_library_invalid_crate_type(self): + '''Test that a RustLibrary Cargo.toml has a permitted crate-type.''' + reader = self.reader('rust-library-invalid-crate-type') + with self.assertRaisesRegexp(SandboxValidationError, + 'crate-type.* is not permitted'): + self.read_topsrcdir(reader) + + def test_rust_library_non_abort_panic(self): + '''Test that a RustLibrary Cargo.toml has `panic = "abort" set''' + reader = self.reader('rust-library-non-abort-panic') + with self.assertRaisesRegexp(SandboxValidationError, + 'does not specify `panic = "abort"`'): + self.read_topsrcdir(reader) + + def test_rust_library_dash_folding(self): + '''Test that on-disk names of RustLibrary objects convert dashes to underscores.''' + reader = self.reader('rust-library-dash-folding', + extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc')) + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + lib = objs[0] + self.assertIsInstance(lib, RustLibrary) + self.assertRegexpMatches(lib.lib_name, "random_crate") + self.assertRegexpMatches(lib.import_name, "random_crate") + self.assertRegexpMatches(lib.basename, "random-crate") + + def test_multiple_rust_libraries(self): + '''Test that linking multiple Rust libraries throws an error''' + reader = self.reader('multiple-rust-libraries', + extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc')) + with self.assertRaisesRegexp(LinkageMultipleRustLibrariesError, + 'Cannot link multiple Rust libraries'): + self.read_topsrcdir(reader) + + def test_crate_dependency_path_resolution(self): + '''Test recursive dependencies resolve with the correct paths.''' + reader = self.reader('crate-dependency-path-resolution', + extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc')) + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], RustLibrary) + + def test_android_res_dirs(self): + """Test that ANDROID_RES_DIRS works properly.""" + reader = self.reader('android-res-dirs') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], AndroidResDirs) + + # Android resource directories are ordered. + expected = [ + mozpath.join(reader.config.topsrcdir, 'dir1'), + mozpath.join(reader.config.topobjdir, 'dir2'), + '/dir3', + ] + self.assertEquals([p.full_path for p in objs[0].paths], expected) + + def test_binary_components(self): + """Test that IS_COMPONENT/NO_COMPONENTS_MANIFEST work properly.""" + reader = self.reader('binary-components') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 3) + self.assertIsInstance(objs[0], ChromeManifestEntry) + self.assertEqual(objs[0].path, + 'dist/bin/components/components.manifest') + self.assertIsInstance(objs[0].entry, manifest.ManifestBinaryComponent) + self.assertEqual(objs[0].entry.base, 'dist/bin/components') + self.assertEqual(objs[0].entry.relpath, objs[1].lib_name) + self.assertIsInstance(objs[1], SharedLibrary) + self.assertEqual(objs[1].basename, 'foo') + self.assertIsInstance(objs[2], SharedLibrary) + self.assertEqual(objs[2].basename, 'bar') + + def test_install_shared_lib(self): + """Test that we can install a shared library with TEST_HARNESS_FILES""" + reader = self.reader('test-install-shared-lib') + objs = self.read_topsrcdir(reader) + self.assertIsInstance(objs[0], TestHarnessFiles) + self.assertIsInstance(objs[1], VariablePassthru) + self.assertIsInstance(objs[2], SharedLibrary) + for path, files in objs[0].files.walk(): + for f in files: + self.assertEqual(str(f), '!libfoo.so') + self.assertEqual(path, 'foo/bar') + + def test_symbols_file(self): + """Test that SYMBOLS_FILE works""" + reader = self.reader('test-symbols-file') + genfile, shlib = self.read_topsrcdir(reader) + self.assertIsInstance(genfile, GeneratedFile) + self.assertIsInstance(shlib, SharedLibrary) + # This looks weird but MockConfig sets DLL_{PREFIX,SUFFIX} and + # the reader method in this class sets OS_TARGET=WINNT. + self.assertEqual(shlib.symbols_file, 'libfoo.so.def') + + def test_symbols_file_objdir(self): + """Test that a SYMBOLS_FILE in the objdir works""" + reader = self.reader('test-symbols-file-objdir') + genfile, shlib = self.read_topsrcdir(reader) + self.assertIsInstance(genfile, GeneratedFile) + self.assertEqual(genfile.script, + mozpath.join(reader.config.topsrcdir, 'foo.py')) + self.assertIsInstance(shlib, SharedLibrary) + self.assertEqual(shlib.symbols_file, 'foo.symbols') + + def test_symbols_file_objdir_missing_generated(self): + """Test that a SYMBOLS_FILE in the objdir that's missing + from GENERATED_FILES is an error. + """ + reader = self.reader('test-symbols-file-objdir-missing-generated') + with self.assertRaisesRegexp(SandboxValidationError, + 'Objdir file specified in SYMBOLS_FILE not in GENERATED_FILES:'): + self.read_topsrcdir(reader) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/frontend/test_namespaces.py b/python/mozbuild/mozbuild/test/frontend/test_namespaces.py new file mode 100644 index 000000000..71cc634e1 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/test_namespaces.py @@ -0,0 +1,207 @@ +# 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 unittest + +from mozunit import main + +from mozbuild.frontend.context import ( + Context, + ContextDerivedValue, + ContextDerivedTypedList, + ContextDerivedTypedListWithItems, +) + +from mozbuild.util import ( + StrictOrderingOnAppendList, + StrictOrderingOnAppendListWithFlagsFactory, + UnsortedError, +) + + +class Fuga(object): + def __init__(self, value): + self.value = value + + +class Piyo(ContextDerivedValue): + def __init__(self, context, value): + if not isinstance(value, unicode): + raise ValueError + self.context = context + self.value = value + + def lower(self): + return self.value.lower() + + def __str__(self): + return self.value + + def __cmp__(self, other): + return cmp(self.value, str(other)) + + def __hash__(self): + return hash(self.value) + + +VARIABLES = { + 'HOGE': (unicode, unicode, None), + 'FUGA': (Fuga, unicode, None), + 'PIYO': (Piyo, unicode, None), + 'HOGERA': (ContextDerivedTypedList(Piyo, StrictOrderingOnAppendList), + list, None), + 'HOGEHOGE': (ContextDerivedTypedListWithItems( + Piyo, + StrictOrderingOnAppendListWithFlagsFactory({ + 'foo': bool, + })), list, None), +} + +class TestContext(unittest.TestCase): + def test_key_rejection(self): + # Lowercase keys should be rejected during normal operation. + ns = Context(allowed_variables=VARIABLES) + + with self.assertRaises(KeyError) as ke: + ns['foo'] = True + + e = ke.exception.args + self.assertEqual(e[0], 'global_ns') + self.assertEqual(e[1], 'set_unknown') + self.assertEqual(e[2], 'foo') + self.assertTrue(e[3]) + + # Unknown uppercase keys should be rejected. + with self.assertRaises(KeyError) as ke: + ns['FOO'] = True + + e = ke.exception.args + self.assertEqual(e[0], 'global_ns') + self.assertEqual(e[1], 'set_unknown') + self.assertEqual(e[2], 'FOO') + self.assertTrue(e[3]) + + def test_allowed_set(self): + self.assertIn('HOGE', VARIABLES) + + ns = Context(allowed_variables=VARIABLES) + + ns['HOGE'] = 'foo' + self.assertEqual(ns['HOGE'], 'foo') + + def test_value_checking(self): + ns = Context(allowed_variables=VARIABLES) + + # Setting to a non-allowed type should not work. + with self.assertRaises(ValueError) as ve: + ns['HOGE'] = True + + e = ve.exception.args + self.assertEqual(e[0], 'global_ns') + self.assertEqual(e[1], 'set_type') + self.assertEqual(e[2], 'HOGE') + self.assertEqual(e[3], True) + self.assertEqual(e[4], unicode) + + def test_key_checking(self): + # Checking for existence of a key should not populate the key if it + # doesn't exist. + g = Context(allowed_variables=VARIABLES) + + self.assertFalse('HOGE' in g) + self.assertFalse('HOGE' in g) + + def test_coercion(self): + ns = Context(allowed_variables=VARIABLES) + + # Setting to a type different from the allowed input type should not + # work. + with self.assertRaises(ValueError) as ve: + ns['FUGA'] = False + + e = ve.exception.args + self.assertEqual(e[0], 'global_ns') + self.assertEqual(e[1], 'set_type') + self.assertEqual(e[2], 'FUGA') + self.assertEqual(e[3], False) + self.assertEqual(e[4], unicode) + + ns['FUGA'] = 'fuga' + self.assertIsInstance(ns['FUGA'], Fuga) + self.assertEqual(ns['FUGA'].value, 'fuga') + + ns['FUGA'] = Fuga('hoge') + self.assertIsInstance(ns['FUGA'], Fuga) + self.assertEqual(ns['FUGA'].value, 'hoge') + + def test_context_derived_coercion(self): + ns = Context(allowed_variables=VARIABLES) + + # Setting to a type different from the allowed input type should not + # work. + with self.assertRaises(ValueError) as ve: + ns['PIYO'] = False + + e = ve.exception.args + self.assertEqual(e[0], 'global_ns') + self.assertEqual(e[1], 'set_type') + self.assertEqual(e[2], 'PIYO') + self.assertEqual(e[3], False) + self.assertEqual(e[4], unicode) + + ns['PIYO'] = 'piyo' + self.assertIsInstance(ns['PIYO'], Piyo) + self.assertEqual(ns['PIYO'].value, 'piyo') + self.assertEqual(ns['PIYO'].context, ns) + + ns['PIYO'] = Piyo(ns, 'fuga') + self.assertIsInstance(ns['PIYO'], Piyo) + self.assertEqual(ns['PIYO'].value, 'fuga') + self.assertEqual(ns['PIYO'].context, ns) + + def test_context_derived_typed_list(self): + ns = Context(allowed_variables=VARIABLES) + + # Setting to a type that's rejected by coercion should not work. + with self.assertRaises(ValueError): + ns['HOGERA'] = [False] + + ns['HOGERA'] += ['a', 'b', 'c'] + + self.assertIsInstance(ns['HOGERA'], VARIABLES['HOGERA'][0]) + for n in range(0, 3): + self.assertIsInstance(ns['HOGERA'][n], Piyo) + self.assertEqual(ns['HOGERA'][n].value, ['a', 'b', 'c'][n]) + self.assertEqual(ns['HOGERA'][n].context, ns) + + with self.assertRaises(UnsortedError): + ns['HOGERA'] += ['f', 'e', 'd'] + + def test_context_derived_typed_list_with_items(self): + ns = Context(allowed_variables=VARIABLES) + + # Setting to a type that's rejected by coercion should not work. + with self.assertRaises(ValueError): + ns['HOGEHOGE'] = [False] + + values = ['a', 'b', 'c'] + ns['HOGEHOGE'] += values + + self.assertIsInstance(ns['HOGEHOGE'], VARIABLES['HOGEHOGE'][0]) + for v in values: + ns['HOGEHOGE'][v].foo = True + + for v, item in zip(values, ns['HOGEHOGE']): + self.assertIsInstance(item, Piyo) + self.assertEqual(v, item) + self.assertEqual(ns['HOGEHOGE'][v].foo, True) + self.assertEqual(ns['HOGEHOGE'][item].foo, True) + + with self.assertRaises(UnsortedError): + ns['HOGEHOGE'] += ['f', 'e', 'd'] + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/frontend/test_reader.py b/python/mozbuild/mozbuild/test/frontend/test_reader.py new file mode 100644 index 000000000..7c2aed9df --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py @@ -0,0 +1,485 @@ +# 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 sys +import unittest + +from mozunit import main + +from mozbuild.frontend.context import BugzillaComponent +from mozbuild.frontend.reader import ( + BuildReaderError, + BuildReader, +) + +from mozbuild.test.common import MockConfig + +import mozpack.path as mozpath + + +if sys.version_info.major == 2: + text_type = 'unicode' +else: + text_type = 'str' + +data_path = mozpath.abspath(mozpath.dirname(__file__)) +data_path = mozpath.join(data_path, 'data') + + +class TestBuildReader(unittest.TestCase): + def setUp(self): + self._old_env = dict(os.environ) + os.environ.pop('MOZ_OBJDIR', None) + + def tearDown(self): + os.environ.clear() + os.environ.update(self._old_env) + + def config(self, name, **kwargs): + path = mozpath.join(data_path, name) + + return MockConfig(path, **kwargs) + + def reader(self, name, enable_tests=False, error_is_fatal=True, **kwargs): + extra = {} + if enable_tests: + extra['ENABLE_TESTS'] = '1' + config = self.config(name, + extra_substs=extra, + error_is_fatal=error_is_fatal) + + return BuildReader(config, **kwargs) + + def file_path(self, name, *args): + return mozpath.join(data_path, name, *args) + + def test_dirs_traversal_simple(self): + reader = self.reader('traversal-simple') + + contexts = list(reader.read_topsrcdir()) + + self.assertEqual(len(contexts), 4) + + def test_dirs_traversal_no_descend(self): + reader = self.reader('traversal-simple') + + path = mozpath.join(reader.config.topsrcdir, 'moz.build') + self.assertTrue(os.path.exists(path)) + + contexts = list(reader.read_mozbuild(path, reader.config, + descend=False)) + + self.assertEqual(len(contexts), 1) + + def test_dirs_traversal_all_variables(self): + reader = self.reader('traversal-all-vars') + + contexts = list(reader.read_topsrcdir()) + self.assertEqual(len(contexts), 2) + + reader = self.reader('traversal-all-vars', enable_tests=True) + + contexts = list(reader.read_topsrcdir()) + self.assertEqual(len(contexts), 3) + + def test_relative_dirs(self): + # Ensure relative directories are traversed. + reader = self.reader('traversal-relative-dirs') + + contexts = list(reader.read_topsrcdir()) + self.assertEqual(len(contexts), 3) + + def test_repeated_dirs_ignored(self): + # Ensure repeated directories are ignored. + reader = self.reader('traversal-repeated-dirs') + + contexts = list(reader.read_topsrcdir()) + self.assertEqual(len(contexts), 3) + + def test_outside_topsrcdir(self): + # References to directories outside the topsrcdir should fail. + reader = self.reader('traversal-outside-topsrcdir') + + with self.assertRaises(Exception): + list(reader.read_topsrcdir()) + + def test_error_basic(self): + reader = self.reader('reader-error-basic') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertEqual(e.actual_file, self.file_path('reader-error-basic', + 'moz.build')) + + self.assertIn('The error occurred while processing the', str(e)) + + def test_error_included_from(self): + reader = self.reader('reader-error-included-from') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertEqual(e.actual_file, + self.file_path('reader-error-included-from', 'child.build')) + self.assertEqual(e.main_file, + self.file_path('reader-error-included-from', 'moz.build')) + + self.assertIn('This file was included as part of processing', str(e)) + + def test_error_syntax_error(self): + reader = self.reader('reader-error-syntax') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('Python syntax error on line 5', str(e)) + self.assertIn(' foo =', str(e)) + self.assertIn(' ^', str(e)) + + def test_error_read_unknown_global(self): + reader = self.reader('reader-error-read-unknown-global') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('The error was triggered on line 5', str(e)) + self.assertIn('The underlying problem is an attempt to read', str(e)) + self.assertIn(' FOO', str(e)) + + def test_error_write_unknown_global(self): + reader = self.reader('reader-error-write-unknown-global') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('The error was triggered on line 7', str(e)) + self.assertIn('The underlying problem is an attempt to write', str(e)) + self.assertIn(' FOO', str(e)) + + def test_error_write_bad_value(self): + reader = self.reader('reader-error-write-bad-value') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('The error was triggered on line 5', str(e)) + self.assertIn('is an attempt to write an illegal value to a special', + str(e)) + + self.assertIn('variable whose value was rejected is:\n\n DIRS', + str(e)) + + self.assertIn('written to it was of the following type:\n\n %s' % text_type, + str(e)) + + self.assertIn('expects the following type(s):\n\n list', str(e)) + + def test_error_illegal_path(self): + reader = self.reader('reader-error-outside-topsrcdir') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('The underlying problem is an illegal file access', + str(e)) + + def test_error_missing_include_path(self): + reader = self.reader('reader-error-missing-include') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('we referenced a path that does not exist', str(e)) + + def test_error_script_error(self): + reader = self.reader('reader-error-script-error') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('The error appears to be the fault of the script', + str(e)) + self.assertIn(' ["TypeError: unsupported operand', str(e)) + + def test_error_bad_dir(self): + reader = self.reader('reader-error-bad-dir') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('we referenced a path that does not exist', str(e)) + + def test_error_repeated_dir(self): + reader = self.reader('reader-error-repeated-dir') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('Directory (foo) registered multiple times', str(e)) + + def test_error_error_func(self): + reader = self.reader('reader-error-error-func') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('A moz.build file called the error() function.', str(e)) + self.assertIn(' Some error.', str(e)) + + def test_error_error_func_ok(self): + reader = self.reader('reader-error-error-func', error_is_fatal=False) + + contexts = list(reader.read_topsrcdir()) + + def test_error_empty_list(self): + reader = self.reader('reader-error-empty-list') + + with self.assertRaises(BuildReaderError) as bre: + list(reader.read_topsrcdir()) + + e = bre.exception + self.assertIn('Variable DIRS assigned an empty value.', str(e)) + + def test_inheriting_variables(self): + reader = self.reader('inheriting-variables') + + contexts = list(reader.read_topsrcdir()) + + self.assertEqual(len(contexts), 4) + self.assertEqual([context.relsrcdir for context in contexts], + ['', 'foo', 'foo/baz', 'bar']) + self.assertEqual([context['XPIDL_MODULE'] for context in contexts], + ['foobar', 'foobar', 'baz', 'foobar']) + + def test_find_relevant_mozbuilds(self): + reader = self.reader('reader-relevant-mozbuild') + + # Absolute paths outside topsrcdir are rejected. + with self.assertRaises(Exception): + reader._find_relevant_mozbuilds(['/foo']) + + # File in root directory. + paths = reader._find_relevant_mozbuilds(['file']) + self.assertEqual(paths, {'file': ['moz.build']}) + + # File in child directory. + paths = reader._find_relevant_mozbuilds(['d1/file1']) + self.assertEqual(paths, {'d1/file1': ['moz.build', 'd1/moz.build']}) + + # Multiple files in same directory. + paths = reader._find_relevant_mozbuilds(['d1/file1', 'd1/file2']) + self.assertEqual(paths, { + 'd1/file1': ['moz.build', 'd1/moz.build'], + 'd1/file2': ['moz.build', 'd1/moz.build']}) + + # Missing moz.build from missing intermediate directory. + paths = reader._find_relevant_mozbuilds( + ['d1/no-intermediate-moz-build/child/file']) + self.assertEqual(paths, { + 'd1/no-intermediate-moz-build/child/file': [ + 'moz.build', 'd1/moz.build', 'd1/no-intermediate-moz-build/child/moz.build']}) + + # Lots of empty directories. + paths = reader._find_relevant_mozbuilds([ + 'd1/parent-is-far/dir1/dir2/dir3/file']) + self.assertEqual(paths, { + 'd1/parent-is-far/dir1/dir2/dir3/file': + ['moz.build', 'd1/moz.build', 'd1/parent-is-far/moz.build']}) + + # Lots of levels. + paths = reader._find_relevant_mozbuilds([ + 'd1/every-level/a/file', 'd1/every-level/b/file']) + self.assertEqual(paths, { + 'd1/every-level/a/file': [ + 'moz.build', + 'd1/moz.build', + 'd1/every-level/moz.build', + 'd1/every-level/a/moz.build', + ], + 'd1/every-level/b/file': [ + 'moz.build', + 'd1/moz.build', + 'd1/every-level/moz.build', + 'd1/every-level/b/moz.build', + ], + }) + + # Different root directories. + paths = reader._find_relevant_mozbuilds(['d1/file', 'd2/file', 'file']) + self.assertEqual(paths, { + 'file': ['moz.build'], + 'd1/file': ['moz.build', 'd1/moz.build'], + 'd2/file': ['moz.build', 'd2/moz.build'], + }) + + def test_read_relevant_mozbuilds(self): + reader = self.reader('reader-relevant-mozbuild') + + paths, contexts = reader.read_relevant_mozbuilds(['d1/every-level/a/file', + 'd1/every-level/b/file', 'd2/file']) + self.assertEqual(len(paths), 3) + self.assertEqual(len(contexts), 6) + + self.assertEqual([ctx.relsrcdir for ctx in paths['d1/every-level/a/file']], + ['', 'd1', 'd1/every-level', 'd1/every-level/a']) + self.assertEqual([ctx.relsrcdir for ctx in paths['d1/every-level/b/file']], + ['', 'd1', 'd1/every-level', 'd1/every-level/b']) + self.assertEqual([ctx.relsrcdir for ctx in paths['d2/file']], + ['', 'd2']) + + def test_files_bad_bug_component(self): + reader = self.reader('files-info') + + with self.assertRaises(BuildReaderError): + reader.files_info(['bug_component/bad-assignment/moz.build']) + + def test_files_bug_component_static(self): + reader = self.reader('files-info') + + v = reader.files_info(['bug_component/static/foo', + 'bug_component/static/bar', + 'bug_component/static/foo/baz']) + self.assertEqual(len(v), 3) + self.assertEqual(v['bug_component/static/foo']['BUG_COMPONENT'], + BugzillaComponent('FooProduct', 'FooComponent')) + self.assertEqual(v['bug_component/static/bar']['BUG_COMPONENT'], + BugzillaComponent('BarProduct', 'BarComponent')) + self.assertEqual(v['bug_component/static/foo/baz']['BUG_COMPONENT'], + BugzillaComponent('default_product', 'default_component')) + + def test_files_bug_component_simple(self): + reader = self.reader('files-info') + + v = reader.files_info(['bug_component/simple/moz.build']) + self.assertEqual(len(v), 1) + flags = v['bug_component/simple/moz.build'] + self.assertEqual(flags['BUG_COMPONENT'].product, 'Core') + self.assertEqual(flags['BUG_COMPONENT'].component, 'Build Config') + + def test_files_bug_component_different_matchers(self): + reader = self.reader('files-info') + + v = reader.files_info([ + 'bug_component/different-matchers/foo.jsm', + 'bug_component/different-matchers/bar.cpp', + 'bug_component/different-matchers/baz.misc']) + self.assertEqual(len(v), 3) + + js_flags = v['bug_component/different-matchers/foo.jsm'] + cpp_flags = v['bug_component/different-matchers/bar.cpp'] + misc_flags = v['bug_component/different-matchers/baz.misc'] + + self.assertEqual(js_flags['BUG_COMPONENT'], BugzillaComponent('Firefox', 'JS')) + self.assertEqual(cpp_flags['BUG_COMPONENT'], BugzillaComponent('Firefox', 'C++')) + self.assertEqual(misc_flags['BUG_COMPONENT'], BugzillaComponent('default_product', 'default_component')) + + def test_files_bug_component_final(self): + reader = self.reader('files-info') + + v = reader.files_info([ + 'bug_component/final/foo', + 'bug_component/final/Makefile.in', + 'bug_component/final/subcomponent/Makefile.in', + 'bug_component/final/subcomponent/bar']) + + self.assertEqual(v['bug_component/final/foo']['BUG_COMPONENT'], + BugzillaComponent('default_product', 'default_component')) + self.assertEqual(v['bug_component/final/Makefile.in']['BUG_COMPONENT'], + BugzillaComponent('Core', 'Build Config')) + self.assertEqual(v['bug_component/final/subcomponent/Makefile.in']['BUG_COMPONENT'], + BugzillaComponent('Core', 'Build Config')) + self.assertEqual(v['bug_component/final/subcomponent/bar']['BUG_COMPONENT'], + BugzillaComponent('Another', 'Component')) + + def test_file_test_deps(self): + reader = self.reader('files-test-metadata') + + expected = { + 'simple/src/module.jsm': set(['simple/tests/test_general.html', + 'simple/browser/**.js']), + 'simple/base.cpp': set(['simple/tests/*', + 'default/tests/xpcshell/test_default_mod.js']), + } + + v = reader.files_info([ + 'simple/src/module.jsm', + 'simple/base.cpp', + ]) + + for path, pattern_set in expected.items(): + self.assertEqual(v[path].test_files, + expected[path]) + + def test_file_test_deps_default(self): + reader = self.reader('files-test-metadata') + v = reader.files_info([ + 'default/module.js', + ]) + + expected = { + 'default/module.js': set(['default/tests/xpcshell/**', + 'default/tests/reftests/**']), + } + + for path, pattern_set in expected.items(): + self.assertEqual(v[path].test_files, + expected[path]) + + def test_file_test_deps_tags(self): + reader = self.reader('files-test-metadata') + v = reader.files_info([ + 'tagged/src/bar.jsm', + 'tagged/src/submodule/foo.js', + ]) + + expected_patterns = { + 'tagged/src/submodule/foo.js': set([]), + 'tagged/src/bar.jsm': set(['tagged/**.js']), + } + + for path, pattern_set in expected_patterns.items(): + self.assertEqual(v[path].test_files, + expected_patterns[path]) + + expected_tags = { + 'tagged/src/submodule/foo.js': set(['submodule']), + 'tagged/src/bar.jsm': set([]), + } + for path, pattern_set in expected_tags.items(): + self.assertEqual(v[path].test_tags, + expected_tags[path]) + + expected_flavors = { + 'tagged/src/bar.jsm': set(['browser-chrome']), + 'tagged/src/submodule/foo.js': set([]), + } + for path, pattern_set in expected_flavors.items(): + self.assertEqual(v[path].test_flavors, + expected_flavors[path]) + + def test_invalid_flavor(self): + reader = self.reader('invalid-files-flavor') + + with self.assertRaises(BuildReaderError): + reader.files_info(['foo.js']) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/frontend/test_sandbox.py b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py new file mode 100644 index 000000000..d24c5d9ea --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py @@ -0,0 +1,534 @@ +# 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 shutil +import unittest + +from mozunit import main + +from mozbuild.frontend.reader import ( + MozbuildSandbox, + SandboxCalledError, +) + +from mozbuild.frontend.sandbox import ( + Sandbox, + SandboxExecutionError, + SandboxLoadError, +) + +from mozbuild.frontend.context import ( + Context, + FUNCTIONS, + SourcePath, + SPECIAL_VARIABLES, + VARIABLES, +) + +from mozbuild.test.common import MockConfig +from types import StringTypes + +import mozpack.path as mozpath + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data') + + +class TestSandbox(unittest.TestCase): + def sandbox(self): + return Sandbox(Context({ + 'DIRS': (list, list, None), + })) + + def test_exec_source_success(self): + sandbox = self.sandbox() + context = sandbox._context + + sandbox.exec_source('foo = True', mozpath.abspath('foo.py')) + + self.assertNotIn('foo', context) + self.assertEqual(context.main_path, mozpath.abspath('foo.py')) + self.assertEqual(context.all_paths, set([mozpath.abspath('foo.py')])) + + def test_exec_compile_error(self): + sandbox = self.sandbox() + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source('2f23;k;asfj', mozpath.abspath('foo.py')) + + self.assertEqual(se.exception.file_stack, [mozpath.abspath('foo.py')]) + self.assertIsInstance(se.exception.exc_value, SyntaxError) + self.assertEqual(sandbox._context.main_path, mozpath.abspath('foo.py')) + + def test_exec_import_denied(self): + sandbox = self.sandbox() + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source('import sys') + + self.assertIsInstance(se.exception, SandboxExecutionError) + self.assertEqual(se.exception.exc_type, ImportError) + + def test_exec_source_multiple(self): + sandbox = self.sandbox() + + sandbox.exec_source('DIRS = ["foo"]') + sandbox.exec_source('DIRS += ["bar"]') + + self.assertEqual(sandbox['DIRS'], ['foo', 'bar']) + + def test_exec_source_illegal_key_set(self): + sandbox = self.sandbox() + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source('ILLEGAL = True') + + e = se.exception + self.assertIsInstance(e.exc_value, KeyError) + + e = se.exception.exc_value + self.assertEqual(e.args[0], 'global_ns') + self.assertEqual(e.args[1], 'set_unknown') + + def test_exec_source_reassign(self): + sandbox = self.sandbox() + + sandbox.exec_source('DIRS = ["foo"]') + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source('DIRS = ["bar"]') + + self.assertEqual(sandbox['DIRS'], ['foo']) + e = se.exception + self.assertIsInstance(e.exc_value, KeyError) + + e = se.exception.exc_value + self.assertEqual(e.args[0], 'global_ns') + self.assertEqual(e.args[1], 'reassign') + self.assertEqual(e.args[2], 'DIRS') + + def test_exec_source_reassign_builtin(self): + sandbox = self.sandbox() + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source('True = 1') + + e = se.exception + self.assertIsInstance(e.exc_value, KeyError) + + e = se.exception.exc_value + self.assertEqual(e.args[0], 'Cannot reassign builtins') + + +class TestedSandbox(MozbuildSandbox): + '''Version of MozbuildSandbox with a little more convenience for testing. + + It automatically normalizes paths given to exec_file and exec_source. This + helps simplify the test code. + ''' + def normalize_path(self, path): + return mozpath.normpath( + mozpath.join(self._context.config.topsrcdir, path)) + + def source_path(self, path): + return SourcePath(self._context, path) + + def exec_file(self, path): + super(TestedSandbox, self).exec_file(self.normalize_path(path)) + + def exec_source(self, source, path=''): + super(TestedSandbox, self).exec_source(source, + self.normalize_path(path) if path else '') + + +class TestMozbuildSandbox(unittest.TestCase): + def sandbox(self, data_path=None, metadata={}): + config = None + + if data_path is not None: + config = MockConfig(mozpath.join(test_data_path, data_path)) + else: + config = MockConfig() + + return TestedSandbox(Context(VARIABLES, config), metadata) + + def test_default_state(self): + sandbox = self.sandbox() + sandbox._context.add_source(sandbox.normalize_path('moz.build')) + config = sandbox._context.config + + self.assertEqual(sandbox['TOPSRCDIR'], config.topsrcdir) + self.assertEqual(sandbox['TOPOBJDIR'], config.topobjdir) + self.assertEqual(sandbox['RELATIVEDIR'], '') + self.assertEqual(sandbox['SRCDIR'], config.topsrcdir) + self.assertEqual(sandbox['OBJDIR'], config.topobjdir) + + def test_symbol_presence(self): + # Ensure no discrepancies between the master symbol table and what's in + # the sandbox. + sandbox = self.sandbox() + sandbox._context.add_source(sandbox.normalize_path('moz.build')) + + all_symbols = set() + all_symbols |= set(FUNCTIONS.keys()) + all_symbols |= set(SPECIAL_VARIABLES.keys()) + + for symbol in all_symbols: + self.assertIsNotNone(sandbox[symbol]) + + def test_path_calculation(self): + sandbox = self.sandbox() + sandbox._context.add_source(sandbox.normalize_path('foo/bar/moz.build')) + config = sandbox._context.config + + self.assertEqual(sandbox['TOPSRCDIR'], config.topsrcdir) + self.assertEqual(sandbox['TOPOBJDIR'], config.topobjdir) + self.assertEqual(sandbox['RELATIVEDIR'], 'foo/bar') + self.assertEqual(sandbox['SRCDIR'], + mozpath.join(config.topsrcdir, 'foo/bar')) + self.assertEqual(sandbox['OBJDIR'], + mozpath.join(config.topobjdir, 'foo/bar')) + + def test_config_access(self): + sandbox = self.sandbox() + config = sandbox._context.config + + self.assertEqual(sandbox['CONFIG']['MOZ_TRUE'], '1') + self.assertEqual(sandbox['CONFIG']['MOZ_FOO'], config.substs['MOZ_FOO']) + + # Access to an undefined substitution should return None. + self.assertNotIn('MISSING', sandbox['CONFIG']) + self.assertIsNone(sandbox['CONFIG']['MISSING']) + + # Should shouldn't be allowed to assign to the config. + with self.assertRaises(Exception): + sandbox['CONFIG']['FOO'] = '' + + def test_special_variables(self): + sandbox = self.sandbox() + sandbox._context.add_source(sandbox.normalize_path('moz.build')) + + for k in SPECIAL_VARIABLES: + with self.assertRaises(KeyError): + sandbox[k] = 0 + + def test_exec_source_reassign_exported(self): + template_sandbox = self.sandbox(data_path='templates') + + # Templates need to be defined in actual files because of + # inspect.getsourcelines. + template_sandbox.exec_file('templates.mozbuild') + + config = MockConfig() + + exports = {'DIST_SUBDIR': 'browser'} + + sandbox = TestedSandbox(Context(VARIABLES, config), metadata={ + 'exports': exports, + 'templates': template_sandbox.templates, + }) + + self.assertEqual(sandbox['DIST_SUBDIR'], 'browser') + + # Templates should not interfere + sandbox.exec_source('Template([])', 'foo.mozbuild') + + sandbox.exec_source('DIST_SUBDIR = "foo"') + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source('DIST_SUBDIR = "bar"') + + self.assertEqual(sandbox['DIST_SUBDIR'], 'foo') + e = se.exception + self.assertIsInstance(e.exc_value, KeyError) + + e = se.exception.exc_value + self.assertEqual(e.args[0], 'global_ns') + self.assertEqual(e.args[1], 'reassign') + self.assertEqual(e.args[2], 'DIST_SUBDIR') + + def test_include_basic(self): + sandbox = self.sandbox(data_path='include-basic') + + sandbox.exec_file('moz.build') + + self.assertEqual(sandbox['DIRS'], [ + sandbox.source_path('foo'), + sandbox.source_path('bar'), + ]) + self.assertEqual(sandbox._context.main_path, + sandbox.normalize_path('moz.build')) + self.assertEqual(len(sandbox._context.all_paths), 2) + + def test_include_outside_topsrcdir(self): + sandbox = self.sandbox(data_path='include-outside-topsrcdir') + + with self.assertRaises(SandboxLoadError) as se: + sandbox.exec_file('relative.build') + + self.assertEqual(se.exception.illegal_path, + sandbox.normalize_path('../moz.build')) + + def test_include_error_stack(self): + # Ensure the path stack is reported properly in exceptions. + sandbox = self.sandbox(data_path='include-file-stack') + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_file('moz.build') + + e = se.exception + self.assertIsInstance(e.exc_value, KeyError) + + args = e.exc_value.args + self.assertEqual(args[0], 'global_ns') + self.assertEqual(args[1], 'set_unknown') + self.assertEqual(args[2], 'ILLEGAL') + + expected_stack = [mozpath.join(sandbox._context.config.topsrcdir, p) for p in [ + 'moz.build', 'included-1.build', 'included-2.build']] + + self.assertEqual(e.file_stack, expected_stack) + + def test_include_missing(self): + sandbox = self.sandbox(data_path='include-missing') + + with self.assertRaises(SandboxLoadError) as sle: + sandbox.exec_file('moz.build') + + self.assertIsNotNone(sle.exception.read_error) + + def test_include_relative_from_child_dir(self): + # A relative path from a subdirectory should be relative from that + # child directory. + sandbox = self.sandbox(data_path='include-relative-from-child') + sandbox.exec_file('child/child.build') + self.assertEqual(sandbox['DIRS'], [sandbox.source_path('../foo')]) + + sandbox = self.sandbox(data_path='include-relative-from-child') + sandbox.exec_file('child/child2.build') + self.assertEqual(sandbox['DIRS'], [sandbox.source_path('../foo')]) + + def test_include_topsrcdir_relative(self): + # An absolute path for include() is relative to topsrcdir. + + sandbox = self.sandbox(data_path='include-topsrcdir-relative') + sandbox.exec_file('moz.build') + + self.assertEqual(sandbox['DIRS'], [sandbox.source_path('foo')]) + + def test_error(self): + sandbox = self.sandbox() + + with self.assertRaises(SandboxCalledError) as sce: + sandbox.exec_source('error("This is an error.")') + + e = sce.exception + self.assertEqual(e.message, 'This is an error.') + + def test_substitute_config_files(self): + sandbox = self.sandbox() + sandbox._context.add_source(sandbox.normalize_path('moz.build')) + + sandbox.exec_source('CONFIGURE_SUBST_FILES += ["bar", "foo"]') + self.assertEqual(sandbox['CONFIGURE_SUBST_FILES'], ['bar', 'foo']) + for item in sandbox['CONFIGURE_SUBST_FILES']: + self.assertIsInstance(item, SourcePath) + + def test_invalid_utf8_substs(self): + """Ensure invalid UTF-8 in substs is converted with an error.""" + + # This is really mbcs. It's a bunch of invalid UTF-8. + config = MockConfig(extra_substs={'BAD_UTF8': b'\x83\x81\x83\x82\x3A'}) + + sandbox = MozbuildSandbox(Context(VARIABLES, config)) + + self.assertEqual(sandbox['CONFIG']['BAD_UTF8'], + u'\ufffd\ufffd\ufffd\ufffd:') + + def test_invalid_exports_set_base(self): + sandbox = self.sandbox() + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source('EXPORTS = "foo.h"') + + self.assertEqual(se.exception.exc_type, ValueError) + + def test_templates(self): + sandbox = self.sandbox(data_path='templates') + + # Templates need to be defined in actual files because of + # inspect.getsourcelines. + sandbox.exec_file('templates.mozbuild') + + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +Template([ + 'foo.cpp', +]) +''' + sandbox2.exec_source(source, 'foo.mozbuild') + + self.assertEqual(sandbox2._context, { + 'SOURCES': ['foo.cpp'], + 'DIRS': [], + }) + + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +SOURCES += ['qux.cpp'] +Template([ + 'bar.cpp', + 'foo.cpp', +],[ + 'foo', +]) +SOURCES += ['hoge.cpp'] +''' + sandbox2.exec_source(source, 'foo.mozbuild') + + self.assertEqual(sandbox2._context, { + 'SOURCES': ['qux.cpp', 'bar.cpp', 'foo.cpp', 'hoge.cpp'], + 'DIRS': [sandbox2.source_path('foo')], + }) + + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +TemplateError([ + 'foo.cpp', +]) +''' + with self.assertRaises(SandboxExecutionError) as se: + sandbox2.exec_source(source, 'foo.mozbuild') + + e = se.exception + self.assertIsInstance(e.exc_value, KeyError) + + e = se.exception.exc_value + self.assertEqual(e.args[0], 'global_ns') + self.assertEqual(e.args[1], 'set_unknown') + + # TemplateGlobalVariable tries to access 'illegal' but that is expected + # to throw. + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +illegal = True +TemplateGlobalVariable() +''' + with self.assertRaises(SandboxExecutionError) as se: + sandbox2.exec_source(source, 'foo.mozbuild') + + e = se.exception + self.assertIsInstance(e.exc_value, NameError) + + # TemplateGlobalUPPERVariable sets SOURCES with DIRS, but the context + # used when running the template is not expected to access variables + # from the global context. + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +DIRS += ['foo'] +TemplateGlobalUPPERVariable() +''' + sandbox2.exec_source(source, 'foo.mozbuild') + self.assertEqual(sandbox2._context, { + 'SOURCES': [], + 'DIRS': [sandbox2.source_path('foo')], + }) + + # However, the result of the template is mixed with the global + # context. + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +SOURCES += ['qux.cpp'] +TemplateInherit([ + 'bar.cpp', + 'foo.cpp', +]) +SOURCES += ['hoge.cpp'] +''' + sandbox2.exec_source(source, 'foo.mozbuild') + + self.assertEqual(sandbox2._context, { + 'SOURCES': ['qux.cpp', 'bar.cpp', 'foo.cpp', 'hoge.cpp'], + 'USE_LIBS': ['foo'], + 'DIRS': [], + }) + + # Template names must be CamelCase. Here, we can define the template + # inline because the error happens before inspect.getsourcelines. + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +@template +def foo(): + pass +''' + + with self.assertRaises(SandboxExecutionError) as se: + sandbox2.exec_source(source, 'foo.mozbuild') + + e = se.exception + self.assertIsInstance(e.exc_value, NameError) + + e = se.exception.exc_value + self.assertEqual(e.message, + 'Template function names must be CamelCase.') + + # Template names must not already be registered. + sandbox2 = self.sandbox(metadata={'templates': sandbox.templates}) + source = ''' +@template +def Template(): + pass +''' + with self.assertRaises(SandboxExecutionError) as se: + sandbox2.exec_source(source, 'foo.mozbuild') + + e = se.exception + self.assertIsInstance(e.exc_value, KeyError) + + e = se.exception.exc_value + self.assertEqual(e.message, + 'A template named "Template" was already declared in %s.' % + sandbox.normalize_path('templates.mozbuild')) + + def test_function_args(self): + class Foo(int): pass + + def foo(a, b): + return type(a), type(b) + + FUNCTIONS.update({ + 'foo': (lambda self: foo, (Foo, int), ''), + }) + + try: + sandbox = self.sandbox() + source = 'foo("a", "b")' + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source(source, 'foo.mozbuild') + + e = se.exception + self.assertIsInstance(e.exc_value, ValueError) + + sandbox = self.sandbox() + source = 'foo(1, "b")' + + with self.assertRaises(SandboxExecutionError) as se: + sandbox.exec_source(source, 'foo.mozbuild') + + e = se.exception + self.assertIsInstance(e.exc_value, ValueError) + + sandbox = self.sandbox() + source = 'a = foo(1, 2)' + sandbox.exec_source(source, 'foo.mozbuild') + + self.assertEquals(sandbox['a'], (Foo, int)) + finally: + del FUNCTIONS['foo'] + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_android_version_code.py b/python/mozbuild/mozbuild/test/test_android_version_code.py new file mode 100644 index 000000000..059f4588c --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_android_version_code.py @@ -0,0 +1,63 @@ +# 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 mozunit import main +import unittest + +from mozbuild.android_version_code import ( + android_version_code_v0, + android_version_code_v1, +) + +class TestAndroidVersionCode(unittest.TestCase): + def test_android_version_code_v0(self): + # From https://treeherder.mozilla.org/#/jobs?repo=mozilla-central&revision=e25de9972a77. + buildid = '20150708104620' + arm_api9 = 2015070819 + arm_api11 = 2015070821 + x86_api9 = 2015070822 + self.assertEqual(android_version_code_v0(buildid, cpu_arch='armeabi', min_sdk=9, max_sdk=None), arm_api9) + self.assertEqual(android_version_code_v0(buildid, cpu_arch='armeabi-v7a', min_sdk=11, max_sdk=None), arm_api11) + self.assertEqual(android_version_code_v0(buildid, cpu_arch='x86', min_sdk=9, max_sdk=None), x86_api9) + + def test_android_version_code_v1(self): + buildid = '20150825141628' + arm_api15 = 0b01111000001000000001001001110001 + x86_api9 = 0b01111000001000000001001001110100 + self.assertEqual(android_version_code_v1(buildid, cpu_arch='armeabi-v7a', min_sdk=15, max_sdk=None), arm_api15) + self.assertEqual(android_version_code_v1(buildid, cpu_arch='x86', min_sdk=9, max_sdk=None), x86_api9) + + def test_android_version_code_v1_underflow(self): + '''Verify that it is an error to ask for v1 codes predating the cutoff.''' + buildid = '201508010000' # Earliest possible. + arm_api9 = 0b01111000001000000000000000000000 + self.assertEqual(android_version_code_v1(buildid, cpu_arch='armeabi', min_sdk=9, max_sdk=None), arm_api9) + with self.assertRaises(ValueError) as cm: + underflow = '201507310000' # Latest possible (valid) underflowing date. + android_version_code_v1(underflow, cpu_arch='armeabi', min_sdk=9, max_sdk=None) + self.assertTrue('underflow' in cm.exception.message) + + def test_android_version_code_v1_running_low(self): + '''Verify there is an informative message if one asks for v1 codes that are close to overflow.''' + with self.assertRaises(ValueError) as cm: + overflow = '20290801000000' + android_version_code_v1(overflow, cpu_arch='armeabi', min_sdk=9, max_sdk=None) + self.assertTrue('Running out of low order bits' in cm.exception.message) + + def test_android_version_code_v1_overflow(self): + '''Verify that it is an error to ask for v1 codes that actually does overflow.''' + with self.assertRaises(ValueError) as cm: + overflow = '20310801000000' + android_version_code_v1(overflow, cpu_arch='armeabi', min_sdk=9, max_sdk=None) + self.assertTrue('overflow' in cm.exception.message) + + def test_android_version_code_v0_relative_v1(self): + '''Verify that the first v1 code is greater than the equivalent v0 code.''' + buildid = '20150801000000' + self.assertGreater(android_version_code_v1(buildid, cpu_arch='armeabi', min_sdk=9, max_sdk=None), + android_version_code_v0(buildid, cpu_arch='armeabi', min_sdk=9, max_sdk=None)) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_base.py b/python/mozbuild/mozbuild/test/test_base.py new file mode 100644 index 000000000..87f0db85b --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_base.py @@ -0,0 +1,410 @@ +# 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 json +import os +import shutil +import subprocess +import sys +import tempfile +import unittest + +from cStringIO import StringIO +from mozfile.mozfile import NamedTemporaryFile + +from mozunit import main + +from mach.logging import LoggingManager + +from mozbuild.base import ( + BadEnvironmentException, + MachCommandBase, + MozbuildObject, + ObjdirMismatchException, + PathArgument, +) + +from mozbuild.backend.configenvironment import ConfigEnvironment +from buildconfig import topsrcdir, topobjdir +import mozpack.path as mozpath + + +curdir = os.path.dirname(__file__) +log_manager = LoggingManager() + + +class TestMozbuildObject(unittest.TestCase): + def setUp(self): + self._old_cwd = os.getcwd() + self._old_env = dict(os.environ) + os.environ.pop('MOZCONFIG', None) + os.environ.pop('MOZ_OBJDIR', None) + os.environ.pop('MOZ_CURRENT_PROJECT', None) + + def tearDown(self): + os.chdir(self._old_cwd) + os.environ.clear() + os.environ.update(self._old_env) + + def get_base(self, topobjdir=None): + return MozbuildObject(topsrcdir, None, log_manager, topobjdir=topobjdir) + + def test_objdir_config_guess(self): + base = self.get_base() + + with NamedTemporaryFile() as mozconfig: + os.environ[b'MOZCONFIG'] = mozconfig.name + + self.assertIsNotNone(base.topobjdir) + self.assertEqual(len(base.topobjdir.split()), 1) + config_guess = base.resolve_config_guess() + self.assertTrue(base.topobjdir.endswith(config_guess)) + self.assertTrue(os.path.isabs(base.topobjdir)) + self.assertTrue(base.topobjdir.startswith(base.topsrcdir)) + + def test_objdir_trailing_slash(self): + """Trailing slashes in topobjdir should be removed.""" + base = self.get_base() + + with NamedTemporaryFile() as mozconfig: + mozconfig.write('mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/foo/') + mozconfig.flush() + os.environ[b'MOZCONFIG'] = mozconfig.name + + self.assertEqual(base.topobjdir, mozpath.join(base.topsrcdir, + 'foo')) + self.assertTrue(base.topobjdir.endswith('foo')) + + def test_objdir_config_status(self): + """Ensure @CONFIG_GUESS@ is handled when loading mozconfig.""" + base = self.get_base() + cmd = base._normalize_command( + [os.path.join(topsrcdir, 'build', 'autoconf', 'config.guess')], + True) + guess = subprocess.check_output(cmd, cwd=topsrcdir).strip() + + # There may be symlinks involved, so we use real paths to ensure + # path consistency. + d = os.path.realpath(tempfile.mkdtemp()) + try: + mozconfig = os.path.join(d, 'mozconfig') + with open(mozconfig, 'wt') as fh: + fh.write('mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/foo/@CONFIG_GUESS@') + print('Wrote mozconfig %s' % mozconfig) + + topobjdir = os.path.join(d, 'foo', guess) + os.makedirs(topobjdir) + + # Create a fake topsrcdir. + guess_path = os.path.join(d, 'build', 'autoconf', 'config.guess') + os.makedirs(os.path.dirname(guess_path)) + shutil.copy(os.path.join(topsrcdir, 'build', 'autoconf', + 'config.guess',), guess_path) + + mozinfo = os.path.join(topobjdir, 'mozinfo.json') + with open(mozinfo, 'wt') as fh: + json.dump(dict( + topsrcdir=d, + mozconfig=mozconfig, + ), fh) + + os.environ[b'MOZCONFIG'] = mozconfig.encode('utf-8') + os.chdir(topobjdir) + + obj = MozbuildObject.from_environment( + detect_virtualenv_mozinfo=False) + + self.assertEqual(obj.topobjdir, mozpath.normsep(topobjdir)) + finally: + os.chdir(self._old_cwd) + shutil.rmtree(d) + + def test_relative_objdir(self): + """Relative defined objdirs are loaded properly.""" + d = os.path.realpath(tempfile.mkdtemp()) + try: + mozconfig = os.path.join(d, 'mozconfig') + with open(mozconfig, 'wt') as fh: + fh.write('mk_add_options MOZ_OBJDIR=./objdir') + + topobjdir = mozpath.join(d, 'objdir') + os.mkdir(topobjdir) + + mozinfo = os.path.join(topobjdir, 'mozinfo.json') + with open(mozinfo, 'wt') as fh: + json.dump(dict( + topsrcdir=d, + mozconfig=mozconfig, + ), fh) + + os.environ[b'MOZCONFIG'] = mozconfig.encode('utf-8') + child = os.path.join(topobjdir, 'foo', 'bar') + os.makedirs(child) + os.chdir(child) + + obj = MozbuildObject.from_environment( + detect_virtualenv_mozinfo=False) + + self.assertEqual(obj.topobjdir, topobjdir) + + finally: + os.chdir(self._old_cwd) + shutil.rmtree(d) + + @unittest.skipIf(not hasattr(os, 'symlink'), 'symlinks not available.') + def test_symlink_objdir(self): + """Objdir that is a symlink is loaded properly.""" + d = os.path.realpath(tempfile.mkdtemp()) + try: + topobjdir_real = os.path.join(d, 'objdir') + topobjdir_link = os.path.join(d, 'objlink') + + os.mkdir(topobjdir_real) + os.symlink(topobjdir_real, topobjdir_link) + + mozconfig = os.path.join(d, 'mozconfig') + with open(mozconfig, 'wt') as fh: + fh.write('mk_add_options MOZ_OBJDIR=%s' % topobjdir_link) + + mozinfo = os.path.join(topobjdir_real, 'mozinfo.json') + with open(mozinfo, 'wt') as fh: + json.dump(dict( + topsrcdir=d, + mozconfig=mozconfig, + ), fh) + + os.chdir(topobjdir_link) + obj = MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) + self.assertEqual(obj.topobjdir, topobjdir_real) + + os.chdir(topobjdir_real) + obj = MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) + self.assertEqual(obj.topobjdir, topobjdir_real) + + finally: + os.chdir(self._old_cwd) + shutil.rmtree(d) + + def test_mach_command_base_inside_objdir(self): + """Ensure a MachCommandBase constructed from inside the objdir works.""" + + d = os.path.realpath(tempfile.mkdtemp()) + + try: + topobjdir = os.path.join(d, 'objdir') + os.makedirs(topobjdir) + + topsrcdir = os.path.join(d, 'srcdir') + os.makedirs(topsrcdir) + + mozinfo = os.path.join(topobjdir, 'mozinfo.json') + with open(mozinfo, 'wt') as fh: + json.dump(dict( + topsrcdir=topsrcdir, + ), fh) + + os.chdir(topobjdir) + + class MockMachContext(object): + pass + + context = MockMachContext() + context.cwd = topobjdir + context.topdir = topsrcdir + context.settings = None + context.log_manager = None + context.detect_virtualenv_mozinfo=False + + o = MachCommandBase(context) + + self.assertEqual(o.topobjdir, mozpath.normsep(topobjdir)) + self.assertEqual(o.topsrcdir, mozpath.normsep(topsrcdir)) + + finally: + os.chdir(self._old_cwd) + shutil.rmtree(d) + + def test_objdir_is_srcdir_rejected(self): + """Ensure the srcdir configurations are rejected.""" + d = os.path.realpath(tempfile.mkdtemp()) + + try: + # The easiest way to do this is to create a mozinfo.json with data + # that will never happen. + mozinfo = os.path.join(d, 'mozinfo.json') + with open(mozinfo, 'wt') as fh: + json.dump({'topsrcdir': d}, fh) + + os.chdir(d) + + with self.assertRaises(BadEnvironmentException): + MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) + + finally: + os.chdir(self._old_cwd) + shutil.rmtree(d) + + def test_objdir_mismatch(self): + """Ensure MachCommandBase throwing on objdir mismatch.""" + d = os.path.realpath(tempfile.mkdtemp()) + + try: + real_topobjdir = os.path.join(d, 'real-objdir') + os.makedirs(real_topobjdir) + + topobjdir = os.path.join(d, 'objdir') + os.makedirs(topobjdir) + + topsrcdir = os.path.join(d, 'srcdir') + os.makedirs(topsrcdir) + + mozconfig = os.path.join(d, 'mozconfig') + with open(mozconfig, 'wt') as fh: + fh.write('mk_add_options MOZ_OBJDIR=%s' % real_topobjdir) + + mozinfo = os.path.join(topobjdir, 'mozinfo.json') + with open(mozinfo, 'wt') as fh: + json.dump(dict( + topsrcdir=topsrcdir, + mozconfig=mozconfig, + ), fh) + + os.chdir(topobjdir) + + class MockMachContext(object): + pass + + context = MockMachContext() + context.cwd = topobjdir + context.topdir = topsrcdir + context.settings = None + context.log_manager = None + context.detect_virtualenv_mozinfo=False + + stdout = sys.stdout + sys.stdout = StringIO() + try: + with self.assertRaises(SystemExit): + MachCommandBase(context) + + self.assertTrue(sys.stdout.getvalue().startswith( + 'Ambiguous object directory detected.')) + finally: + sys.stdout = stdout + + finally: + os.chdir(self._old_cwd) + shutil.rmtree(d) + + def test_config_environment(self): + base = self.get_base(topobjdir=topobjdir) + + ce = base.config_environment + self.assertIsInstance(ce, ConfigEnvironment) + + self.assertEqual(base.defines, ce.defines) + self.assertEqual(base.substs, ce.substs) + + self.assertIsInstance(base.defines, dict) + self.assertIsInstance(base.substs, dict) + + def test_get_binary_path(self): + base = self.get_base(topobjdir=topobjdir) + + platform = sys.platform + + # We should ideally use the config.status from the build. Let's install + # a fake one. + substs = [ + ('MOZ_APP_NAME', 'awesomeapp'), + ('MOZ_BUILD_APP', 'awesomeapp'), + ] + if sys.platform.startswith('darwin'): + substs.append(('OS_ARCH', 'Darwin')) + substs.append(('BIN_SUFFIX', '')) + substs.append(('MOZ_MACBUNDLE_NAME', 'Nightly.app')) + elif sys.platform.startswith(('win32', 'cygwin')): + substs.append(('OS_ARCH', 'WINNT')) + substs.append(('BIN_SUFFIX', '.exe')) + else: + substs.append(('OS_ARCH', 'something')) + substs.append(('BIN_SUFFIX', '')) + + base._config_environment = ConfigEnvironment(base.topsrcdir, + base.topobjdir, substs=substs) + + p = base.get_binary_path('xpcshell', False) + if platform.startswith('darwin'): + self.assertTrue(p.endswith('Contents/MacOS/xpcshell')) + elif platform.startswith(('win32', 'cygwin')): + self.assertTrue(p.endswith('xpcshell.exe')) + else: + self.assertTrue(p.endswith('dist/bin/xpcshell')) + + p = base.get_binary_path(validate_exists=False) + if platform.startswith('darwin'): + self.assertTrue(p.endswith('Contents/MacOS/awesomeapp')) + elif platform.startswith(('win32', 'cygwin')): + self.assertTrue(p.endswith('awesomeapp.exe')) + else: + self.assertTrue(p.endswith('dist/bin/awesomeapp')) + + p = base.get_binary_path(validate_exists=False, where="staged-package") + if platform.startswith('darwin'): + self.assertTrue(p.endswith('awesomeapp/Nightly.app/Contents/MacOS/awesomeapp')) + elif platform.startswith(('win32', 'cygwin')): + self.assertTrue(p.endswith('awesomeapp\\awesomeapp.exe')) + else: + self.assertTrue(p.endswith('awesomeapp/awesomeapp')) + + self.assertRaises(Exception, base.get_binary_path, where="somewhere") + + p = base.get_binary_path('foobar', validate_exists=False) + if platform.startswith('win32'): + self.assertTrue(p.endswith('foobar.exe')) + else: + self.assertTrue(p.endswith('foobar')) + +class TestPathArgument(unittest.TestCase): + def test_path_argument(self): + # Absolute path + p = PathArgument("/obj/foo", "/src", "/obj", "/src") + self.assertEqual(p.relpath(), "foo") + self.assertEqual(p.srcdir_path(), "/src/foo") + self.assertEqual(p.objdir_path(), "/obj/foo") + + # Relative path within srcdir + p = PathArgument("foo", "/src", "/obj", "/src") + self.assertEqual(p.relpath(), "foo") + self.assertEqual(p.srcdir_path(), "/src/foo") + self.assertEqual(p.objdir_path(), "/obj/foo") + + # Relative path within subdirectory + p = PathArgument("bar", "/src", "/obj", "/src/foo") + self.assertEqual(p.relpath(), "foo/bar") + self.assertEqual(p.srcdir_path(), "/src/foo/bar") + self.assertEqual(p.objdir_path(), "/obj/foo/bar") + + # Relative path within objdir + p = PathArgument("foo", "/src", "/obj", "/obj") + self.assertEqual(p.relpath(), "foo") + self.assertEqual(p.srcdir_path(), "/src/foo") + self.assertEqual(p.objdir_path(), "/obj/foo") + + # "." path + p = PathArgument(".", "/src", "/obj", "/src/foo") + self.assertEqual(p.relpath(), "foo") + self.assertEqual(p.srcdir_path(), "/src/foo") + self.assertEqual(p.objdir_path(), "/obj/foo") + + # Nested src/obj directories + p = PathArgument("bar", "/src", "/src/obj", "/src/obj/foo") + self.assertEqual(p.relpath(), "foo/bar") + self.assertEqual(p.srcdir_path(), "/src/foo/bar") + self.assertEqual(p.objdir_path(), "/src/obj/foo/bar") + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_containers.py b/python/mozbuild/mozbuild/test/test_containers.py new file mode 100644 index 000000000..3d46f86a9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_containers.py @@ -0,0 +1,224 @@ +# 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/. + +import unittest + +from mozunit import main + +from mozbuild.util import ( + KeyedDefaultDict, + List, + OrderedDefaultDict, + ReadOnlyNamespace, + ReadOnlyDefaultDict, + ReadOnlyDict, + ReadOnlyKeyedDefaultDict, +) + +from collections import OrderedDict + + +class TestReadOnlyNamespace(unittest.TestCase): + def test_basic(self): + test = ReadOnlyNamespace(foo=1, bar=2) + + self.assertEqual(test.foo, 1) + self.assertEqual(test.bar, 2) + self.assertEqual( + sorted(i for i in dir(test) if not i.startswith('__')), + ['bar', 'foo']) + + with self.assertRaises(AttributeError): + value = test.missing + + with self.assertRaises(Exception): + test.foo = 2 + + with self.assertRaises(Exception): + del test.foo + + self.assertEqual(test, test) + self.assertEqual(test, ReadOnlyNamespace(foo=1, bar=2)) + self.assertNotEqual(test, ReadOnlyNamespace(foo='1', bar=2)) + self.assertNotEqual(test, ReadOnlyNamespace(foo=1, bar=2, qux=3)) + self.assertNotEqual(test, ReadOnlyNamespace(foo=1, qux=3)) + self.assertNotEqual(test, ReadOnlyNamespace(foo=3, bar='42')) + + +class TestReadOnlyDict(unittest.TestCase): + def test_basic(self): + original = {'foo': 1, 'bar': 2} + + test = ReadOnlyDict(original) + + self.assertEqual(original, test) + self.assertEqual(test['foo'], 1) + + with self.assertRaises(KeyError): + value = test['missing'] + + with self.assertRaises(Exception): + test['baz'] = True + + def test_update(self): + original = {'foo': 1, 'bar': 2} + + test = ReadOnlyDict(original) + + with self.assertRaises(Exception): + test.update(foo=2) + + self.assertEqual(original, test) + + def test_del(self): + original = {'foo': 1, 'bar': 2} + + test = ReadOnlyDict(original) + + with self.assertRaises(Exception): + del test['foo'] + + self.assertEqual(original, test) + + +class TestReadOnlyDefaultDict(unittest.TestCase): + def test_simple(self): + original = {'foo': 1, 'bar': 2} + + test = ReadOnlyDefaultDict(bool, original) + + self.assertEqual(original, test) + + self.assertEqual(test['foo'], 1) + + def test_assignment(self): + test = ReadOnlyDefaultDict(bool, {}) + + with self.assertRaises(Exception): + test['foo'] = True + + def test_defaults(self): + test = ReadOnlyDefaultDict(bool, {'foo': 1}) + + self.assertEqual(test['foo'], 1) + + self.assertEqual(test['qux'], False) + + +class TestList(unittest.TestCase): + def test_add_list(self): + test = List([1, 2, 3]) + + test += [4, 5, 6] + self.assertIsInstance(test, List) + self.assertEqual(test, [1, 2, 3, 4, 5, 6]) + + test = test + [7, 8] + self.assertIsInstance(test, List) + self.assertEqual(test, [1, 2, 3, 4, 5, 6, 7, 8]) + + def test_add_string(self): + test = List([1, 2, 3]) + + with self.assertRaises(ValueError): + test += 'string' + + def test_none(self): + """As a special exception, we allow None to be treated as an empty + list.""" + test = List([1, 2, 3]) + + test += None + self.assertEqual(test, [1, 2, 3]) + + test = test + None + self.assertIsInstance(test, List) + self.assertEqual(test, [1, 2, 3]) + + with self.assertRaises(ValueError): + test += False + + with self.assertRaises(ValueError): + test = test + False + +class TestOrderedDefaultDict(unittest.TestCase): + def test_simple(self): + original = OrderedDict(foo=1, bar=2) + + test = OrderedDefaultDict(bool, original) + + self.assertEqual(original, test) + + self.assertEqual(test['foo'], 1) + + self.assertEqual(test.keys(), ['foo', 'bar' ]) + + def test_defaults(self): + test = OrderedDefaultDict(bool, {'foo': 1 }) + + self.assertEqual(test['foo'], 1) + + self.assertEqual(test['qux'], False) + + self.assertEqual(test.keys(), ['foo', 'qux' ]) + + +class TestKeyedDefaultDict(unittest.TestCase): + def test_simple(self): + original = {'foo': 1, 'bar': 2 } + + test = KeyedDefaultDict(lambda x: x, original) + + self.assertEqual(original, test) + + self.assertEqual(test['foo'], 1) + + def test_defaults(self): + test = KeyedDefaultDict(lambda x: x, {'foo': 1 }) + + self.assertEqual(test['foo'], 1) + + self.assertEqual(test['qux'], 'qux') + + self.assertEqual(test['bar'], 'bar') + + test['foo'] = 2 + test['qux'] = None + test['baz'] = 'foo' + + self.assertEqual(test['foo'], 2) + + self.assertEqual(test['qux'], None) + + self.assertEqual(test['baz'], 'foo') + + +class TestReadOnlyKeyedDefaultDict(unittest.TestCase): + def test_defaults(self): + test = ReadOnlyKeyedDefaultDict(lambda x: x, {'foo': 1 }) + + self.assertEqual(test['foo'], 1) + + self.assertEqual(test['qux'], 'qux') + + self.assertEqual(test['bar'], 'bar') + + copy = dict(test) + + with self.assertRaises(Exception): + test['foo'] = 2 + + with self.assertRaises(Exception): + test['qux'] = None + + with self.assertRaises(Exception): + test['baz'] = 'foo' + + self.assertEqual(test, copy) + + self.assertEqual(len(test), 3) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_dotproperties.py b/python/mozbuild/mozbuild/test/test_dotproperties.py new file mode 100644 index 000000000..a03f85b0d --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_dotproperties.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals + +import os +import unittest + +from StringIO import StringIO + +import mozpack.path as mozpath + +from mozbuild.dotproperties import ( + DotProperties, +) + +from mozunit import ( + main, +) + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data') + + +class TestDotProperties(unittest.TestCase): + def test_get(self): + contents = StringIO(''' +key=value +''') + p = DotProperties(contents) + self.assertEqual(p.get('missing'), None) + self.assertEqual(p.get('missing', 'default'), 'default') + self.assertEqual(p.get('key'), 'value') + + + def test_update(self): + contents = StringIO(''' +old=old value +key=value +''') + p = DotProperties(contents) + self.assertEqual(p.get('old'), 'old value') + self.assertEqual(p.get('key'), 'value') + + new_contents = StringIO(''' +key=new value +''') + p.update(new_contents) + self.assertEqual(p.get('old'), 'old value') + self.assertEqual(p.get('key'), 'new value') + + + def test_get_list(self): + contents = StringIO(''' +list.0=A +list.1=B +list.2=C + +order.1=B +order.0=A +order.2=C +''') + p = DotProperties(contents) + self.assertEqual(p.get_list('missing'), []) + self.assertEqual(p.get_list('list'), ['A', 'B', 'C']) + self.assertEqual(p.get_list('order'), ['A', 'B', 'C']) + + + def test_get_list_with_shared_prefix(self): + contents = StringIO(''' +list.0=A +list.1=B +list.2=C + +list.sublist.1=E +list.sublist.0=D +list.sublist.2=F + +list.sublist.second.0=G + +list.other.0=H +''') + p = DotProperties(contents) + self.assertEqual(p.get_list('list'), ['A', 'B', 'C']) + self.assertEqual(p.get_list('list.sublist'), ['D', 'E', 'F']) + self.assertEqual(p.get_list('list.sublist.second'), ['G']) + self.assertEqual(p.get_list('list.other'), ['H']) + + + def test_get_dict(self): + contents = StringIO(''' +A.title=title A + +B.title=title B +B.url=url B + +C=value +''') + p = DotProperties(contents) + self.assertEqual(p.get_dict('missing'), {}) + self.assertEqual(p.get_dict('A'), {'title': 'title A'}) + self.assertEqual(p.get_dict('B'), {'title': 'title B', 'url': 'url B'}) + with self.assertRaises(ValueError): + p.get_dict('A', required_keys=['title', 'url']) + with self.assertRaises(ValueError): + p.get_dict('missing', required_keys=['key']) + # A key=value pair is considered to root an empty dict. + self.assertEqual(p.get_dict('C'), {}) + with self.assertRaises(ValueError): + p.get_dict('C', required_keys=['missing_key']) + + + def test_get_dict_with_shared_prefix(self): + contents = StringIO(''' +A.title=title A +A.subdict.title=title A subdict + +B.title=title B +B.url=url B +B.subdict.title=title B subdict +B.subdict.url=url B subdict +''') + p = DotProperties(contents) + self.assertEqual(p.get_dict('A'), {'title': 'title A'}) + self.assertEqual(p.get_dict('B'), {'title': 'title B', 'url': 'url B'}) + self.assertEqual(p.get_dict('A.subdict'), + {'title': 'title A subdict'}) + self.assertEqual(p.get_dict('B.subdict'), + {'title': 'title B subdict', 'url': 'url B subdict'}) + + def test_get_dict_with_value_prefix(self): + contents = StringIO(''' +A.default=A +A.default.B=B +A.default.B.ignored=B ignored +A.default.C=C +A.default.C.ignored=C ignored +''') + p = DotProperties(contents) + self.assertEqual(p.get('A.default'), 'A') + # This enumerates the properties. + self.assertEqual(p.get_dict('A.default'), {'B': 'B', 'C': 'C'}) + # They can still be fetched directly. + self.assertEqual(p.get('A.default.B'), 'B') + self.assertEqual(p.get('A.default.C'), 'C') + + + def test_unicode(self): + contents = StringIO(''' +# Danish. +# #### ~~ Søren Munk Skrøder, sskroeder - 2009-05-30 @ #mozmae + +# Korean. +A.title=í•œë©”ì¼ + +# Russian. +list.0 = test +list.1 = Ð¯Ð½Ð´ÐµÐºÑ +''') + p = DotProperties(contents) + self.assertEqual(p.get_dict('A'), {'title': '한메ì¼'}) + self.assertEqual(p.get_list('list'), ['test', 'ЯндекÑ']) + + def test_valid_unicode_from_file(self): + # The contents of valid.properties is identical to the contents of the + # test above. This specifically exercises reading from a file. + p = DotProperties(os.path.join(test_data_path, 'valid.properties')) + self.assertEqual(p.get_dict('A'), {'title': '한메ì¼'}) + self.assertEqual(p.get_list('list'), ['test', 'ЯндекÑ']) + + def test_bad_unicode_from_file(self): + # The contents of bad.properties is not valid Unicode; see the comments + # in the file itself for details. + with self.assertRaises(UnicodeDecodeError): + DotProperties(os.path.join(test_data_path, 'bad.properties')) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_expression.py b/python/mozbuild/mozbuild/test/test_expression.py new file mode 100644 index 000000000..fb3c45894 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_expression.py @@ -0,0 +1,82 @@ +import unittest + +import sys +import os.path +import mozunit + +from mozbuild.preprocessor import Expression, Context + +class TestContext(unittest.TestCase): + """ + Unit tests for the Context class + """ + + def setUp(self): + self.c = Context() + self.c['FAIL'] = 'PASS' + + def test_string_literal(self): + """test string literal, fall-through for undefined var in a Context""" + self.assertEqual(self.c['PASS'], 'PASS') + + def test_variable(self): + """test value for defined var in the Context class""" + self.assertEqual(self.c['FAIL'], 'PASS') + + def test_in(self): + """test 'var in context' to not fall for fallback""" + self.assert_('FAIL' in self.c) + self.assert_('PASS' not in self.c) + +class TestExpression(unittest.TestCase): + """ + Unit tests for the Expression class + evaluate() is called with a context {FAIL: 'PASS'} + """ + + def setUp(self): + self.c = Context() + self.c['FAIL'] = 'PASS' + + def test_string_literal(self): + """Test for a string literal in an Expression""" + self.assertEqual(Expression('PASS').evaluate(self.c), 'PASS') + + def test_variable(self): + """Test for variable value in an Expression""" + self.assertEqual(Expression('FAIL').evaluate(self.c), 'PASS') + + def test_not(self): + """Test for the ! operator""" + self.assert_(Expression('!0').evaluate(self.c)) + self.assert_(not Expression('!1').evaluate(self.c)) + + def test_equals(self): + """ Test for the == operator""" + self.assert_(Expression('FAIL == PASS').evaluate(self.c)) + + def test_notequals(self): + """ Test for the != operator""" + self.assert_(Expression('FAIL != 1').evaluate(self.c)) + + def test_logical_and(self): + """ Test for the && operator""" + self.assertTrue(Expression('PASS == PASS && PASS != NOTPASS').evaluate(self.c)) + + def test_logical_or(self): + """ Test for the || operator""" + self.assertTrue(Expression('PASS == NOTPASS || PASS != NOTPASS').evaluate(self.c)) + + def test_logical_ops(self): + """ Test for the && and || operators precedence""" + # Would evaluate to false if precedence was wrong + self.assertTrue(Expression('PASS == PASS || PASS != NOTPASS && PASS == NOTPASS').evaluate(self.c)) + + def test_defined(self): + """ Test for the defined() value""" + self.assertTrue(Expression('defined(FAIL)').evaluate(self.c)) + self.assertTrue(Expression('!defined(PASS)').evaluate(self.c)) + + +if __name__ == '__main__': + mozunit.main() diff --git a/python/mozbuild/mozbuild/test/test_jarmaker.py b/python/mozbuild/mozbuild/test/test_jarmaker.py new file mode 100644 index 000000000..a4d4156a7 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_jarmaker.py @@ -0,0 +1,367 @@ +# 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 print_function +import unittest + +import os, sys, os.path, time, inspect +from filecmp import dircmp +from tempfile import mkdtemp +from shutil import rmtree, copy2 +from StringIO import StringIO +from zipfile import ZipFile +import mozunit + +from mozbuild.jar import JarMaker + + +if sys.platform == "win32": + import ctypes + from ctypes import POINTER, WinError + DWORD = ctypes.c_ulong + LPDWORD = POINTER(DWORD) + HANDLE = ctypes.c_void_p + GENERIC_READ = 0x80000000 + FILE_SHARE_READ = 0x00000001 + OPEN_EXISTING = 3 + MAX_PATH = 260 + + class FILETIME(ctypes.Structure): + _fields_ = [("dwLowDateTime", DWORD), + ("dwHighDateTime", DWORD)] + + class BY_HANDLE_FILE_INFORMATION(ctypes.Structure): + _fields_ = [("dwFileAttributes", DWORD), + ("ftCreationTime", FILETIME), + ("ftLastAccessTime", FILETIME), + ("ftLastWriteTime", FILETIME), + ("dwVolumeSerialNumber", DWORD), + ("nFileSizeHigh", DWORD), + ("nFileSizeLow", DWORD), + ("nNumberOfLinks", DWORD), + ("nFileIndexHigh", DWORD), + ("nFileIndexLow", DWORD)] + + # http://msdn.microsoft.com/en-us/library/aa363858 + CreateFile = ctypes.windll.kernel32.CreateFileA + CreateFile.argtypes = [ctypes.c_char_p, DWORD, DWORD, ctypes.c_void_p, + DWORD, DWORD, HANDLE] + CreateFile.restype = HANDLE + + # http://msdn.microsoft.com/en-us/library/aa364952 + GetFileInformationByHandle = ctypes.windll.kernel32.GetFileInformationByHandle + GetFileInformationByHandle.argtypes = [HANDLE, POINTER(BY_HANDLE_FILE_INFORMATION)] + GetFileInformationByHandle.restype = ctypes.c_int + + # http://msdn.microsoft.com/en-us/library/aa364996 + GetVolumePathName = ctypes.windll.kernel32.GetVolumePathNameA + GetVolumePathName.argtypes = [ctypes.c_char_p, ctypes.c_char_p, DWORD] + GetVolumePathName.restype = ctypes.c_int + + # http://msdn.microsoft.com/en-us/library/aa364993 + GetVolumeInformation = ctypes.windll.kernel32.GetVolumeInformationA + GetVolumeInformation.argtypes = [ctypes.c_char_p, ctypes.c_char_p, DWORD, + LPDWORD, LPDWORD, LPDWORD, ctypes.c_char_p, + DWORD] + GetVolumeInformation.restype = ctypes.c_int + +def symlinks_supported(path): + if sys.platform == "win32": + # Add 1 for a trailing backslash if necessary, and 1 for the terminating + # null character. + volpath = ctypes.create_string_buffer(len(path) + 2) + rv = GetVolumePathName(path, volpath, len(volpath)) + if rv == 0: + raise WinError() + + fsname = ctypes.create_string_buffer(MAX_PATH + 1) + rv = GetVolumeInformation(volpath, None, 0, None, None, None, fsname, + len(fsname)) + if rv == 0: + raise WinError() + + # Return true only if the fsname is NTFS + return fsname.value == "NTFS" + else: + return True + +def _getfileinfo(path): + """Return information for the given file. This only works on Windows.""" + fh = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, None, OPEN_EXISTING, 0, None) + if fh is None: + raise WinError() + info = BY_HANDLE_FILE_INFORMATION() + rv = GetFileInformationByHandle(fh, info) + if rv == 0: + raise WinError() + return info + +def is_symlink_to(dest, src): + if sys.platform == "win32": + # Check if both are on the same volume and have the same file ID + destinfo = _getfileinfo(dest) + srcinfo = _getfileinfo(src) + return (destinfo.dwVolumeSerialNumber == srcinfo.dwVolumeSerialNumber and + destinfo.nFileIndexHigh == srcinfo.nFileIndexHigh and + destinfo.nFileIndexLow == srcinfo.nFileIndexLow) + else: + # Read the link and check if it is correct + if not os.path.islink(dest): + return False + target = os.path.abspath(os.readlink(dest)) + abssrc = os.path.abspath(src) + return target == abssrc + +class _TreeDiff(dircmp): + """Helper to report rich results on difference between two directories. + """ + def _fillDiff(self, dc, rv, basepath="{0}"): + rv['right_only'] += map(lambda l: basepath.format(l), dc.right_only) + rv['left_only'] += map(lambda l: basepath.format(l), dc.left_only) + rv['diff_files'] += map(lambda l: basepath.format(l), dc.diff_files) + rv['funny'] += map(lambda l: basepath.format(l), dc.common_funny) + rv['funny'] += map(lambda l: basepath.format(l), dc.funny_files) + for subdir, _dc in dc.subdirs.iteritems(): + self._fillDiff(_dc, rv, basepath.format(subdir + "/{0}")) + def allResults(self, left, right): + rv = {'right_only':[], 'left_only':[], + 'diff_files':[], 'funny': []} + self._fillDiff(self, rv) + chunks = [] + if rv['right_only']: + chunks.append('{0} only in {1}'.format(', '.join(rv['right_only']), + right)) + if rv['left_only']: + chunks.append('{0} only in {1}'.format(', '.join(rv['left_only']), + left)) + if rv['diff_files']: + chunks.append('{0} differ'.format(', '.join(rv['diff_files']))) + if rv['funny']: + chunks.append("{0} don't compare".format(', '.join(rv['funny']))) + return '; '.join(chunks) + +class TestJarMaker(unittest.TestCase): + """ + Unit tests for JarMaker.py + """ + debug = False # set to True to debug failing tests on disk + def setUp(self): + self.tmpdir = mkdtemp() + self.srcdir = os.path.join(self.tmpdir, 'src') + os.mkdir(self.srcdir) + self.builddir = os.path.join(self.tmpdir, 'build') + os.mkdir(self.builddir) + self.refdir = os.path.join(self.tmpdir, 'ref') + os.mkdir(self.refdir) + self.stagedir = os.path.join(self.tmpdir, 'stage') + os.mkdir(self.stagedir) + + def tearDown(self): + if self.debug: + print(self.tmpdir) + elif sys.platform != "win32": + # can't clean up on windows + rmtree(self.tmpdir) + + def _jar_and_compare(self, infile, **kwargs): + jm = JarMaker(outputFormat='jar') + if 'topsourcedir' not in kwargs: + kwargs['topsourcedir'] = self.srcdir + for attr in ('topsourcedir', 'sourcedirs'): + if attr in kwargs: + setattr(jm, attr, kwargs[attr]) + jm.makeJar(infile, self.builddir) + cwd = os.getcwd() + os.chdir(self.builddir) + try: + # expand build to stage + for path, dirs, files in os.walk('.'): + stagedir = os.path.join(self.stagedir, path) + if not os.path.isdir(stagedir): + os.mkdir(stagedir) + for file in files: + if file.endswith('.jar'): + # expand jar + stagepath = os.path.join(stagedir, file) + os.mkdir(stagepath) + zf = ZipFile(os.path.join(path, file)) + # extractall is only in 2.6, do this manually :-( + for entry_name in zf.namelist(): + segs = entry_name.split('/') + fname = segs.pop() + dname = os.path.join(stagepath, *segs) + if not os.path.isdir(dname): + os.makedirs(dname) + if not fname: + # directory, we're done + continue + _c = zf.read(entry_name) + open(os.path.join(dname, fname), 'wb').write(_c) + zf.close() + else: + copy2(os.path.join(path, file), stagedir) + # compare both dirs + os.chdir('..') + td = _TreeDiff('ref', 'stage') + return td.allResults('reference', 'build') + finally: + os.chdir(cwd) + + def _create_simple_setup(self): + # create src content + jarf = open(os.path.join(self.srcdir, 'jar.mn'), 'w') + jarf.write('''test.jar: + dir/foo (bar) +''') + jarf.close() + open(os.path.join(self.srcdir,'bar'),'w').write('content\n') + # create reference + refpath = os.path.join(self.refdir, 'chrome', 'test.jar', 'dir') + os.makedirs(refpath) + open(os.path.join(refpath, 'foo'), 'w').write('content\n') + + def test_a_simple_jar(self): + '''Test a simple jar.mn''' + self._create_simple_setup() + # call JarMaker + rv = self._jar_and_compare(os.path.join(self.srcdir,'jar.mn'), + sourcedirs = [self.srcdir]) + self.assertTrue(not rv, rv) + + def test_a_simple_symlink(self): + '''Test a simple jar.mn with a symlink''' + if not symlinks_supported(self.srcdir): + raise unittest.SkipTest('symlinks not supported') + + self._create_simple_setup() + jm = JarMaker(outputFormat='symlink') + jm.sourcedirs = [self.srcdir] + jm.topsourcedir = self.srcdir + jm.makeJar(os.path.join(self.srcdir,'jar.mn'), self.builddir) + # All we do is check that srcdir/bar points to builddir/chrome/test/dir/foo + srcbar = os.path.join(self.srcdir, 'bar') + destfoo = os.path.join(self.builddir, 'chrome', 'test', 'dir', 'foo') + self.assertTrue(is_symlink_to(destfoo, srcbar), + "{0} is not a symlink to {1}".format(destfoo, srcbar)) + + def _create_wildcard_setup(self): + # create src content + jarf = open(os.path.join(self.srcdir, 'jar.mn'), 'w') + jarf.write('''test.jar: + dir/bar (*.js) + dir/hoge (qux/*) +''') + jarf.close() + open(os.path.join(self.srcdir,'foo.js'),'w').write('foo.js\n') + open(os.path.join(self.srcdir,'bar.js'),'w').write('bar.js\n') + os.makedirs(os.path.join(self.srcdir, 'qux', 'foo')) + open(os.path.join(self.srcdir,'qux', 'foo', '1'),'w').write('1\n') + open(os.path.join(self.srcdir,'qux', 'foo', '2'),'w').write('2\n') + open(os.path.join(self.srcdir,'qux', 'baz'),'w').write('baz\n') + # create reference + refpath = os.path.join(self.refdir, 'chrome', 'test.jar', 'dir') + os.makedirs(os.path.join(refpath, 'bar')) + os.makedirs(os.path.join(refpath, 'hoge', 'foo')) + open(os.path.join(refpath, 'bar', 'foo.js'), 'w').write('foo.js\n') + open(os.path.join(refpath, 'bar', 'bar.js'), 'w').write('bar.js\n') + open(os.path.join(refpath, 'hoge', 'foo', '1'), 'w').write('1\n') + open(os.path.join(refpath, 'hoge', 'foo', '2'), 'w').write('2\n') + open(os.path.join(refpath, 'hoge', 'baz'), 'w').write('baz\n') + + def test_a_wildcard_jar(self): + '''Test a wildcard in jar.mn''' + self._create_wildcard_setup() + # call JarMaker + rv = self._jar_and_compare(os.path.join(self.srcdir,'jar.mn'), + sourcedirs = [self.srcdir]) + self.assertTrue(not rv, rv) + + def test_a_wildcard_symlink(self): + '''Test a wildcard in jar.mn with symlinks''' + if not symlinks_supported(self.srcdir): + raise unittest.SkipTest('symlinks not supported') + + self._create_wildcard_setup() + jm = JarMaker(outputFormat='symlink') + jm.sourcedirs = [self.srcdir] + jm.topsourcedir = self.srcdir + jm.makeJar(os.path.join(self.srcdir,'jar.mn'), self.builddir) + + expected_symlinks = { + ('bar', 'foo.js'): ('foo.js',), + ('bar', 'bar.js'): ('bar.js',), + ('hoge', 'foo', '1'): ('qux', 'foo', '1'), + ('hoge', 'foo', '2'): ('qux', 'foo', '2'), + ('hoge', 'baz'): ('qux', 'baz'), + } + for dest, src in expected_symlinks.iteritems(): + srcpath = os.path.join(self.srcdir, *src) + destpath = os.path.join(self.builddir, 'chrome', 'test', 'dir', + *dest) + self.assertTrue(is_symlink_to(destpath, srcpath), + "{0} is not a symlink to {1}".format(destpath, + srcpath)) + + +class Test_relativesrcdir(unittest.TestCase): + def setUp(self): + self.jm = JarMaker() + self.jm.topsourcedir = '/TOPSOURCEDIR' + self.jm.relativesrcdir = 'browser/locales' + self.fake_empty_file = StringIO() + self.fake_empty_file.name = 'fake_empty_file' + def tearDown(self): + del self.jm + del self.fake_empty_file + def test_en_US(self): + jm = self.jm + jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED') + self.assertEquals(jm.localedirs, + [ + os.path.join(os.path.abspath('/TOPSOURCEDIR'), + 'browser/locales', 'en-US') + ]) + def test_l10n_no_merge(self): + jm = self.jm + jm.l10nbase = '/L10N_BASE' + jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED') + self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'browser')]) + def test_l10n_merge(self): + jm = self.jm + jm.l10nbase = '/L10N_BASE' + jm.l10nmerge = '/L10N_MERGE' + jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED') + self.assertEquals(jm.localedirs, + [os.path.join('/L10N_MERGE', 'browser'), + os.path.join('/L10N_BASE', 'browser'), + os.path.join(os.path.abspath('/TOPSOURCEDIR'), + 'browser/locales', 'en-US') + ]) + def test_override(self): + jm = self.jm + jm.outputFormat = 'flat' # doesn't touch chrome dir without files + jarcontents = StringIO('''en-US.jar: +relativesrcdir dom/locales: +''') + jarcontents.name = 'override.mn' + jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED') + self.assertEquals(jm.localedirs, + [ + os.path.join(os.path.abspath('/TOPSOURCEDIR'), + 'dom/locales', 'en-US') + ]) + def test_override_l10n(self): + jm = self.jm + jm.l10nbase = '/L10N_BASE' + jm.outputFormat = 'flat' # doesn't touch chrome dir without files + jarcontents = StringIO('''en-US.jar: +relativesrcdir dom/locales: +''') + jarcontents.name = 'override.mn' + jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED') + self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'dom')]) + + +if __name__ == '__main__': + mozunit.main() diff --git a/python/mozbuild/mozbuild/test/test_line_endings.py b/python/mozbuild/mozbuild/test/test_line_endings.py new file mode 100644 index 000000000..565abc8c9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_line_endings.py @@ -0,0 +1,46 @@ +import unittest + +from StringIO import StringIO +import os +import sys +import os.path +import mozunit + +from mozbuild.preprocessor import Preprocessor + +class TestLineEndings(unittest.TestCase): + """ + Unit tests for the Context class + """ + + def setUp(self): + self.pp = Preprocessor() + self.pp.out = StringIO() + self.tempnam = os.tempnam('.') + + def tearDown(self): + os.remove(self.tempnam) + + def createFile(self, lineendings): + f = open(self.tempnam, 'wb') + for line, ending in zip(['a', '#literal b', 'c'], lineendings): + f.write(line+ending) + f.close() + + def testMac(self): + self.createFile(['\x0D']*3) + self.pp.do_include(self.tempnam) + self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n') + + def testUnix(self): + self.createFile(['\x0A']*3) + self.pp.do_include(self.tempnam) + self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n') + + def testWindows(self): + self.createFile(['\x0D\x0A']*3) + self.pp.do_include(self.tempnam) + self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n') + +if __name__ == '__main__': + mozunit.main() diff --git a/python/mozbuild/mozbuild/test/test_makeutil.py b/python/mozbuild/mozbuild/test/test_makeutil.py new file mode 100644 index 000000000..6fffa0e0e --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_makeutil.py @@ -0,0 +1,165 @@ +# 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 mozbuild.makeutil import ( + Makefile, + read_dep_makefile, + Rule, + write_dep_makefile, +) +from mozunit import main +import os +import unittest +from StringIO import StringIO + + +class TestMakefile(unittest.TestCase): + def test_rule(self): + out = StringIO() + rule = Rule() + rule.dump(out) + self.assertEqual(out.getvalue(), '') + out.truncate(0) + + rule.add_targets(['foo', 'bar']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar:\n') + out.truncate(0) + + rule.add_targets(['baz']) + rule.add_dependencies(['qux', 'hoge', 'piyo']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar baz: qux hoge piyo\n') + out.truncate(0) + + rule = Rule(['foo', 'bar']) + rule.add_dependencies(['baz']) + rule.add_commands(['echo $@']) + rule.add_commands(['$(BAZ) -o $@ $<', '$(TOUCH) $@']) + rule.dump(out) + self.assertEqual(out.getvalue(), + 'foo bar: baz\n' + + '\techo $@\n' + + '\t$(BAZ) -o $@ $<\n' + + '\t$(TOUCH) $@\n') + out.truncate(0) + + rule = Rule(['foo']) + rule.add_dependencies(['bar', 'foo', 'baz']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo: bar baz\n') + out.truncate(0) + + rule.add_targets(['bar']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar: baz\n') + out.truncate(0) + + rule.add_targets(['bar']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar: baz\n') + out.truncate(0) + + rule.add_dependencies(['bar']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar: baz\n') + out.truncate(0) + + rule.add_dependencies(['qux']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar: baz qux\n') + out.truncate(0) + + rule.add_dependencies(['qux']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar: baz qux\n') + out.truncate(0) + + rule.add_dependencies(['hoge', 'hoge']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar: baz qux hoge\n') + out.truncate(0) + + rule.add_targets(['fuga', 'fuga']) + rule.dump(out) + self.assertEqual(out.getvalue(), 'foo bar fuga: baz qux hoge\n') + + def test_makefile(self): + out = StringIO() + mk = Makefile() + rule = mk.create_rule(['foo']) + rule.add_dependencies(['bar', 'baz', 'qux']) + rule.add_commands(['echo foo']) + rule = mk.create_rule().add_targets(['bar', 'baz']) + rule.add_dependencies(['hoge']) + rule.add_commands(['echo $@']) + mk.dump(out, removal_guard=False) + self.assertEqual(out.getvalue(), + 'foo: bar baz qux\n' + + '\techo foo\n' + + 'bar baz: hoge\n' + + '\techo $@\n') + out.truncate(0) + + mk.dump(out) + self.assertEqual(out.getvalue(), + 'foo: bar baz qux\n' + + '\techo foo\n' + + 'bar baz: hoge\n' + + '\techo $@\n' + + 'hoge qux:\n') + + def test_statement(self): + out = StringIO() + mk = Makefile() + mk.create_rule(['foo']).add_dependencies(['bar']) \ + .add_commands(['echo foo']) + mk.add_statement('BAR = bar') + mk.create_rule(['$(BAR)']).add_commands(['echo $@']) + mk.dump(out, removal_guard=False) + self.assertEqual(out.getvalue(), + 'foo: bar\n' + + '\techo foo\n' + + 'BAR = bar\n' + + '$(BAR):\n' + + '\techo $@\n') + + @unittest.skipIf(os.name != 'nt', 'Test only applicable on Windows.') + def test_path_normalization(self): + out = StringIO() + mk = Makefile() + rule = mk.create_rule(['c:\\foo']) + rule.add_dependencies(['c:\\bar', 'c:\\baz\\qux']) + rule.add_commands(['echo c:\\foo']) + mk.dump(out) + self.assertEqual(out.getvalue(), + 'c:/foo: c:/bar c:/baz/qux\n' + + '\techo c:\\foo\n' + + 'c:/bar c:/baz/qux:\n') + + def test_read_dep_makefile(self): + input = StringIO( + os.path.abspath('foo') + ': bar\n' + + 'baz qux: \\ \n' + + 'hoge \\\n' + + 'piyo \\\n' + + 'fuga\n' + + 'fuga:\n' + ) + result = list(read_dep_makefile(input)) + self.assertEqual(len(result), 2) + self.assertEqual(list(result[0].targets()), [os.path.abspath('foo').replace(os.sep, '/')]) + self.assertEqual(list(result[0].dependencies()), ['bar']) + self.assertEqual(list(result[1].targets()), ['baz', 'qux']) + self.assertEqual(list(result[1].dependencies()), ['hoge', 'piyo', 'fuga']) + + def test_write_dep_makefile(self): + out = StringIO() + write_dep_makefile(out, 'target', ['b', 'c', 'a']) + self.assertEqual(out.getvalue(), + 'target: b c a\n' + + 'a b c:\n') + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_mozconfig.py b/python/mozbuild/mozbuild/test/test_mozconfig.py new file mode 100644 index 000000000..0cd125912 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_mozconfig.py @@ -0,0 +1,489 @@ +# 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('MOZ_OBJDIR', 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_multiple_configs(self): + """Ensure multiple relative-path MOZCONFIGs result in error.""" + relative_mozconfig = '.mconfig' + os.environ[b'MOZCONFIG'] = relative_mozconfig + + srcdir = self.get_temp_dir() + curdir = self.get_temp_dir() + dirs = [srcdir, curdir] + loader = MozconfigLoader(srcdir) + for d in dirs: + path = os.path.join(d, relative_mozconfig) + with open(path, 'wb') as f: + f.write(path) + + orig_dir = os.getcwd() + try: + os.chdir(curdir) + with self.assertRaises(MozconfigFindException) as e: + loader.find_mozconfig() + finally: + os.chdir(orig_dir) + + self.assertIn('exists in more than one of', e.exception.message) + for d in dirs: + self.assertIn(d, e.exception.message) + + def test_find_multiple_but_identical_configs(self): + """Ensure multiple relative-path MOZCONFIGs pointing at the same file are OK.""" + relative_mozconfig = '../src/.mconfig' + os.environ[b'MOZCONFIG'] = relative_mozconfig + + topdir = self.get_temp_dir() + srcdir = os.path.join(topdir, 'src') + os.mkdir(srcdir) + curdir = os.path.join(topdir, 'obj') + os.mkdir(curdir) + + loader = MozconfigLoader(srcdir) + path = os.path.join(srcdir, relative_mozconfig) + with open(path, 'w'): + pass + + orig_dir = os.getcwd() + try: + os.chdir(curdir) + self.assertEqual(os.path.realpath(loader.find_mozconfig()), + os.path.realpath(path)) + finally: + os.chdir(orig_dir) + + def test_find_no_relative_configs(self): + """Ensure a missing relative-path MOZCONFIG is detected.""" + relative_mozconfig = '.mconfig' + os.environ[b'MOZCONFIG'] = relative_mozconfig + + srcdir = self.get_temp_dir() + curdir = self.get_temp_dir() + dirs = [srcdir, curdir] + loader = MozconfigLoader(srcdir) + + orig_dir = os.getcwd() + try: + os.chdir(curdir) + with self.assertRaises(MozconfigFindException) as e: + loader.find_mozconfig() + finally: + os.chdir(orig_dir) + + self.assertIn('does not exist in any of', e.exception.message) + for d in dirs: + self.assertIn(d, e.exception.message) + + def test_find_relative_mozconfig(self): + """Ensure a relative MOZCONFIG can be found in the srcdir.""" + relative_mozconfig = '.mconfig' + os.environ[b'MOZCONFIG'] = relative_mozconfig + + srcdir = self.get_temp_dir() + curdir = self.get_temp_dir() + dirs = [srcdir, curdir] + loader = MozconfigLoader(srcdir) + + path = os.path.join(srcdir, relative_mozconfig) + with open(path, 'w'): + pass + + orig_dir = os.getcwd() + try: + os.chdir(curdir) + self.assertEqual(os.path.normpath(loader.find_mozconfig()), + os.path.normpath(path)) + finally: + os.chdir(orig_dir) + + def test_find_abs_path_not_exist(self): + """Ensure a missing absolute path is 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, + 'vars': 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['vars'][f]), 0) + self.assertEqual(len(result['env'][f]), 0) + + self.assertEqual(result['env']['unmodified'], {}) + + 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 baz"\n') + mozconfig.flush() + + result = self.get_loader().read_mozconfig(mozconfig.name) + self.assertEqual(result['configure_args'], [ + '--enable-debug', '--disable-tests', '--enable-foo', + '--foo=bar baz']) + + 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 -s"\n') + mozconfig.write('mk_add_options FOO="BAR BAZ"\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', '-s']) + self.assertEqual(result['make_extra'], ['FOO=BAR BAZ', 'BIZ=1']) + + vars = result['vars']['added'] + for var in ('MOZ_OBJDIR', 'MOZ_MAKE_FLAGS', 'FOO', 'BIZ'): + self.assertEqual(vars.get('%s_IS_SET' % var), '1') + + def test_read_empty_mozconfig_objdir_environ(self): + os.environ[b'MOZ_OBJDIR'] = b'obj-firefox' + with NamedTemporaryFile(mode='w') as mozconfig: + result = self.get_loader().read_mozconfig(mozconfig.name) + self.assertEqual(result['topobjdir'], 'obj-firefox') + + def test_read_capture_mk_options_objdir_environ(self): + """Ensures mk_add_options calls are captured and override the environ.""" + os.environ[b'MOZ_OBJDIR'] = b'obj-firefox' + with NamedTemporaryFile(mode='w') as mozconfig: + mozconfig.write('mk_add_options MOZ_OBJDIR=/foo/bar\n') + mozconfig.flush() + + result = self.get_loader().read_mozconfig(mozconfig.name) + self.assertEqual(result['topobjdir'], '/foo/bar') + + 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['vars']['added'], { + 'CC': '/usr/local/bin/clang', + 'CXX': '/usr/local/bin/clang++'}) + self.assertEqual(result['env']['added'], {}) + + 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['vars']['added'], {}) + self.assertEqual(result['env']['added'], { + 'MY_EXPORTED': 'woot'}) + + def test_read_modify_variables(self): + """Variables modified by mozconfig are detected.""" + old_path = os.path.realpath(b'/usr/bin/gcc') + new_path = os.path.realpath(b'/usr/local/bin/clang') + os.environ[b'CC'] = old_path + + with NamedTemporaryFile(mode='w') as mozconfig: + mozconfig.write('CC="%s"\n' % new_path) + mozconfig.flush() + + result = self.get_loader().read_mozconfig(mozconfig.name) + + self.assertEqual(result['vars']['modified'], {}) + self.assertEqual(result['env']['modified'], { + 'CC': (old_path, new_path) + }) + + def test_read_unmodified_variables(self): + """Variables modified by mozconfig are detected.""" + cc_path = os.path.realpath(b'/usr/bin/gcc') + os.environ[b'CC'] = cc_path + + with NamedTemporaryFile(mode='w') as mozconfig: + mozconfig.flush() + + result = self.get_loader().read_mozconfig(mozconfig.name) + + self.assertEqual(result['vars']['unmodified'], {}) + self.assertEqual(result['env']['unmodified'], { + 'CC': cc_path + }) + + def test_read_removed_variables(self): + """Variables unset by the mozconfig are detected.""" + cc_path = os.path.realpath(b'/usr/bin/clang') + os.environ[b'CC'] = cc_path + + with NamedTemporaryFile(mode='w') as mozconfig: + mozconfig.write('unset CC\n') + mozconfig.flush() + + result = self.get_loader().read_mozconfig(mozconfig.name) + + self.assertEqual(result['vars']['removed'], {}) + self.assertEqual(result['env']['removed'], { + 'CC': cc_path}) + + 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['vars']['added'], { + 'multi': 'foo\nbar', + 'single': '1' + }) + self.assertEqual(result['env']['added'], {}) + + 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['vars']['added']['TEST'], + loader.topsrcdir.replace(os.sep, '/')) + self.assertEqual(result['env']['added'], {}) + + 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.write('export EXPORT_EMPTY=\n') + mozconfig.flush() + + result = self.get_loader().read_mozconfig(mozconfig.name) + + self.assertEqual(result['vars']['added'], { + 'EMPTY': '', + }) + self.assertEqual(result['env']['added'], { + 'EXPORT_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() diff --git a/python/mozbuild/mozbuild/test/test_mozinfo.py b/python/mozbuild/mozbuild/test/test_mozinfo.py new file mode 100755 index 000000000..1a4194cb5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_mozinfo.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +# 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/. + +import json +import os +import tempfile +import unittest + +from StringIO import StringIO + +import mozunit + +from mozbuild.backend.configenvironment import ConfigEnvironment + +from mozbuild.mozinfo import ( + build_dict, + write_mozinfo, +) + +from mozfile.mozfile import NamedTemporaryFile + + +class Base(object): + def _config(self, substs={}): + d = os.path.dirname(__file__) + return ConfigEnvironment(d, d, substs=substs) + + +class TestBuildDict(unittest.TestCase, Base): + def test_missing(self): + """ + Test that missing required values raises. + """ + + with self.assertRaises(Exception): + build_dict(self._config(substs=dict(OS_TARGET='foo'))) + + with self.assertRaises(Exception): + build_dict(self._config(substs=dict(TARGET_CPU='foo'))) + + with self.assertRaises(Exception): + build_dict(self._config(substs=dict(MOZ_WIDGET_TOOLKIT='foo'))) + + def test_win(self): + d = build_dict(self._config(dict( + OS_TARGET='WINNT', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='windows', + ))) + self.assertEqual('win', d['os']) + self.assertEqual('x86', d['processor']) + self.assertEqual('windows', d['toolkit']) + self.assertEqual(32, d['bits']) + + def test_linux(self): + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='gtk2', + ))) + self.assertEqual('linux', d['os']) + self.assertEqual('x86', d['processor']) + self.assertEqual('gtk2', d['toolkit']) + self.assertEqual(32, d['bits']) + + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='x86_64', + MOZ_WIDGET_TOOLKIT='gtk2', + ))) + self.assertEqual('linux', d['os']) + self.assertEqual('x86_64', d['processor']) + self.assertEqual('gtk2', d['toolkit']) + self.assertEqual(64, d['bits']) + + def test_mac(self): + d = build_dict(self._config(dict( + OS_TARGET='Darwin', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='cocoa', + ))) + self.assertEqual('mac', d['os']) + self.assertEqual('x86', d['processor']) + self.assertEqual('cocoa', d['toolkit']) + self.assertEqual(32, d['bits']) + + d = build_dict(self._config(dict( + OS_TARGET='Darwin', + TARGET_CPU='x86_64', + MOZ_WIDGET_TOOLKIT='cocoa', + ))) + self.assertEqual('mac', d['os']) + self.assertEqual('x86_64', d['processor']) + self.assertEqual('cocoa', d['toolkit']) + self.assertEqual(64, d['bits']) + + def test_mac_universal(self): + d = build_dict(self._config(dict( + OS_TARGET='Darwin', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='cocoa', + UNIVERSAL_BINARY='1', + ))) + self.assertEqual('mac', d['os']) + self.assertEqual('universal-x86-x86_64', d['processor']) + self.assertEqual('cocoa', d['toolkit']) + self.assertFalse('bits' in d) + + d = build_dict(self._config(dict( + OS_TARGET='Darwin', + TARGET_CPU='x86_64', + MOZ_WIDGET_TOOLKIT='cocoa', + UNIVERSAL_BINARY='1', + ))) + self.assertEqual('mac', d['os']) + self.assertEqual('universal-x86-x86_64', d['processor']) + self.assertEqual('cocoa', d['toolkit']) + self.assertFalse('bits' in d) + + def test_android(self): + d = build_dict(self._config(dict( + OS_TARGET='Android', + TARGET_CPU='arm', + MOZ_WIDGET_TOOLKIT='android', + ))) + self.assertEqual('android', d['os']) + self.assertEqual('arm', d['processor']) + self.assertEqual('android', d['toolkit']) + self.assertEqual(32, d['bits']) + + def test_x86(self): + """ + Test that various i?86 values => x86. + """ + d = build_dict(self._config(dict( + OS_TARGET='WINNT', + TARGET_CPU='i486', + MOZ_WIDGET_TOOLKIT='windows', + ))) + self.assertEqual('x86', d['processor']) + + d = build_dict(self._config(dict( + OS_TARGET='WINNT', + TARGET_CPU='i686', + MOZ_WIDGET_TOOLKIT='windows', + ))) + self.assertEqual('x86', d['processor']) + + def test_arm(self): + """ + Test that all arm CPU architectures => arm. + """ + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='arm', + MOZ_WIDGET_TOOLKIT='gtk2', + ))) + self.assertEqual('arm', d['processor']) + + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='armv7', + MOZ_WIDGET_TOOLKIT='gtk2', + ))) + self.assertEqual('arm', d['processor']) + + def test_unknown(self): + """ + Test that unknown values pass through okay. + """ + d = build_dict(self._config(dict( + OS_TARGET='RandOS', + TARGET_CPU='cptwo', + MOZ_WIDGET_TOOLKIT='foobar', + ))) + self.assertEqual("randos", d["os"]) + self.assertEqual("cptwo", d["processor"]) + self.assertEqual("foobar", d["toolkit"]) + # unknown CPUs should not get a bits value + self.assertFalse("bits" in d) + + def test_debug(self): + """ + Test that debug values are properly detected. + """ + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='gtk2', + ))) + self.assertEqual(False, d['debug']) + + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='gtk2', + MOZ_DEBUG='1', + ))) + self.assertEqual(True, d['debug']) + + def test_crashreporter(self): + """ + Test that crashreporter values are properly detected. + """ + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='gtk2', + ))) + self.assertEqual(False, d['crashreporter']) + + d = build_dict(self._config(dict( + OS_TARGET='Linux', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='gtk2', + MOZ_CRASHREPORTER='1', + ))) + self.assertEqual(True, d['crashreporter']) + + +class TestWriteMozinfo(unittest.TestCase, Base): + """ + Test the write_mozinfo function. + """ + def setUp(self): + fd, self.f = tempfile.mkstemp() + os.close(fd) + + def tearDown(self): + os.unlink(self.f) + + def test_basic(self): + """ + Test that writing to a file produces correct output. + """ + c = self._config(dict( + OS_TARGET='WINNT', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='windows', + )) + tempdir = tempfile.tempdir + c.topsrcdir = tempdir + with NamedTemporaryFile(dir=os.path.normpath(c.topsrcdir)) as mozconfig: + mozconfig.write('unused contents') + mozconfig.flush() + c.mozconfig = mozconfig.name + write_mozinfo(self.f, c) + with open(self.f) as f: + d = json.load(f) + self.assertEqual('win', d['os']) + self.assertEqual('x86', d['processor']) + self.assertEqual('windows', d['toolkit']) + self.assertEqual(tempdir, d['topsrcdir']) + self.assertEqual(mozconfig.name, d['mozconfig']) + self.assertEqual(32, d['bits']) + + def test_fileobj(self): + """ + Test that writing to a file-like object produces correct output. + """ + s = StringIO() + c = self._config(dict( + OS_TARGET='WINNT', + TARGET_CPU='i386', + MOZ_WIDGET_TOOLKIT='windows', + )) + write_mozinfo(s, c) + d = json.loads(s.getvalue()) + self.assertEqual('win', d['os']) + self.assertEqual('x86', d['processor']) + self.assertEqual('windows', d['toolkit']) + self.assertEqual(32, d['bits']) + + +if __name__ == '__main__': + mozunit.main() diff --git a/python/mozbuild/mozbuild/test/test_preprocessor.py b/python/mozbuild/mozbuild/test/test_preprocessor.py new file mode 100644 index 000000000..9aba94853 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_preprocessor.py @@ -0,0 +1,646 @@ +# 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/. + +import unittest + +from StringIO import StringIO +import os +import shutil + +from tempfile import mkdtemp + +from mozunit import main, MockedOpen + +from mozbuild.preprocessor import Preprocessor + + +class TestPreprocessor(unittest.TestCase): + """ + Unit tests for the Context class + """ + + def setUp(self): + self.pp = Preprocessor() + self.pp.out = StringIO() + + def do_include_compare(self, content_lines, expected_lines): + content = '%s' % '\n'.join(content_lines) + expected = '%s'.rstrip() % '\n'.join(expected_lines) + + with MockedOpen({'dummy': content}): + self.pp.do_include('dummy') + self.assertEqual(self.pp.out.getvalue().rstrip('\n'), expected) + + def do_include_pass(self, content_lines): + self.do_include_compare(content_lines, ['PASS']) + + def test_conditional_if_0(self): + self.do_include_pass([ + '#if 0', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_no_marker(self): + lines = [ + '#if 0', + 'PASS', + '#endif', + ] + self.pp.setMarker(None) + self.do_include_compare(lines, lines) + + def test_string_value(self): + self.do_include_compare([ + '#define FOO STRING', + '#if FOO', + 'string value is true', + '#else', + 'string value is false', + '#endif', + ], ['string value is false']) + + def test_number_value(self): + self.do_include_compare([ + '#define FOO 1', + '#if FOO', + 'number value is true', + '#else', + 'number value is false', + '#endif', + ], ['number value is true']) + + def test_conditional_if_0_elif_1(self): + self.do_include_pass([ + '#if 0', + '#elif 1', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_conditional_if_1(self): + self.do_include_pass([ + '#if 1', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_conditional_if_0_or_1(self): + self.do_include_pass([ + '#if 0 || 1', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_conditional_if_1_elif_1_else(self): + self.do_include_pass([ + '#if 1', + 'PASS', + '#elif 1', + 'FAIL', + '#else', + 'FAIL', + '#endif', + ]) + + def test_conditional_if_1_if_1(self): + self.do_include_pass([ + '#if 1', + '#if 1', + 'PASS', + '#else', + 'FAIL', + '#endif', + '#else', + 'FAIL', + '#endif', + ]) + + def test_conditional_not_0(self): + self.do_include_pass([ + '#if !0', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_conditional_not_0_and_1(self): + self.do_include_pass([ + '#if !0 && !1', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_conditional_not_1(self): + self.do_include_pass([ + '#if !1', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_conditional_not_emptyval(self): + self.do_include_compare([ + '#define EMPTYVAL', + '#ifndef EMPTYVAL', + 'FAIL', + '#else', + 'PASS', + '#endif', + '#ifdef EMPTYVAL', + 'PASS', + '#else', + 'FAIL', + '#endif', + ], ['PASS', 'PASS']) + + def test_conditional_not_nullval(self): + self.do_include_pass([ + '#define NULLVAL 0', + '#if !NULLVAL', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_expand(self): + self.do_include_pass([ + '#define ASVAR AS', + '#expand P__ASVAR__S', + ]) + + def test_undef_defined(self): + self.do_include_compare([ + '#define BAR', + '#undef BAR', + 'BAR', + ], ['BAR']) + + def test_undef_undefined(self): + self.do_include_compare([ + '#undef BAR', + ], []) + + def test_filter_attemptSubstitution(self): + self.do_include_compare([ + '#filter attemptSubstitution', + '@PASS@', + '#unfilter attemptSubstitution', + ], ['@PASS@']) + + def test_filter_emptyLines(self): + self.do_include_compare([ + 'lines with a', + '', + 'blank line', + '#filter emptyLines', + 'lines with', + '', + 'no blank lines', + '#unfilter emptyLines', + 'yet more lines with', + '', + 'blank lines', + ], [ + 'lines with a', + '', + 'blank line', + 'lines with', + 'no blank lines', + 'yet more lines with', + '', + 'blank lines', + ]) + + def test_filter_slashslash(self): + self.do_include_compare([ + '#filter slashslash', + 'PASS//FAIL // FAIL', + '#unfilter slashslash', + 'PASS // PASS', + ], [ + 'PASS', + 'PASS // PASS', + ]) + + def test_filter_spaces(self): + self.do_include_compare([ + '#filter spaces', + 'You should see two nice ascii tables', + ' +-+-+-+', + ' | | | |', + ' +-+-+-+', + '#unfilter spaces', + '+-+---+', + '| | |', + '+-+---+', + ], [ + 'You should see two nice ascii tables', + '+-+-+-+', + '| | | |', + '+-+-+-+', + '+-+---+', + '| | |', + '+-+---+', + ]) + + def test_filter_substitution(self): + self.do_include_pass([ + '#define VAR ASS', + '#filter substitution', + 'P@VAR@', + '#unfilter substitution', + ]) + + def test_error(self): + with MockedOpen({'f': '#error spit this message out\n'}): + with self.assertRaises(Preprocessor.Error) as e: + self.pp.do_include('f') + self.assertEqual(e.args[0][-1], 'spit this message out') + + def test_javascript_line(self): + # The preprocessor is reading the filename from somewhere not caught + # by MockedOpen. + tmpdir = mkdtemp() + try: + full = os.path.join(tmpdir, 'javascript_line.js.in') + with open(full, 'w') as fh: + fh.write('\n'.join([ + '// Line 1', + '#if 0', + '// line 3', + '#endif', + '// line 5', + '# comment', + '// line 7', + '// line 8', + '// line 9', + '# another comment', + '// line 11', + '#define LINE 1', + '// line 13, given line number overwritten with 2', + '', + ])) + + self.pp.do_include(full) + out = '\n'.join([ + '// Line 1', + '//@line 5 "CWDjavascript_line.js.in"', + '// line 5', + '//@line 7 "CWDjavascript_line.js.in"', + '// line 7', + '// line 8', + '// line 9', + '//@line 11 "CWDjavascript_line.js.in"', + '// line 11', + '//@line 2 "CWDjavascript_line.js.in"', + '// line 13, given line number overwritten with 2', + '', + ]) + out = out.replace('CWD', tmpdir + os.path.sep) + self.assertEqual(self.pp.out.getvalue(), out) + finally: + shutil.rmtree(tmpdir) + + def test_literal(self): + self.do_include_pass([ + '#literal PASS', + ]) + + def test_var_directory(self): + self.do_include_pass([ + '#ifdef DIRECTORY', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_var_file(self): + self.do_include_pass([ + '#ifdef FILE', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_var_if_0(self): + self.do_include_pass([ + '#define VAR 0', + '#if VAR', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_var_if_0_elifdef(self): + self.do_include_pass([ + '#if 0', + '#elifdef FILE', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_var_if_0_elifndef(self): + self.do_include_pass([ + '#if 0', + '#elifndef VAR', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_var_ifdef_0(self): + self.do_include_pass([ + '#define VAR 0', + '#ifdef VAR', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_var_ifdef_1_or_undef(self): + self.do_include_pass([ + '#define FOO 1', + '#if defined(FOO) || defined(BAR)', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_var_ifdef_undef(self): + self.do_include_pass([ + '#define VAR 0', + '#undef VAR', + '#ifdef VAR', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_var_ifndef_0(self): + self.do_include_pass([ + '#define VAR 0', + '#ifndef VAR', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_var_ifndef_0_and_undef(self): + self.do_include_pass([ + '#define FOO 0', + '#if !defined(FOO) && !defined(BAR)', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_var_ifndef_undef(self): + self.do_include_pass([ + '#define VAR 0', + '#undef VAR', + '#ifndef VAR', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_var_line(self): + self.do_include_pass([ + '#ifdef LINE', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_filterDefine(self): + self.do_include_pass([ + '#filter substitution', + '#define VAR AS', + '#define VAR2 P@VAR@', + '@VAR2@S', + ]) + + def test_number_value_equals(self): + self.do_include_pass([ + '#define FOO 1000', + '#if FOO == 1000', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_default_defines(self): + self.pp.handleCommandLine(["-DFOO"]) + self.do_include_pass([ + '#if FOO == 1', + 'PASS', + '#else', + 'FAIL', + ]) + + def test_number_value_equals_defines(self): + self.pp.handleCommandLine(["-DFOO=1000"]) + self.do_include_pass([ + '#if FOO == 1000', + 'PASS', + '#else', + 'FAIL', + ]) + + def test_octal_value_equals(self): + self.do_include_pass([ + '#define FOO 0100', + '#if FOO == 0100', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_octal_value_equals_defines(self): + self.pp.handleCommandLine(["-DFOO=0100"]) + self.do_include_pass([ + '#if FOO == 0100', + 'PASS', + '#else', + 'FAIL', + '#endif', + ]) + + def test_value_quoted_expansion(self): + """ + Quoted values on the commandline don't currently have quotes stripped. + Pike says this is for compat reasons. + """ + self.pp.handleCommandLine(['-DFOO="ABCD"']) + self.do_include_compare([ + '#filter substitution', + '@FOO@', + ], ['"ABCD"']) + + def test_octal_value_quoted_expansion(self): + self.pp.handleCommandLine(['-DFOO="0100"']) + self.do_include_compare([ + '#filter substitution', + '@FOO@', + ], ['"0100"']) + + def test_number_value_not_equals_quoted_defines(self): + self.pp.handleCommandLine(['-DFOO="1000"']) + self.do_include_pass([ + '#if FOO == 1000', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_octal_value_not_equals_quoted_defines(self): + self.pp.handleCommandLine(['-DFOO="0100"']) + self.do_include_pass([ + '#if FOO == 0100', + 'FAIL', + '#else', + 'PASS', + '#endif', + ]) + + def test_undefined_variable(self): + with MockedOpen({'f': '#filter substitution\n@foo@'}): + with self.assertRaises(Preprocessor.Error) as e: + self.pp.do_include('f') + self.assertEqual(e.key, 'UNDEFINED_VAR') + + def test_include(self): + files = { + 'foo/test': '\n'.join([ + '#define foo foobarbaz', + '#include @inc@', + '@bar@', + '', + ]), + 'bar': '\n'.join([ + '#define bar barfoobaz', + '@foo@', + '', + ]), + 'f': '\n'.join([ + '#filter substitution', + '#define inc ../bar', + '#include foo/test', + '', + ]), + } + + with MockedOpen(files): + self.pp.do_include('f') + self.assertEqual(self.pp.out.getvalue(), 'foobarbaz\nbarfoobaz\n') + + def test_include_line(self): + files = { + 'test.js': '\n'.join([ + '#define foo foobarbaz', + '#include @inc@', + '@bar@', + '', + ]), + 'bar.js': '\n'.join([ + '#define bar barfoobaz', + '@foo@', + '', + ]), + 'foo.js': '\n'.join([ + 'bazfoobar', + '#include bar.js', + 'bazbarfoo', + '', + ]), + 'baz.js': 'baz\n', + 'f.js': '\n'.join([ + '#include foo.js', + '#filter substitution', + '#define inc bar.js', + '#include test.js', + '#include baz.js', + 'fin', + '', + ]), + } + + with MockedOpen(files): + self.pp.do_include('f.js') + self.assertEqual(self.pp.out.getvalue(), + ('//@line 1 "CWD/foo.js"\n' + 'bazfoobar\n' + '//@line 2 "CWD/bar.js"\n' + '@foo@\n' + '//@line 3 "CWD/foo.js"\n' + 'bazbarfoo\n' + '//@line 2 "CWD/bar.js"\n' + 'foobarbaz\n' + '//@line 3 "CWD/test.js"\n' + 'barfoobaz\n' + '//@line 1 "CWD/baz.js"\n' + 'baz\n' + '//@line 6 "CWD/f.js"\n' + 'fin\n').replace('CWD/', + os.getcwd() + os.path.sep)) + + def test_include_missing_file(self): + with MockedOpen({'f': '#include foo\n'}): + with self.assertRaises(Preprocessor.Error) as e: + self.pp.do_include('f') + self.assertEqual(e.exception.key, 'FILE_NOT_FOUND') + + def test_include_undefined_variable(self): + with MockedOpen({'f': '#filter substitution\n#include @foo@\n'}): + with self.assertRaises(Preprocessor.Error) as e: + self.pp.do_include('f') + self.assertEqual(e.exception.key, 'UNDEFINED_VAR') + + def test_include_literal_at(self): + files = { + '@foo@': '#define foo foobarbaz\n', + 'f': '#include @foo@\n#filter substitution\n@foo@\n', + } + + with MockedOpen(files): + self.pp.do_include('f') + self.assertEqual(self.pp.out.getvalue(), 'foobarbaz\n') + + def test_command_line_literal_at(self): + with MockedOpen({"@foo@.in": '@foo@\n'}): + self.pp.handleCommandLine(['-Fsubstitution', '-Dfoo=foobarbaz', '@foo@.in']) + self.assertEqual(self.pp.out.getvalue(), 'foobarbaz\n') + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_pythonutil.py b/python/mozbuild/mozbuild/test/test_pythonutil.py new file mode 100644 index 000000000..87399b3f5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_pythonutil.py @@ -0,0 +1,23 @@ +# 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 mozbuild.pythonutil import iter_modules_in_path +from mozunit import main +import os +import unittest + + +class TestIterModules(unittest.TestCase): + def test_iter_modules_in_path(self): + mozbuild_path = os.path.normcase(os.path.dirname(os.path.dirname(__file__))) + paths = list(iter_modules_in_path(mozbuild_path)) + self.assertEquals(sorted(paths), [ + os.path.join(os.path.abspath(mozbuild_path), '__init__.py'), + os.path.join(os.path.abspath(mozbuild_path), 'pythonutil.py'), + os.path.join(os.path.abspath(mozbuild_path), 'test', 'test_pythonutil.py'), + ]) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_testing.py b/python/mozbuild/mozbuild/test/test_testing.py new file mode 100644 index 000000000..e71892e24 --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_testing.py @@ -0,0 +1,332 @@ +# 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 cPickle as pickle +import os +import shutil +import tempfile +import unittest + +import mozpack.path as mozpath + +from mozfile import NamedTemporaryFile +from mozunit import main + +from mozbuild.base import MozbuildObject +from mozbuild.testing import ( + TestMetadata, + TestResolver, +) + + +ALL_TESTS = { + "accessible/tests/mochitest/actions/test_anchors.html": [ + { + "dir_relpath": "accessible/tests/mochitest/actions", + "expected": "pass", + "file_relpath": "accessible/tests/mochitest/actions/test_anchors.html", + "flavor": "a11y", + "here": "/Users/gps/src/firefox/accessible/tests/mochitest/actions", + "manifest": "/Users/gps/src/firefox/accessible/tests/mochitest/actions/a11y.ini", + "name": "test_anchors.html", + "path": "/Users/gps/src/firefox/accessible/tests/mochitest/actions/test_anchors.html", + "relpath": "test_anchors.html" + } + ], + "services/common/tests/unit/test_async_chain.js": [ + { + "dir_relpath": "services/common/tests/unit", + "file_relpath": "services/common/tests/unit/test_async_chain.js", + "firefox-appdir": "browser", + "flavor": "xpcshell", + "head": "head_global.js head_helpers.js head_http.js", + "here": "/Users/gps/src/firefox/services/common/tests/unit", + "manifest": "/Users/gps/src/firefox/services/common/tests/unit/xpcshell.ini", + "name": "test_async_chain.js", + "path": "/Users/gps/src/firefox/services/common/tests/unit/test_async_chain.js", + "relpath": "test_async_chain.js", + "tail": "" + } + ], + "services/common/tests/unit/test_async_querySpinningly.js": [ + { + "dir_relpath": "services/common/tests/unit", + "file_relpath": "services/common/tests/unit/test_async_querySpinningly.js", + "firefox-appdir": "browser", + "flavor": "xpcshell", + "head": "head_global.js head_helpers.js head_http.js", + "here": "/Users/gps/src/firefox/services/common/tests/unit", + "manifest": "/Users/gps/src/firefox/services/common/tests/unit/xpcshell.ini", + "name": "test_async_querySpinningly.js", + "path": "/Users/gps/src/firefox/services/common/tests/unit/test_async_querySpinningly.js", + "relpath": "test_async_querySpinningly.js", + "tail": "" + } + ], + "toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js": [ + { + "dir_relpath": "toolkit/mozapps/update/test/unit", + "file_relpath": "toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js", + "flavor": "xpcshell", + "generated-files": "head_update.js", + "head": "head_update.js", + "here": "/Users/gps/src/firefox/toolkit/mozapps/update/test/unit", + "manifest": "/Users/gps/src/firefox/toolkit/mozapps/update/test/unit/xpcshell_updater.ini", + "name": "test_0201_app_launch_apply_update.js", + "path": "/Users/gps/src/firefox/toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js", + "reason": "bug 820380", + "relpath": "test_0201_app_launch_apply_update.js", + "run-sequentially": "Launches application.", + "skip-if": "toolkit == 'gonk' || os == 'android'", + "tail": "" + }, + { + "dir_relpath": "toolkit/mozapps/update/test/unit", + "file_relpath": "toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js", + "flavor": "xpcshell", + "generated-files": "head_update.js", + "head": "head_update.js head2.js", + "here": "/Users/gps/src/firefox/toolkit/mozapps/update/test/unit", + "manifest": "/Users/gps/src/firefox/toolkit/mozapps/update/test/unit/xpcshell_updater.ini", + "name": "test_0201_app_launch_apply_update.js", + "path": "/Users/gps/src/firefox/toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js", + "reason": "bug 820380", + "relpath": "test_0201_app_launch_apply_update.js", + "run-sequentially": "Launches application.", + "skip-if": "toolkit == 'gonk' || os == 'android'", + "tail": "" + } + ], + "mobile/android/tests/background/junit3/src/common/TestAndroidLogWriters.java": [ + { + "dir_relpath": "mobile/android/tests/background/junit3/src/common", + "file_relpath": "mobile/android/tests/background/junit3/src/common/TestAndroidLogWriters.java", + "flavor": "instrumentation", + "here": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/background/junit3", + "manifest": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/background/junit3/instrumentation.ini", + "name": "src/common/TestAndroidLogWriters.java", + "path": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/background/junit3/src/common/TestAndroidLogWriters.java", + "relpath": "src/common/TestAndroidLogWriters.java", + "subsuite": "background" + } + ], + "mobile/android/tests/browser/junit3/src/TestDistribution.java": [ + { + "dir_relpath": "mobile/android/tests/browser/junit3/src", + "file_relpath": "mobile/android/tests/browser/junit3/src/TestDistribution.java", + "flavor": "instrumentation", + "here": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/browser/junit3", + "manifest": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/browser/junit3/instrumentation.ini", + "name": "src/TestDistribution.java", + "path": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/browser/junit3/src/TestDistribution.java", + "relpath": "src/TestDistribution.java", + "subsuite": "browser" + } + ], + "image/test/browser/browser_bug666317.js": [ + { + "dir_relpath": "image/test/browser", + "file_relpath": "image/test/browser/browser_bug666317.js", + "flavor": "browser-chrome", + "here": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/image/test/browser", + "manifest": "/home/chris/m-c/image/test/browser/browser.ini", + "name": "browser_bug666317.js", + "path": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/image/test/browser/browser_bug666317.js", + "relpath": "image/test/browser/browser_bug666317.js", + "skip-if": "e10s # Bug 948194 - Decoded Images seem to not be discarded on memory-pressure notification with e10s enabled", + "subsuite": "" + } + ], + "devtools/client/markupview/test/browser_markupview_copy_image_data.js": [ + { + "dir_relpath": "devtools/client/markupview/test", + "file_relpath": "devtools/client/markupview/test/browser_markupview_copy_image_data.js", + "flavor": "browser-chrome", + "here": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/devtools/client/markupview/test", + "manifest": "/home/chris/m-c/devtools/client/markupview/test/browser.ini", + "name": "browser_markupview_copy_image_data.js", + "path": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/devtools/client/markupview/test/browser_markupview_copy_image_data.js", + "relpath": "devtools/client/markupview/test/browser_markupview_copy_image_data.js", + "subsuite": "devtools", + "tags": "devtools" + } + ] +} + +TEST_DEFAULTS = { + "/Users/gps/src/firefox/toolkit/mozapps/update/test/unit/xpcshell_updater.ini": {"support-files": "\ndata/**\nxpcshell_updater.ini"} +} + + +class Base(unittest.TestCase): + def setUp(self): + self._temp_files = [] + + def tearDown(self): + for f in self._temp_files: + del f + + self._temp_files = [] + + def _get_test_metadata(self): + all_tests = NamedTemporaryFile(mode='wb') + pickle.dump(ALL_TESTS, all_tests) + all_tests.flush() + self._temp_files.append(all_tests) + + test_defaults = NamedTemporaryFile(mode='wb') + pickle.dump(TEST_DEFAULTS, test_defaults) + test_defaults.flush() + self._temp_files.append(test_defaults) + + return TestMetadata(all_tests.name, test_defaults=test_defaults.name) + + +class TestTestMetadata(Base): + def test_load(self): + t = self._get_test_metadata() + self.assertEqual(len(t._tests_by_path), 8) + + self.assertEqual(len(list(t.tests_with_flavor('xpcshell'))), 3) + self.assertEqual(len(list(t.tests_with_flavor('mochitest-plain'))), 0) + + def test_resolve_all(self): + t = self._get_test_metadata() + self.assertEqual(len(list(t.resolve_tests())), 9) + + def test_resolve_filter_flavor(self): + t = self._get_test_metadata() + self.assertEqual(len(list(t.resolve_tests(flavor='xpcshell'))), 4) + + def test_resolve_by_dir(self): + t = self._get_test_metadata() + self.assertEqual(len(list(t.resolve_tests(paths=['services/common']))), 2) + + def test_resolve_under_path(self): + t = self._get_test_metadata() + self.assertEqual(len(list(t.resolve_tests(under_path='services'))), 2) + + self.assertEqual(len(list(t.resolve_tests(flavor='xpcshell', + under_path='services'))), 2) + + def test_resolve_multiple_paths(self): + t = self._get_test_metadata() + result = list(t.resolve_tests(paths=['services', 'toolkit'])) + self.assertEqual(len(result), 4) + + def test_resolve_support_files(self): + expected_support_files = "\ndata/**\nxpcshell_updater.ini" + t = self._get_test_metadata() + result = list(t.resolve_tests(paths=['toolkit'])) + self.assertEqual(len(result), 2) + + for test in result: + self.assertEqual(test['support-files'], + expected_support_files) + + def test_resolve_path_prefix(self): + t = self._get_test_metadata() + result = list(t.resolve_tests(paths=['image'])) + self.assertEqual(len(result), 1) + + +class TestTestResolver(Base): + FAKE_TOPSRCDIR = '/Users/gps/src/firefox' + + def setUp(self): + Base.setUp(self) + + self._temp_dirs = [] + + def tearDown(self): + Base.tearDown(self) + + for d in self._temp_dirs: + shutil.rmtree(d) + + def _get_resolver(self): + topobjdir = tempfile.mkdtemp() + self._temp_dirs.append(topobjdir) + + with open(os.path.join(topobjdir, 'all-tests.pkl'), 'wb') as fh: + pickle.dump(ALL_TESTS, fh) + with open(os.path.join(topobjdir, 'test-defaults.pkl'), 'wb') as fh: + pickle.dump(TEST_DEFAULTS, fh) + + o = MozbuildObject(self.FAKE_TOPSRCDIR, None, None, topobjdir=topobjdir) + + # Monkey patch the test resolver to avoid tests failing to find make + # due to our fake topscrdir. + TestResolver._run_make = lambda *a, **b: None + + return o._spawn(TestResolver) + + def test_cwd_children_only(self): + """If cwd is defined, only resolve tests under the specified cwd.""" + r = self._get_resolver() + + # Pretend we're under '/services' and ask for 'common'. This should + # pick up all tests from '/services/common' + tests = list(r.resolve_tests(paths=['common'], cwd=os.path.join(r.topsrcdir, + 'services'))) + + self.assertEqual(len(tests), 2) + + # Tests should be rewritten to objdir. + for t in tests: + self.assertEqual(t['here'], mozpath.join(r.topobjdir, + '_tests/xpcshell/services/common/tests/unit')) + + def test_various_cwd(self): + """Test various cwd conditions are all equal.""" + + r = self._get_resolver() + + expected = list(r.resolve_tests(paths=['services'])) + actual = list(r.resolve_tests(paths=['services'], cwd='/')) + self.assertEqual(actual, expected) + + actual = list(r.resolve_tests(paths=['services'], cwd=r.topsrcdir)) + self.assertEqual(actual, expected) + + actual = list(r.resolve_tests(paths=['services'], cwd=r.topobjdir)) + self.assertEqual(actual, expected) + + def test_subsuites(self): + """Test filtering by subsuite.""" + + r = self._get_resolver() + + tests = list(r.resolve_tests(paths=['mobile'])) + self.assertEqual(len(tests), 2) + + tests = list(r.resolve_tests(paths=['mobile'], subsuite='browser')) + self.assertEqual(len(tests), 1) + self.assertEqual(tests[0]['name'], 'src/TestDistribution.java') + + tests = list(r.resolve_tests(paths=['mobile'], subsuite='background')) + self.assertEqual(len(tests), 1) + self.assertEqual(tests[0]['name'], 'src/common/TestAndroidLogWriters.java') + + def test_wildcard_patterns(self): + """Test matching paths by wildcard.""" + + r = self._get_resolver() + + tests = list(r.resolve_tests(paths=['mobile/**'])) + self.assertEqual(len(tests), 2) + for t in tests: + self.assertTrue(t['file_relpath'].startswith('mobile')) + + tests = list(r.resolve_tests(paths=['**/**.js', 'accessible/**'])) + self.assertEqual(len(tests), 7) + for t in tests: + path = t['file_relpath'] + self.assertTrue(path.startswith('accessible') or path.endswith('.js')) + + +if __name__ == '__main__': + main() diff --git a/python/mozbuild/mozbuild/test/test_util.py b/python/mozbuild/mozbuild/test/test_util.py new file mode 100644 index 000000000..6c3b39b1e --- /dev/null +++ b/python/mozbuild/mozbuild/test/test_util.py @@ -0,0 +1,924 @@ +# coding: utf-8 +# 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 itertools +import hashlib +import os +import unittest +import shutil +import string +import sys +import tempfile +import textwrap + +from mozfile.mozfile import NamedTemporaryFile +from mozunit import ( + main, + MockedOpen, +) + +from mozbuild.util import ( + expand_variables, + FileAvoidWrite, + group_unified_files, + hash_file, + indented_repr, + memoize, + memoized_property, + pair, + resolve_target_to_make, + MozbuildDeletionError, + HierarchicalStringList, + EnumString, + EnumStringComparisonError, + ListWithAction, + StrictOrderingOnAppendList, + StrictOrderingOnAppendListWithFlagsFactory, + TypedList, + TypedNamedTuple, + UnsortedError, +) + +if sys.version_info[0] == 3: + str_type = 'str' +else: + str_type = 'unicode' + +data_path = os.path.abspath(os.path.dirname(__file__)) +data_path = os.path.join(data_path, 'data') + + +class TestHashing(unittest.TestCase): + def test_hash_file_known_hash(self): + """Ensure a known hash value is recreated.""" + data = b'The quick brown fox jumps over the lazy cog' + expected = 'de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3' + + temp = NamedTemporaryFile() + temp.write(data) + temp.flush() + + actual = hash_file(temp.name) + + self.assertEqual(actual, expected) + + def test_hash_file_large(self): + """Ensure that hash_file seems to work with a large file.""" + data = b'x' * 1048576 + + hasher = hashlib.sha1() + hasher.update(data) + expected = hasher.hexdigest() + + temp = NamedTemporaryFile() + temp.write(data) + temp.flush() + + actual = hash_file(temp.name) + + self.assertEqual(actual, expected) + + +class TestFileAvoidWrite(unittest.TestCase): + def test_file_avoid_write(self): + with MockedOpen({'file': 'content'}): + # Overwriting an existing file replaces its content + faw = FileAvoidWrite('file') + faw.write('bazqux') + self.assertEqual(faw.close(), (True, True)) + self.assertEqual(open('file', 'r').read(), 'bazqux') + + # Creating a new file (obviously) stores its content + faw = FileAvoidWrite('file2') + faw.write('content') + self.assertEqual(faw.close(), (False, True)) + self.assertEqual(open('file2').read(), 'content') + + with MockedOpen({'file': 'content'}): + with FileAvoidWrite('file') as file: + file.write('foobar') + + self.assertEqual(open('file', 'r').read(), 'foobar') + + class MyMockedOpen(MockedOpen): + '''MockedOpen extension to raise an exception if something + attempts to write in an opened file. + ''' + def __call__(self, name, mode): + if 'w' in mode: + raise Exception, 'Unexpected open with write mode' + return MockedOpen.__call__(self, name, mode) + + with MyMockedOpen({'file': 'content'}): + # Validate that MyMockedOpen works as intended + file = FileAvoidWrite('file') + file.write('foobar') + self.assertRaises(Exception, file.close) + + # Check that no write actually happens when writing the + # same content as what already is in the file + faw = FileAvoidWrite('file') + faw.write('content') + self.assertEqual(faw.close(), (True, False)) + + def test_diff_not_default(self): + """Diffs are not produced by default.""" + + with MockedOpen({'file': 'old'}): + faw = FileAvoidWrite('file') + faw.write('dummy') + faw.close() + self.assertIsNone(faw.diff) + + def test_diff_update(self): + """Diffs are produced on file update.""" + + with MockedOpen({'file': 'old'}): + faw = FileAvoidWrite('file', capture_diff=True) + faw.write('new') + faw.close() + + diff = '\n'.join(faw.diff) + self.assertIn('-old', diff) + self.assertIn('+new', diff) + + def test_diff_create(self): + """Diffs are produced when files are created.""" + + tmpdir = tempfile.mkdtemp() + try: + path = os.path.join(tmpdir, 'file') + faw = FileAvoidWrite(path, capture_diff=True) + faw.write('new') + faw.close() + + diff = '\n'.join(faw.diff) + self.assertIn('+new', diff) + finally: + shutil.rmtree(tmpdir) + +class TestResolveTargetToMake(unittest.TestCase): + def setUp(self): + self.topobjdir = data_path + + def assertResolve(self, path, expected): + # Handle Windows path separators. + (reldir, target) = resolve_target_to_make(self.topobjdir, path) + if reldir is not None: + reldir = reldir.replace(os.sep, '/') + if target is not None: + target = target.replace(os.sep, '/') + self.assertEqual((reldir, target), expected) + + def test_root_path(self): + self.assertResolve('/test-dir', ('test-dir', None)) + self.assertResolve('/test-dir/with', ('test-dir/with', None)) + self.assertResolve('/test-dir/without', ('test-dir', None)) + self.assertResolve('/test-dir/without/with', ('test-dir/without/with', None)) + + def test_dir(self): + self.assertResolve('test-dir', ('test-dir', None)) + self.assertResolve('test-dir/with', ('test-dir/with', None)) + self.assertResolve('test-dir/with', ('test-dir/with', None)) + self.assertResolve('test-dir/without', ('test-dir', None)) + self.assertResolve('test-dir/without/with', ('test-dir/without/with', None)) + + def test_top_level(self): + self.assertResolve('package', (None, 'package')) + # Makefile handling shouldn't affect top-level targets. + self.assertResolve('Makefile', (None, 'Makefile')) + + def test_regular_file(self): + self.assertResolve('test-dir/with/file', ('test-dir/with', 'file')) + self.assertResolve('test-dir/with/without/file', ('test-dir/with', 'without/file')) + self.assertResolve('test-dir/with/without/with/file', ('test-dir/with/without/with', 'file')) + + self.assertResolve('test-dir/without/file', ('test-dir', 'without/file')) + self.assertResolve('test-dir/without/with/file', ('test-dir/without/with', 'file')) + self.assertResolve('test-dir/without/with/without/file', ('test-dir/without/with', 'without/file')) + + def test_Makefile(self): + self.assertResolve('test-dir/with/Makefile', ('test-dir', 'with/Makefile')) + self.assertResolve('test-dir/with/without/Makefile', ('test-dir/with', 'without/Makefile')) + self.assertResolve('test-dir/with/without/with/Makefile', ('test-dir/with', 'without/with/Makefile')) + + self.assertResolve('test-dir/without/Makefile', ('test-dir', 'without/Makefile')) + self.assertResolve('test-dir/without/with/Makefile', ('test-dir', 'without/with/Makefile')) + self.assertResolve('test-dir/without/with/without/Makefile', ('test-dir/without/with', 'without/Makefile')) + +class TestHierarchicalStringList(unittest.TestCase): + def setUp(self): + self.EXPORTS = HierarchicalStringList() + + def test_exports_append(self): + self.assertEqual(self.EXPORTS._strings, []) + self.EXPORTS += ["foo.h"] + self.assertEqual(self.EXPORTS._strings, ["foo.h"]) + self.EXPORTS += ["bar.h"] + self.assertEqual(self.EXPORTS._strings, ["foo.h", "bar.h"]) + + def test_exports_subdir(self): + self.assertEqual(self.EXPORTS._children, {}) + self.EXPORTS.foo += ["foo.h"] + self.assertItemsEqual(self.EXPORTS._children, {"foo" : True}) + self.assertEqual(self.EXPORTS.foo._strings, ["foo.h"]) + self.EXPORTS.bar += ["bar.h"] + self.assertItemsEqual(self.EXPORTS._children, + {"foo" : True, "bar" : True}) + self.assertEqual(self.EXPORTS.foo._strings, ["foo.h"]) + self.assertEqual(self.EXPORTS.bar._strings, ["bar.h"]) + + def test_exports_multiple_subdir(self): + self.EXPORTS.foo.bar = ["foobar.h"] + self.assertItemsEqual(self.EXPORTS._children, {"foo" : True}) + self.assertItemsEqual(self.EXPORTS.foo._children, {"bar" : True}) + self.assertItemsEqual(self.EXPORTS.foo.bar._children, {}) + self.assertEqual(self.EXPORTS._strings, []) + self.assertEqual(self.EXPORTS.foo._strings, []) + self.assertEqual(self.EXPORTS.foo.bar._strings, ["foobar.h"]) + + def test_invalid_exports_append(self): + with self.assertRaises(ValueError) as ve: + self.EXPORTS += "foo.h" + self.assertEqual(str(ve.exception), + "Expected a list of strings, not <type '%s'>" % str_type) + + def test_invalid_exports_set(self): + with self.assertRaises(ValueError) as ve: + self.EXPORTS.foo = "foo.h" + + self.assertEqual(str(ve.exception), + "Expected a list of strings, not <type '%s'>" % str_type) + + def test_invalid_exports_append_base(self): + with self.assertRaises(ValueError) as ve: + self.EXPORTS += "foo.h" + + self.assertEqual(str(ve.exception), + "Expected a list of strings, not <type '%s'>" % str_type) + + def test_invalid_exports_bool(self): + with self.assertRaises(ValueError) as ve: + self.EXPORTS += [True] + + self.assertEqual(str(ve.exception), + "Expected a list of strings, not an element of " + "<type 'bool'>") + + def test_del_exports(self): + with self.assertRaises(MozbuildDeletionError) as mde: + self.EXPORTS.foo += ['bar.h'] + del self.EXPORTS.foo + + def test_unsorted(self): + with self.assertRaises(UnsortedError) as ee: + self.EXPORTS += ['foo.h', 'bar.h'] + + with self.assertRaises(UnsortedError) as ee: + self.EXPORTS.foo = ['foo.h', 'bar.h'] + + with self.assertRaises(UnsortedError) as ee: + self.EXPORTS.foo += ['foo.h', 'bar.h'] + + def test_reassign(self): + self.EXPORTS.foo = ['foo.h'] + + with self.assertRaises(KeyError) as ee: + self.EXPORTS.foo = ['bar.h'] + + def test_walk(self): + l = HierarchicalStringList() + l += ['root1', 'root2', 'root3'] + l.child1 += ['child11', 'child12', 'child13'] + l.child1.grandchild1 += ['grandchild111', 'grandchild112'] + l.child1.grandchild2 += ['grandchild121', 'grandchild122'] + l.child2.grandchild1 += ['grandchild211', 'grandchild212'] + l.child2.grandchild1 += ['grandchild213', 'grandchild214'] + + els = list((path, list(seq)) for path, seq in l.walk()) + self.assertEqual(els, [ + ('', ['root1', 'root2', 'root3']), + ('child1', ['child11', 'child12', 'child13']), + ('child1/grandchild1', ['grandchild111', 'grandchild112']), + ('child1/grandchild2', ['grandchild121', 'grandchild122']), + ('child2/grandchild1', ['grandchild211', 'grandchild212', + 'grandchild213', 'grandchild214']), + ]) + + def test_merge(self): + l1 = HierarchicalStringList() + l1 += ['root1', 'root2', 'root3'] + l1.child1 += ['child11', 'child12', 'child13'] + l1.child1.grandchild1 += ['grandchild111', 'grandchild112'] + l1.child1.grandchild2 += ['grandchild121', 'grandchild122'] + l1.child2.grandchild1 += ['grandchild211', 'grandchild212'] + l1.child2.grandchild1 += ['grandchild213', 'grandchild214'] + l2 = HierarchicalStringList() + l2.child1 += ['child14', 'child15'] + l2.child1.grandchild2 += ['grandchild123'] + l2.child3 += ['child31', 'child32'] + + l1 += l2 + els = list((path, list(seq)) for path, seq in l1.walk()) + self.assertEqual(els, [ + ('', ['root1', 'root2', 'root3']), + ('child1', ['child11', 'child12', 'child13', 'child14', + 'child15']), + ('child1/grandchild1', ['grandchild111', 'grandchild112']), + ('child1/grandchild2', ['grandchild121', 'grandchild122', + 'grandchild123']), + ('child2/grandchild1', ['grandchild211', 'grandchild212', + 'grandchild213', 'grandchild214']), + ('child3', ['child31', 'child32']), + ]) + + +class TestStrictOrderingOnAppendList(unittest.TestCase): + def test_init(self): + l = StrictOrderingOnAppendList() + self.assertEqual(len(l), 0) + + l = StrictOrderingOnAppendList(['a', 'b', 'c']) + self.assertEqual(len(l), 3) + + with self.assertRaises(UnsortedError): + StrictOrderingOnAppendList(['c', 'b', 'a']) + + self.assertEqual(len(l), 3) + + def test_extend(self): + l = StrictOrderingOnAppendList() + l.extend(['a', 'b']) + self.assertEqual(len(l), 2) + self.assertIsInstance(l, StrictOrderingOnAppendList) + + with self.assertRaises(UnsortedError): + l.extend(['d', 'c']) + + self.assertEqual(len(l), 2) + + def test_slicing(self): + l = StrictOrderingOnAppendList() + l[:] = ['a', 'b'] + self.assertEqual(len(l), 2) + self.assertIsInstance(l, StrictOrderingOnAppendList) + + with self.assertRaises(UnsortedError): + l[:] = ['b', 'a'] + + self.assertEqual(len(l), 2) + + def test_add(self): + l = StrictOrderingOnAppendList() + l2 = l + ['a', 'b'] + self.assertEqual(len(l), 0) + self.assertEqual(len(l2), 2) + self.assertIsInstance(l2, StrictOrderingOnAppendList) + + with self.assertRaises(UnsortedError): + l2 = l + ['b', 'a'] + + self.assertEqual(len(l), 0) + + def test_iadd(self): + l = StrictOrderingOnAppendList() + l += ['a', 'b'] + self.assertEqual(len(l), 2) + self.assertIsInstance(l, StrictOrderingOnAppendList) + + with self.assertRaises(UnsortedError): + l += ['b', 'a'] + + self.assertEqual(len(l), 2) + + def test_add_after_iadd(self): + l = StrictOrderingOnAppendList(['b']) + l += ['a'] + l2 = l + ['c', 'd'] + self.assertEqual(len(l), 2) + self.assertEqual(len(l2), 4) + self.assertIsInstance(l2, StrictOrderingOnAppendList) + with self.assertRaises(UnsortedError): + l2 = l + ['d', 'c'] + + self.assertEqual(len(l), 2) + + def test_add_StrictOrderingOnAppendList(self): + l = StrictOrderingOnAppendList() + l += ['c', 'd'] + l += ['a', 'b'] + l2 = StrictOrderingOnAppendList() + with self.assertRaises(UnsortedError): + l2 += list(l) + # Adding a StrictOrderingOnAppendList to another shouldn't throw + l2 += l + + +class TestListWithAction(unittest.TestCase): + def setUp(self): + self.action = lambda a: (a, id(a)) + + def assertSameList(self, expected, actual): + self.assertEqual(len(expected), len(actual)) + for idx, item in enumerate(actual): + self.assertEqual(item, expected[idx]) + + def test_init(self): + l = ListWithAction(action=self.action) + self.assertEqual(len(l), 0) + original = ['a', 'b', 'c'] + l = ListWithAction(['a', 'b', 'c'], action=self.action) + expected = map(self.action, original) + self.assertSameList(expected, l) + + with self.assertRaises(ValueError): + ListWithAction('abc', action=self.action) + + with self.assertRaises(ValueError): + ListWithAction() + + def test_extend(self): + l = ListWithAction(action=self.action) + original = ['a', 'b'] + l.extend(original) + expected = map(self.action, original) + self.assertSameList(expected, l) + + with self.assertRaises(ValueError): + l.extend('ab') + + def test_slicing(self): + l = ListWithAction(action=self.action) + original = ['a', 'b'] + l[:] = original + expected = map(self.action, original) + self.assertSameList(expected, l) + + with self.assertRaises(ValueError): + l[:] = 'ab' + + def test_add(self): + l = ListWithAction(action=self.action) + original = ['a', 'b'] + l2 = l + original + expected = map(self.action, original) + self.assertSameList(expected, l2) + + with self.assertRaises(ValueError): + l + 'abc' + + def test_iadd(self): + l = ListWithAction(action=self.action) + original = ['a', 'b'] + l += original + expected = map(self.action, original) + self.assertSameList(expected, l) + + with self.assertRaises(ValueError): + l += 'abc' + + +class TestStrictOrderingOnAppendListWithFlagsFactory(unittest.TestCase): + def test_strict_ordering_on_append_list_with_flags_factory(self): + cls = StrictOrderingOnAppendListWithFlagsFactory({ + 'foo': bool, + 'bar': int, + }) + + l = cls() + l += ['a', 'b'] + + with self.assertRaises(Exception): + l['a'] = 'foo' + + with self.assertRaises(Exception): + c = l['c'] + + self.assertEqual(l['a'].foo, False) + l['a'].foo = True + self.assertEqual(l['a'].foo, True) + + with self.assertRaises(TypeError): + l['a'].bar = 'bar' + + self.assertEqual(l['a'].bar, 0) + l['a'].bar = 42 + self.assertEqual(l['a'].bar, 42) + + l['b'].foo = True + self.assertEqual(l['b'].foo, True) + + with self.assertRaises(AttributeError): + l['b'].baz = False + + l['b'].update(foo=False, bar=12) + self.assertEqual(l['b'].foo, False) + self.assertEqual(l['b'].bar, 12) + + with self.assertRaises(AttributeError): + l['b'].update(xyz=1) + + def test_strict_ordering_on_append_list_with_flags_factory_extend(self): + FooList = StrictOrderingOnAppendListWithFlagsFactory({ + 'foo': bool, 'bar': unicode + }) + foo = FooList(['a', 'b', 'c']) + foo['a'].foo = True + foo['b'].bar = 'bar' + + # Don't allow extending lists with different flag definitions. + BarList = StrictOrderingOnAppendListWithFlagsFactory({ + 'foo': unicode, 'baz': bool + }) + bar = BarList(['d', 'e', 'f']) + bar['d'].foo = 'foo' + bar['e'].baz = True + with self.assertRaises(ValueError): + foo + bar + with self.assertRaises(ValueError): + bar + foo + + # It's not obvious what to do with duplicate list items with possibly + # different flag values, so don't allow that case. + with self.assertRaises(ValueError): + foo + foo + + def assertExtended(l): + self.assertEqual(len(l), 6) + self.assertEqual(l['a'].foo, True) + self.assertEqual(l['b'].bar, 'bar') + self.assertTrue('c' in l) + self.assertEqual(l['d'].foo, True) + self.assertEqual(l['e'].bar, 'bar') + self.assertTrue('f' in l) + + # Test extend. + zot = FooList(['d', 'e', 'f']) + zot['d'].foo = True + zot['e'].bar = 'bar' + zot.extend(foo) + assertExtended(zot) + + # Test __add__. + zot = FooList(['d', 'e', 'f']) + zot['d'].foo = True + zot['e'].bar = 'bar' + assertExtended(foo + zot) + assertExtended(zot + foo) + + # Test __iadd__. + foo += zot + assertExtended(foo) + + # Test __setslice__. + foo[3:] = [] + self.assertEqual(len(foo), 3) + foo[3:] = zot + assertExtended(foo) + + +class TestMemoize(unittest.TestCase): + def test_memoize(self): + self._count = 0 + @memoize + def wrapped(a, b): + self._count += 1 + return a + b + + self.assertEqual(self._count, 0) + self.assertEqual(wrapped(1, 1), 2) + self.assertEqual(self._count, 1) + self.assertEqual(wrapped(1, 1), 2) + self.assertEqual(self._count, 1) + self.assertEqual(wrapped(2, 1), 3) + self.assertEqual(self._count, 2) + self.assertEqual(wrapped(1, 2), 3) + self.assertEqual(self._count, 3) + self.assertEqual(wrapped(1, 2), 3) + self.assertEqual(self._count, 3) + self.assertEqual(wrapped(1, 1), 2) + self.assertEqual(self._count, 3) + + def test_memoize_method(self): + class foo(object): + def __init__(self): + self._count = 0 + + @memoize + def wrapped(self, a, b): + self._count += 1 + return a + b + + instance = foo() + refcount = sys.getrefcount(instance) + self.assertEqual(instance._count, 0) + self.assertEqual(instance.wrapped(1, 1), 2) + self.assertEqual(instance._count, 1) + self.assertEqual(instance.wrapped(1, 1), 2) + self.assertEqual(instance._count, 1) + self.assertEqual(instance.wrapped(2, 1), 3) + self.assertEqual(instance._count, 2) + self.assertEqual(instance.wrapped(1, 2), 3) + self.assertEqual(instance._count, 3) + self.assertEqual(instance.wrapped(1, 2), 3) + self.assertEqual(instance._count, 3) + self.assertEqual(instance.wrapped(1, 1), 2) + self.assertEqual(instance._count, 3) + + # Memoization of methods is expected to not keep references to + # instances, so the refcount shouldn't have changed after executing the + # memoized method. + self.assertEqual(refcount, sys.getrefcount(instance)) + + def test_memoized_property(self): + class foo(object): + def __init__(self): + self._count = 0 + + @memoized_property + def wrapped(self): + self._count += 1 + return 42 + + instance = foo() + self.assertEqual(instance._count, 0) + self.assertEqual(instance.wrapped, 42) + self.assertEqual(instance._count, 1) + self.assertEqual(instance.wrapped, 42) + self.assertEqual(instance._count, 1) + + +class TestTypedList(unittest.TestCase): + def test_init(self): + cls = TypedList(int) + l = cls() + self.assertEqual(len(l), 0) + + l = cls([1, 2, 3]) + self.assertEqual(len(l), 3) + + with self.assertRaises(ValueError): + cls([1, 2, 'c']) + + def test_extend(self): + cls = TypedList(int) + l = cls() + l.extend([1, 2]) + self.assertEqual(len(l), 2) + self.assertIsInstance(l, cls) + + with self.assertRaises(ValueError): + l.extend([3, 'c']) + + self.assertEqual(len(l), 2) + + def test_slicing(self): + cls = TypedList(int) + l = cls() + l[:] = [1, 2] + self.assertEqual(len(l), 2) + self.assertIsInstance(l, cls) + + with self.assertRaises(ValueError): + l[:] = [3, 'c'] + + self.assertEqual(len(l), 2) + + def test_add(self): + cls = TypedList(int) + l = cls() + l2 = l + [1, 2] + self.assertEqual(len(l), 0) + self.assertEqual(len(l2), 2) + self.assertIsInstance(l2, cls) + + with self.assertRaises(ValueError): + l2 = l + [3, 'c'] + + self.assertEqual(len(l), 0) + + def test_iadd(self): + cls = TypedList(int) + l = cls() + l += [1, 2] + self.assertEqual(len(l), 2) + self.assertIsInstance(l, cls) + + with self.assertRaises(ValueError): + l += [3, 'c'] + + self.assertEqual(len(l), 2) + + def test_add_coercion(self): + objs = [] + + class Foo(object): + def __init__(self, obj): + objs.append(obj) + + cls = TypedList(Foo) + l = cls() + l += [1, 2] + self.assertEqual(len(objs), 2) + self.assertEqual(type(l[0]), Foo) + self.assertEqual(type(l[1]), Foo) + + # Adding a TypedList to a TypedList shouldn't trigger coercion again + l2 = cls() + l2 += l + self.assertEqual(len(objs), 2) + self.assertEqual(type(l2[0]), Foo) + self.assertEqual(type(l2[1]), Foo) + + # Adding a TypedList to a TypedList shouldn't even trigger the code + # that does coercion at all. + l2 = cls() + list.__setslice__(l, 0, -1, [1, 2]) + l2 += l + self.assertEqual(len(objs), 2) + self.assertEqual(type(l2[0]), int) + self.assertEqual(type(l2[1]), int) + + def test_memoized(self): + cls = TypedList(int) + cls2 = TypedList(str) + self.assertEqual(TypedList(int), cls) + self.assertNotEqual(cls, cls2) + + +class TypedTestStrictOrderingOnAppendList(unittest.TestCase): + def test_init(self): + class Unicode(unicode): + def __init__(self, other): + if not isinstance(other, unicode): + raise ValueError() + super(Unicode, self).__init__(other) + + cls = TypedList(Unicode, StrictOrderingOnAppendList) + l = cls() + self.assertEqual(len(l), 0) + + l = cls(['a', 'b', 'c']) + self.assertEqual(len(l), 3) + + with self.assertRaises(UnsortedError): + cls(['c', 'b', 'a']) + + with self.assertRaises(ValueError): + cls(['a', 'b', 3]) + + self.assertEqual(len(l), 3) + + +class TestTypedNamedTuple(unittest.TestCase): + def test_simple(self): + FooBar = TypedNamedTuple('FooBar', [('foo', unicode), ('bar', int)]) + + t = FooBar(foo='foo', bar=2) + self.assertEquals(type(t), FooBar) + self.assertEquals(t.foo, 'foo') + self.assertEquals(t.bar, 2) + self.assertEquals(t[0], 'foo') + self.assertEquals(t[1], 2) + + FooBar('foo', 2) + + with self.assertRaises(TypeError): + FooBar('foo', 'not integer') + with self.assertRaises(TypeError): + FooBar(2, 4) + + # Passing a tuple as the first argument is the same as passing multiple + # arguments. + t1 = ('foo', 3) + t2 = FooBar(t1) + self.assertEquals(type(t2), FooBar) + self.assertEqual(FooBar(t1), FooBar('foo', 3)) + + +class TestGroupUnifiedFiles(unittest.TestCase): + FILES = ['%s.cpp' % letter for letter in string.ascii_lowercase] + + def test_multiple_files(self): + mapping = list(group_unified_files(self.FILES, 'Unified', 'cpp', 5)) + + def check_mapping(index, expected_num_source_files): + (unified_file, source_files) = mapping[index] + + self.assertEqual(unified_file, 'Unified%d.cpp' % index) + self.assertEqual(len(source_files), expected_num_source_files) + + all_files = list(itertools.chain(*[files for (_, files) in mapping])) + self.assertEqual(len(all_files), len(self.FILES)) + self.assertEqual(set(all_files), set(self.FILES)) + + expected_amounts = [5, 5, 5, 5, 5, 1] + for i, amount in enumerate(expected_amounts): + check_mapping(i, amount) + + def test_unsorted_files(self): + unsorted_files = ['a%d.cpp' % i for i in range(11)] + sorted_files = sorted(unsorted_files) + mapping = list(group_unified_files(unsorted_files, 'Unified', 'cpp', 5)) + + self.assertEqual(mapping[0][1], sorted_files[0:5]) + self.assertEqual(mapping[1][1], sorted_files[5:10]) + self.assertEqual(mapping[2][1], sorted_files[10:]) + + +class TestMisc(unittest.TestCase): + def test_pair(self): + self.assertEqual( + list(pair([1, 2, 3, 4, 5, 6])), + [(1, 2), (3, 4), (5, 6)] + ) + + self.assertEqual( + list(pair([1, 2, 3, 4, 5, 6, 7])), + [(1, 2), (3, 4), (5, 6), (7, None)] + ) + + def test_expand_variables(self): + self.assertEqual( + expand_variables('$(var)', {'var': 'value'}), + 'value' + ) + + self.assertEqual( + expand_variables('$(a) and $(b)', {'a': '1', 'b': '2'}), + '1 and 2' + ) + + self.assertEqual( + expand_variables('$(a) and $(undefined)', {'a': '1', 'b': '2'}), + '1 and ' + ) + + self.assertEqual( + expand_variables('before $(string) between $(list) after', { + 'string': 'abc', + 'list': ['a', 'b', 'c'] + }), + 'before abc between a b c after' + ) + +class TestEnumString(unittest.TestCase): + def test_string(self): + CompilerType = EnumString.subclass('msvc', 'gcc', 'clang', 'clang-cl') + + type = CompilerType('msvc') + self.assertEquals(type, 'msvc') + self.assertNotEquals(type, 'gcc') + self.assertNotEquals(type, 'clang') + self.assertNotEquals(type, 'clang-cl') + self.assertIn(type, ('msvc', 'clang-cl')) + self.assertNotIn(type, ('gcc', 'clang')) + + with self.assertRaises(EnumStringComparisonError): + self.assertEquals(type, 'foo') + + with self.assertRaises(EnumStringComparisonError): + self.assertNotEquals(type, 'foo') + + with self.assertRaises(EnumStringComparisonError): + self.assertIn(type, ('foo', 'gcc')) + + with self.assertRaises(ValueError): + type = CompilerType('foo') + + +class TestIndentedRepr(unittest.TestCase): + def test_indented_repr(self): + data = textwrap.dedent(r''' + { + 'a': 1, + 'b': b'abc', + b'c': 'xyz', + 'd': False, + 'e': { + 'a': 1, + 'b': b'2', + 'c': '3', + }, + 'f': [ + 1, + b'2', + '3', + ], + 'pile_of_bytes': b'\xf0\x9f\x92\xa9', + 'pile_of_poo': '💩', + 'special_chars': '\\\'"\x08\n\t', + 'with_accents': 'éà ñ', + }''').lstrip() + + obj = eval(data) + + self.assertEqual(indented_repr(obj), data) + + +if __name__ == '__main__': + main() |