summaryrefslogtreecommitdiffstats
path: root/python/mozbuild/mozbuild/test/configure/test_lint.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/mozbuild/mozbuild/test/configure/test_lint.py')
-rw-r--r--python/mozbuild/mozbuild/test/configure/test_lint.py487
1 files changed, 487 insertions, 0 deletions
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 0000000000..7ecac769c3
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/configure/test_lint.py
@@ -0,0 +1,487 @@
+# 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 contextlib
+import os
+import sys
+import textwrap
+import traceback
+import unittest
+
+import mozpack.path as mozpath
+from mozunit import MockedOpen, main
+
+from mozbuild.configure import ConfigureError
+from mozbuild.configure.lint import LintSandbox
+
+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)}
+ )
+
+ @contextlib.contextmanager
+ def assertRaisesFromLine(self, exc_type, line):
+ with self.assertRaises(exc_type) as e:
+ yield e
+
+ _, _, tb = sys.exc_info()
+ self.assertEqual(
+ traceback.extract_tb(tb)[-1][:2],
+ (mozpath.join(test_data_path, "moz.configure"), line),
+ )
+
+ def test_configure_testcase(self):
+ # Lint python/mozbuild/mozbuild/test/configure/data/moz.configure
+ self.lint_test()
+
+ def test_depends_failures(self):
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ def foo(value):
+ return value
+
+ @depends('--help', foo)
+ @imports('os')
+ def bar(help, foo):
+ return foo
+ """
+ ):
+ self.lint_test()
+
+ with self.assertRaisesFromLine(ConfigureError, 7) as e:
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ def foo(value):
+ return value
+
+ @depends('--help', foo)
+ def bar(help, foo):
+ return foo
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "The dependency on `--help` is unused")
+
+ with self.assertRaisesFromLine(ConfigureError, 3) as e:
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ @imports('os')
+ def foo(value):
+ return value
+
+ @depends('--help', foo)
+ @imports('os')
+ def bar(help, foo):
+ return foo
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(
+ str(e.exception),
+ "Missing '--help' dependency because `bar` depends on '--help' and `foo`",
+ )
+
+ with self.assertRaisesFromLine(ConfigureError, 7) 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)
+ @imports('os')
+ def bar(help, foo):
+ return foo
+ tmpl()
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(
+ str(e.exception),
+ "Missing '--help' dependency because `bar` depends on '--help' and `foo`",
+ )
+
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ def foo(value):
+ return value
+
+ include(foo)
+ """
+ ):
+ self.lint_test()
+
+ with self.assertRaisesFromLine(ConfigureError, 3) 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.assertEqual(str(e.exception), "Missing '--help' dependency")
+
+ with self.assertRaisesFromLine(ConfigureError, 3) as e:
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ @imports('os')
+ def foo(value):
+ return value
+
+ @depends(foo)
+ def bar(value):
+ return value
+
+ include(bar)
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "Missing '--help' dependency")
+
+ with self.assertRaisesFromLine(ConfigureError, 3) as e:
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ @imports('os')
+ def foo(value):
+ return value
+
+ option('--bar', help='bar', when=foo)
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "Missing '--help' dependency")
+
+ # This would have failed with "Missing '--help' dependency"
+ # in the past, because of the reference to the builtin False.
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ def foo(value):
+ return False or value
+
+ option('--bar', help='bar', when=foo)
+ """
+ ):
+ self.lint_test()
+
+ # However, when something that is normally a builtin is overridden,
+ # we should still want the dependency on --help.
+ with self.assertRaisesFromLine(ConfigureError, 7) as e:
+ with self.moz_configure(
+ """
+ @template
+ def tmpl():
+ sorted = 42
+
+ option('--foo', help='foo')
+ @depends('--foo')
+ def foo(value):
+ return sorted
+
+ option('--bar', help='bar', when=foo)
+ tmpl()
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "Missing '--help' dependency")
+
+ # 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()
+
+ with self.assertRaisesFromLine(ConfigureError, 3) as e:
+ with self.moz_configure(
+ """
+ option('--foo', help='foo')
+ @depends('--foo')
+ def foo(value):
+ return
+
+ include(foo)
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "The dependency on `--foo` is unused")
+
+ with self.assertRaisesFromLine(ConfigureError, 5) as e:
+ with self.moz_configure(
+ """
+ @depends(when=True)
+ def bar():
+ return
+ @depends(bar)
+ def foo(value):
+ return
+
+ include(foo)
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "The dependency on `bar` is unused")
+
+ with self.assertRaisesFromLine(ConfigureError, 2) as e:
+ with self.moz_configure(
+ """
+ @depends(depends(when=True)(lambda: None))
+ def foo(value):
+ return
+
+ include(foo)
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "The dependency on `<lambda>` is unused")
+
+ with self.assertRaisesFromLine(ConfigureError, 9) as e:
+ with self.moz_configure(
+ """
+ @template
+ def tmpl():
+ @depends(when=True)
+ def bar():
+ return
+ return bar
+ qux = tmpl()
+ @depends(qux)
+ def foo(value):
+ return
+
+ include(foo)
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "The dependency on `qux` is unused")
+
+ def test_default_enable(self):
+ # --enable-* with default=True is not allowed.
+ with self.moz_configure(
+ """
+ option('--enable-foo', default=False, help='foo')
+ """
+ ):
+ self.lint_test()
+ with self.assertRaisesFromLine(ConfigureError, 2) as e:
+ with self.moz_configure(
+ """
+ option('--enable-foo', default=True, help='foo')
+ """
+ ):
+ self.lint_test()
+ self.assertEqual(
+ str(e.exception),
+ "--disable-foo should be used instead of " "--enable-foo with default=True",
+ )
+
+ def test_default_disable(self):
+ # --disable-* with default=False is not allowed.
+ with self.moz_configure(
+ """
+ option('--disable-foo', default=True, help='foo')
+ """
+ ):
+ self.lint_test()
+ with self.assertRaisesFromLine(ConfigureError, 2) as e:
+ with self.moz_configure(
+ """
+ option('--disable-foo', default=False, help='foo')
+ """
+ ):
+ self.lint_test()
+ self.assertEqual(
+ str(e.exception),
+ "--enable-foo should be used instead of "
+ "--disable-foo with default=False",
+ )
+
+ def test_default_with(self):
+ # --with-* with default=True is not allowed.
+ with self.moz_configure(
+ """
+ option('--with-foo', default=False, help='foo')
+ """
+ ):
+ self.lint_test()
+ with self.assertRaisesFromLine(ConfigureError, 2) as e:
+ with self.moz_configure(
+ """
+ option('--with-foo', default=True, help='foo')
+ """
+ ):
+ self.lint_test()
+ self.assertEqual(
+ str(e.exception),
+ "--without-foo should be used instead of " "--with-foo with default=True",
+ )
+
+ def test_default_without(self):
+ # --without-* with default=False is not allowed.
+ with self.moz_configure(
+ """
+ option('--without-foo', default=True, help='foo')
+ """
+ ):
+ self.lint_test()
+ with self.assertRaisesFromLine(ConfigureError, 2) as e:
+ with self.moz_configure(
+ """
+ option('--without-foo', default=False, help='foo')
+ """
+ ):
+ self.lint_test()
+ self.assertEqual(
+ str(e.exception),
+ "--with-foo should be used instead of " "--without-foo with default=False",
+ )
+
+ def test_default_func(self):
+ # Help text for an option with variable default should contain
+ # {enable|disable} rule.
+ with self.moz_configure(
+ """
+ option(env='FOO', help='foo')
+ option('--enable-bar', default=depends('FOO')(lambda x: bool(x)),
+ help='{Enable|Disable} bar')
+ """
+ ):
+ self.lint_test()
+ with self.assertRaisesFromLine(ConfigureError, 3) as e:
+ with self.moz_configure(
+ """
+ option(env='FOO', help='foo')
+ option('--enable-bar', default=depends('FOO')(lambda x: bool(x)),\
+ help='Enable bar')
+ """
+ ):
+ self.lint_test()
+ self.assertEqual(
+ str(e.exception),
+ '`help` should contain "{Enable|Disable}" because of '
+ "non-constant default",
+ )
+
+ def test_large_offset(self):
+ with self.assertRaisesFromLine(ConfigureError, 375):
+ with self.moz_configure(
+ """
+ option(env='FOO', help='foo')
+ """
+ + "\n" * 371
+ + """
+ option('--enable-bar', default=depends('FOO')(lambda x: bool(x)),\
+ help='Enable bar')
+ """
+ ):
+ self.lint_test()
+
+ def test_undefined_global(self):
+ with self.assertRaisesFromLine(NameError, 6) as e:
+ with self.moz_configure(
+ """
+ option(env='FOO', help='foo')
+ @depends('FOO')
+ def foo(value):
+ if value:
+ return unknown
+ return value
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "global name 'unknown' is not defined")
+
+ # Ideally, this would raise on line 4, where `unknown` is used, but
+ # python disassembly doesn't give use the information.
+ with self.assertRaisesFromLine(NameError, 2) as e:
+ with self.moz_configure(
+ """
+ @template
+ def tmpl():
+ @depends(unknown)
+ def foo(value):
+ if value:
+ return True
+ return foo
+ tmpl()
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "global name 'unknown' is not defined")
+
+ def test_unnecessary_imports(self):
+ with self.assertRaisesFromLine(NameError, 3) as e:
+ with self.moz_configure(
+ """
+ option(env='FOO', help='foo')
+ @depends('FOO')
+ @imports(_from='__builtin__', _import='list')
+ def foo(value):
+ if value:
+ return list()
+ return value
+ """
+ ):
+ self.lint_test()
+
+ self.assertEqual(str(e.exception), "builtin 'list' doesn't need to be imported")
+
+
+if __name__ == "__main__":
+ main()