tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_checks_configure.py (41995B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this
      3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 import os
      6 import sys
      7 import textwrap
      8 import unittest
      9 from io import StringIO
     10 
     11 from buildconfig import topsrcdir
     12 from mozpack import path as mozpath
     13 from mozshellutil import quote as shell_quote
     14 from mozunit import MockedOpen, main
     15 
     16 from common import ConfigureTestSandbox, ensure_exe_extension, fake_short_path
     17 from mozbuild.configure import ConfigureError, ConfigureSandbox
     18 
     19 
     20 class TestChecksConfigure(unittest.TestCase):
     21    def test_checking(self):
     22        def make_test(to_exec):
     23            def test(val, msg):
     24                out = StringIO()
     25                sandbox = ConfigureSandbox({}, stdout=out, stderr=out)
     26                base_dir = os.path.join(topsrcdir, "build", "moz.configure")
     27                sandbox.include_file(os.path.join(base_dir, "checks.configure"))
     28                exec(to_exec, sandbox)
     29                sandbox["foo"](val)
     30                self.assertEqual(out.getvalue(), msg)
     31 
     32            return test
     33 
     34        test = make_test(
     35            textwrap.dedent(
     36                """
     37            @checking('for a thing')
     38            def foo(value):
     39                return value
     40        """
     41            )
     42        )
     43        test(True, "checking for a thing... yes\n")
     44        test(False, "checking for a thing... no\n")
     45        test(42, "checking for a thing... 42\n")
     46        test("foo", "checking for a thing... foo\n")
     47        data = ["foo", "bar"]
     48        test(data, "checking for a thing... %r\n" % data)
     49 
     50        # When the function given to checking does nothing interesting, the
     51        # behavior is not altered
     52        test = make_test(
     53            textwrap.dedent(
     54                """
     55            @checking('for a thing', lambda x: x)
     56            def foo(value):
     57                return value
     58        """
     59            )
     60        )
     61        test(True, "checking for a thing... yes\n")
     62        test(False, "checking for a thing... no\n")
     63        test(42, "checking for a thing... 42\n")
     64        test("foo", "checking for a thing... foo\n")
     65        data = ["foo", "bar"]
     66        test(data, "checking for a thing... %r\n" % data)
     67 
     68        test = make_test(
     69            textwrap.dedent(
     70                """
     71            def munge(x):
     72                if not x:
     73                    return 'not found'
     74                if isinstance(x, (str, bool, int)):
     75                    return x
     76                return ' '.join(x)
     77 
     78            @checking('for a thing', munge)
     79            def foo(value):
     80                return value
     81        """
     82            )
     83        )
     84        test(True, "checking for a thing... yes\n")
     85        test(False, "checking for a thing... not found\n")
     86        test(42, "checking for a thing... 42\n")
     87        test("foo", "checking for a thing... foo\n")
     88        data = ["foo", "bar"]
     89        test(data, "checking for a thing... foo bar\n")
     90 
     91    KNOWN_A = ensure_exe_extension(mozpath.abspath("/usr/bin/known-a"))
     92    KNOWN_B = ensure_exe_extension(mozpath.abspath("/usr/local/bin/known-b"))
     93    KNOWN_C = ensure_exe_extension(mozpath.abspath("/home/user/bin/known c"))
     94    OTHER_A = ensure_exe_extension(mozpath.abspath("/lib/other/known-a"))
     95 
     96    def get_result(
     97        self,
     98        command="",
     99        args=[],
    100        environ={},
    101        prog="/bin/configure",
    102        extra_paths=None,
    103        includes=("util.configure", "checks.configure"),
    104    ):
    105        config = {}
    106        out = StringIO()
    107        paths = {self.KNOWN_A: None, self.KNOWN_B: None, self.KNOWN_C: None}
    108        if extra_paths:
    109            paths.update(extra_paths)
    110        environ = dict(environ)
    111        if "PATH" not in environ:
    112            environ["PATH"] = os.pathsep.join(os.path.dirname(p) for p in paths)
    113        paths[self.OTHER_A] = None
    114        sandbox = ConfigureTestSandbox(paths, config, environ, [prog] + args, out, out)
    115        base_dir = os.path.join(topsrcdir, "build", "moz.configure")
    116        for f in includes:
    117            sandbox.include_file(os.path.join(base_dir, f))
    118 
    119        status = 0
    120        try:
    121            exec(command, sandbox)
    122            sandbox.run()
    123        except SystemExit as e:
    124            status = e.code
    125 
    126        return config, out.getvalue(), status
    127 
    128    def test_check_prog(self):
    129        config, out, status = self.get_result('check_prog("FOO", ("known-a",))')
    130        self.assertEqual(status, 0)
    131        self.assertEqual(config, {"FOO": self.KNOWN_A})
    132        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)
    133 
    134        config, out, status = self.get_result(
    135            'check_prog("FOO", ("unknown", "known-b", "known c"))'
    136        )
    137        self.assertEqual(status, 0)
    138        self.assertEqual(config, {"FOO": self.KNOWN_B})
    139        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_B)
    140 
    141        config, out, status = self.get_result(
    142            'check_prog("FOO", ("unknown", "unknown-2", "known c"))'
    143        )
    144        self.assertEqual(status, 0)
    145        self.assertEqual(config, {"FOO": fake_short_path(self.KNOWN_C)})
    146        self.assertEqual(
    147            out, "checking for foo... %s\n" % shell_quote(fake_short_path(self.KNOWN_C))
    148        )
    149 
    150        config, out, status = self.get_result('check_prog("FOO", ("unknown",))')
    151        self.assertEqual(status, 1)
    152        self.assertEqual(config, {})
    153        self.assertEqual(
    154            out,
    155            textwrap.dedent(
    156                """\
    157            checking for foo... not found
    158            DEBUG: foo: Looking for unknown
    159            ERROR: Cannot find foo
    160        """
    161            ),
    162        )
    163 
    164        config, out, status = self.get_result(
    165            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"))'
    166        )
    167        self.assertEqual(status, 1)
    168        self.assertEqual(config, {})
    169        self.assertEqual(
    170            out,
    171            textwrap.dedent(
    172                """\
    173            checking for foo... not found
    174            DEBUG: foo: Looking for unknown
    175            DEBUG: foo: Looking for unknown-2
    176            DEBUG: foo: Looking for 'unknown 3'
    177            ERROR: Cannot find foo
    178        """
    179            ),
    180        )
    181 
    182        config, out, status = self.get_result(
    183            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
    184            "allow_missing=True)"
    185        )
    186        self.assertEqual(status, 0)
    187        self.assertEqual(config, {})
    188        self.assertEqual(out, "checking for foo... not found\n")
    189 
    190    @unittest.skipIf(not sys.platform.startswith("win"), "Windows-only test")
    191    def test_check_prog_exe(self):
    192        config, out, status = self.get_result(
    193            'check_prog("FOO", ("unknown", "known-b", "known c"))', ["FOO=known-a.exe"]
    194        )
    195        self.assertEqual(status, 0)
    196        self.assertEqual(config, {"FOO": self.KNOWN_A})
    197        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)
    198 
    199        config, out, status = self.get_result(
    200            'check_prog("FOO", ("unknown", "known-b", "known c"))',
    201            ["FOO=%s" % os.path.splitext(self.KNOWN_A)[0]],
    202        )
    203        self.assertEqual(status, 0)
    204        self.assertEqual(config, {"FOO": self.KNOWN_A})
    205        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)
    206 
    207    def test_check_prog_with_args(self):
    208        config, out, status = self.get_result(
    209            'check_prog("FOO", ("unknown", "known-b", "known c"))', ["FOO=known-a"]
    210        )
    211        self.assertEqual(status, 0)
    212        self.assertEqual(config, {"FOO": self.KNOWN_A})
    213        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)
    214 
    215        config, out, status = self.get_result(
    216            'check_prog("FOO", ("unknown", "known-b", "known c"))',
    217            ["FOO=%s" % self.KNOWN_A],
    218        )
    219        self.assertEqual(status, 0)
    220        self.assertEqual(config, {"FOO": self.KNOWN_A})
    221        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)
    222 
    223        path = self.KNOWN_B.replace("known-b", "known-a")
    224        config, out, status = self.get_result(
    225            'check_prog("FOO", ("unknown", "known-b", "known c"))', ["FOO=%s" % path]
    226        )
    227        self.assertEqual(status, 1)
    228        self.assertEqual(config, {})
    229        self.assertEqual(
    230            out,
    231            textwrap.dedent(
    232                """\
    233            checking for foo... not found
    234            DEBUG: foo: Looking for %s
    235            ERROR: Cannot find foo
    236        """
    237            )
    238            % path,
    239        )
    240 
    241        config, out, status = self.get_result(
    242            'check_prog("FOO", ("unknown",))', ["FOO=known c"]
    243        )
    244        self.assertEqual(status, 0)
    245        self.assertEqual(config, {"FOO": fake_short_path(self.KNOWN_C)})
    246        self.assertEqual(
    247            out, "checking for foo... %s\n" % shell_quote(fake_short_path(self.KNOWN_C))
    248        )
    249 
    250        config, out, status = self.get_result(
    251            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
    252            "allow_missing=True)",
    253            ["FOO=unknown"],
    254        )
    255        self.assertEqual(status, 1)
    256        self.assertEqual(config, {})
    257        self.assertEqual(
    258            out,
    259            textwrap.dedent(
    260                """\
    261            checking for foo... not found
    262            DEBUG: foo: Looking for unknown
    263            ERROR: Cannot find foo
    264        """
    265            ),
    266        )
    267 
    268    def test_check_prog_what(self):
    269        config, out, status = self.get_result(
    270            'check_prog("CC", ("known-a",), what="the target C compiler")'
    271        )
    272        self.assertEqual(status, 0)
    273        self.assertEqual(config, {"CC": self.KNOWN_A})
    274        self.assertEqual(
    275            out, "checking for the target C compiler... %s\n" % self.KNOWN_A
    276        )
    277 
    278        config, out, status = self.get_result(
    279            'check_prog("CC", ("unknown", "unknown-2", "unknown 3"),'
    280            '           what="the target C compiler")'
    281        )
    282        self.assertEqual(status, 1)
    283        self.assertEqual(config, {})
    284        self.assertEqual(
    285            out,
    286            textwrap.dedent(
    287                """\
    288            checking for the target C compiler... not found
    289            DEBUG: cc: Looking for unknown
    290            DEBUG: cc: Looking for unknown-2
    291            DEBUG: cc: Looking for 'unknown 3'
    292            ERROR: Cannot find the target C compiler
    293        """
    294            ),
    295        )
    296 
    297    def test_check_prog_input(self):
    298        config, out, status = self.get_result(
    299            textwrap.dedent(
    300                """
    301            option("--with-ccache", nargs=1, help="Ccache")
    302            check_prog("CCACHE", ("known-a",), input="--with-ccache")
    303        """
    304            ),
    305            ["--with-ccache=known-b"],
    306        )
    307        self.assertEqual(status, 0)
    308        self.assertEqual(config, {"CCACHE": self.KNOWN_B})
    309        self.assertEqual(out, "checking for ccache... %s\n" % self.KNOWN_B)
    310 
    311        script = textwrap.dedent(
    312            """
    313            option(env="CC", nargs=1, help="Compiler")
    314            @depends("CC")
    315            def compiler(value):
    316                return value[0].split()[0] if value else None
    317            check_prog("CC", ("known-a",), input=compiler)
    318        """
    319        )
    320        config, out, status = self.get_result(script)
    321        self.assertEqual(status, 0)
    322        self.assertEqual(config, {"CC": self.KNOWN_A})
    323        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A)
    324 
    325        config, out, status = self.get_result(script, ["CC=known-b"])
    326        self.assertEqual(status, 0)
    327        self.assertEqual(config, {"CC": self.KNOWN_B})
    328        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_B)
    329 
    330        config, out, status = self.get_result(script, ["CC=known-b -m32"])
    331        self.assertEqual(status, 0)
    332        self.assertEqual(config, {"CC": self.KNOWN_B})
    333        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_B)
    334 
    335    def test_check_prog_progs(self):
    336        config, out, status = self.get_result('check_prog("FOO", ())')
    337        self.assertEqual(status, 0)
    338        self.assertEqual(config, {})
    339        self.assertEqual(out, "")
    340 
    341        config, out, status = self.get_result('check_prog("FOO", ())', ["FOO=known-a"])
    342        self.assertEqual(status, 0)
    343        self.assertEqual(config, {"FOO": self.KNOWN_A})
    344        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)
    345 
    346        script = textwrap.dedent(
    347            """
    348            option(env="TARGET", nargs=1, default="linux", help="Target")
    349            @depends("TARGET")
    350            def compiler(value):
    351                if value:
    352                    if value[0] == "linux":
    353                        return ("gcc", "clang")
    354                    if value[0] == "winnt":
    355                        return ("cl", "clang-cl")
    356            check_prog("CC", compiler)
    357        """
    358        )
    359        config, out, status = self.get_result(script)
    360        self.assertEqual(status, 1)
    361        self.assertEqual(config, {})
    362        self.assertEqual(
    363            out,
    364            textwrap.dedent(
    365                """\
    366            checking for cc... not found
    367            DEBUG: cc: Looking for gcc
    368            DEBUG: cc: Looking for clang
    369            ERROR: Cannot find cc
    370        """
    371            ),
    372        )
    373 
    374        config, out, status = self.get_result(script, ["TARGET=linux"])
    375        self.assertEqual(status, 1)
    376        self.assertEqual(config, {})
    377        self.assertEqual(
    378            out,
    379            textwrap.dedent(
    380                """\
    381            checking for cc... not found
    382            DEBUG: cc: Looking for gcc
    383            DEBUG: cc: Looking for clang
    384            ERROR: Cannot find cc
    385        """
    386            ),
    387        )
    388 
    389        config, out, status = self.get_result(script, ["TARGET=winnt"])
    390        self.assertEqual(status, 1)
    391        self.assertEqual(config, {})
    392        self.assertEqual(
    393            out,
    394            textwrap.dedent(
    395                """\
    396            checking for cc... not found
    397            DEBUG: cc: Looking for cl
    398            DEBUG: cc: Looking for clang-cl
    399            ERROR: Cannot find cc
    400        """
    401            ),
    402        )
    403 
    404        config, out, status = self.get_result(script, ["TARGET=none"])
    405        self.assertEqual(status, 0)
    406        self.assertEqual(config, {})
    407        self.assertEqual(out, "")
    408 
    409        config, out, status = self.get_result(script, ["TARGET=winnt", "CC=known-a"])
    410        self.assertEqual(status, 0)
    411        self.assertEqual(config, {"CC": self.KNOWN_A})
    412        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A)
    413 
    414        config, out, status = self.get_result(script, ["TARGET=none", "CC=known-a"])
    415        self.assertEqual(status, 0)
    416        self.assertEqual(config, {"CC": self.KNOWN_A})
    417        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A)
    418 
    419    def test_check_prog_configure_error(self):
    420        with self.assertRaises(ConfigureError) as e:
    421            self.get_result('check_prog("FOO", "foo")')
    422 
    423        self.assertEqual(str(e.exception), "progs must resolve to a list or tuple!")
    424 
    425        with self.assertRaises(ConfigureError) as e:
    426            self.get_result(
    427                'foo = depends(when=True)(lambda: ("a", "b"))\n'
    428                'check_prog("FOO", ("known-a",), input=foo)'
    429            )
    430 
    431        self.assertEqual(
    432            str(e.exception),
    433            "input must resolve to a tuple or a list with a "
    434            "single element, or a string",
    435        )
    436 
    437        with self.assertRaises(ConfigureError) as e:
    438            self.get_result(
    439                'foo = depends(when=True)(lambda: {"a": "b"})\n'
    440                'check_prog("FOO", ("known-a",), input=foo)'
    441            )
    442 
    443        self.assertEqual(
    444            str(e.exception),
    445            "input must resolve to a tuple or a list with a "
    446            "single element, or a string",
    447        )
    448 
    449    def test_check_prog_with_path(self):
    450        config, out, status = self.get_result(
    451            'check_prog("A", ("known-a",), paths=["/some/path"])'
    452        )
    453        self.assertEqual(status, 1)
    454        self.assertEqual(config, {})
    455        self.assertEqual(
    456            out,
    457            textwrap.dedent(
    458                """\
    459            checking for a... not found
    460            DEBUG: a: Looking for known-a
    461            ERROR: Cannot find a
    462        """
    463            ),
    464        )
    465 
    466        config, out, status = self.get_result(
    467            'check_prog("A", ("known-a",), paths=["%s"])'
    468            % os.path.dirname(self.OTHER_A)
    469        )
    470        self.assertEqual(status, 0)
    471        self.assertEqual(config, {"A": self.OTHER_A})
    472        self.assertEqual(
    473            out,
    474            textwrap.dedent(
    475                """\
    476            checking for a... %s
    477        """
    478                % self.OTHER_A
    479            ),
    480        )
    481 
    482        dirs = map(mozpath.dirname, (self.OTHER_A, self.KNOWN_A))
    483        config, out, status = self.get_result(
    484            textwrap.dedent(
    485                """\
    486            check_prog("A", ("known-a",), paths=["%s"])
    487        """
    488                % os.pathsep.join(dirs)
    489            )
    490        )
    491        self.assertEqual(status, 0)
    492        self.assertEqual(config, {"A": self.OTHER_A})
    493        self.assertEqual(
    494            out,
    495            textwrap.dedent(
    496                """\
    497            checking for a... %s
    498        """
    499                % self.OTHER_A
    500            ),
    501        )
    502 
    503        dirs = map(mozpath.dirname, (self.KNOWN_A, self.KNOWN_B))
    504        config, out, status = self.get_result(
    505            textwrap.dedent(
    506                """\
    507            check_prog("A", ("known-a",), paths=["%s", "%s"])
    508        """
    509                % (os.pathsep.join(dirs), self.OTHER_A)
    510            )
    511        )
    512        self.assertEqual(status, 0)
    513        self.assertEqual(config, {"A": self.KNOWN_A})
    514        self.assertEqual(
    515            out,
    516            textwrap.dedent(
    517                """\
    518            checking for a... %s
    519        """
    520                % self.KNOWN_A
    521            ),
    522        )
    523 
    524        config, out, status = self.get_result(
    525            'check_prog("A", ("known-a",), paths="%s")' % os.path.dirname(self.OTHER_A)
    526        )
    527 
    528        self.assertEqual(status, 1)
    529        self.assertEqual(config, {})
    530        self.assertEqual(
    531            out,
    532            textwrap.dedent(
    533                """\
    534            checking for a... """  # noqa  # trailing whitespace...
    535                """
    536            DEBUG: a: Looking for known-a
    537            ERROR: Paths provided to find_program must be a list of strings, not %r
    538        """ % mozpath.dirname(self.OTHER_A)
    539            ),
    540        )
    541 
    542    @unittest.skipIf(
    543        not sys.platform.startswith("linux"),
    544        "Linux-only test, assumes Java is located from a $PATH",
    545    )
    546    def test_java_tool_checks_linux(self):
    547        def run_configure_java(
    548            mock_fs_paths, mock_java_home=None, mock_path=None, args=[]
    549        ):
    550            script = textwrap.dedent(
    551                """\
    552                    @depends('--help')
    553                    def host(_):
    554                        return namespace(os='unknown', kernel='unknown')
    555                    toolchains_base_dir = depends(when=True)(lambda: '/mozbuild')
    556                    want_bootstrap = dependable(lambda: lambda _: False)
    557                    include('%(topsrcdir)s/build/moz.configure/java.configure')
    558                """
    559                % {"topsrcdir": topsrcdir}
    560            )
    561 
    562            # Don't let system JAVA_HOME influence the test
    563            original_java_home = os.environ.pop("JAVA_HOME", None)
    564            configure_environ = {}
    565 
    566            if mock_java_home:
    567                os.environ["JAVA_HOME"] = mock_java_home
    568                configure_environ["JAVA_HOME"] = mock_java_home
    569 
    570            if mock_path:
    571                configure_environ["PATH"] = mock_path
    572 
    573            # * Even if the real file sysphabtem has a symlink at the mocked path, don't let
    574            #   realpath follow it, as it may influence the test.
    575            # * When finding a binary, check the mock paths rather than the real filesystem.
    576            # Note: Python doesn't allow the different "with" bits to be put in parenthesis,
    577            # because then it thinks it's an un-with-able tuple. Additionally, if this is cleanly
    578            # lined up with "\", black removes them and autoformats them to the block that is
    579            # below.
    580            result = self.get_result(
    581                args=args,
    582                command=script,
    583                extra_paths=paths,
    584                environ=configure_environ,
    585            )
    586 
    587            if original_java_home:
    588                os.environ["JAVA_HOME"] = original_java_home
    589            return result
    590 
    591        java = mozpath.abspath("/usr/bin/java")
    592        javac = mozpath.abspath("/usr/bin/javac")
    593        paths = {java: None, javac: None}
    594        expected_error_message = (
    595            "ERROR: Could not locate Java at /mozbuild/jdk/jdk-17.0.17+10/bin, "
    596            "please run ./mach bootstrap --no-system-changes\n"
    597        )
    598 
    599        config, out, status = run_configure_java(paths)
    600        self.assertEqual(status, 1)
    601        self.assertEqual(config, {})
    602        self.assertEqual(out, expected_error_message)
    603 
    604        # An alternative valid set of tools referred to by JAVA_HOME.
    605        alt_java = mozpath.abspath("/usr/local/bin/java")
    606        alt_javac = mozpath.abspath("/usr/local/bin/javac")
    607        alt_java_home = mozpath.dirname(mozpath.dirname(alt_java))
    608        paths = {alt_java: None, alt_javac: None, java: None, javac: None}
    609 
    610        alt_path = mozpath.dirname(java)
    611        config, out, status = run_configure_java(paths, alt_java_home, alt_path)
    612        self.assertEqual(status, 1)
    613        self.assertEqual(config, {})
    614        self.assertEqual(out, expected_error_message)
    615 
    616        # We can use --with-java-bin-path instead of JAVA_HOME to similar
    617        # effect.
    618        config, out, status = run_configure_java(
    619            paths,
    620            mock_path=mozpath.dirname(java),
    621            args=["--with-java-bin-path=%s" % mozpath.dirname(alt_java)],
    622        )
    623        self.assertEqual(status, 0)
    624        self.assertEqual(config, {"JAVA": alt_java, "MOZ_JAVA_CODE_COVERAGE": False})
    625        self.assertEqual(
    626            out,
    627            textwrap.dedent(
    628                """\
    629             checking for java... %s
    630        """
    631                % alt_java
    632            ),
    633        )
    634 
    635        # If --with-java-bin-path and JAVA_HOME are both set,
    636        # --with-java-bin-path takes precedence.
    637        config, out, status = run_configure_java(
    638            paths,
    639            mock_java_home=mozpath.dirname(mozpath.dirname(java)),
    640            mock_path=mozpath.dirname(java),
    641            args=["--with-java-bin-path=%s" % mozpath.dirname(alt_java)],
    642        )
    643        self.assertEqual(status, 0)
    644        self.assertEqual(config, {"JAVA": alt_java, "MOZ_JAVA_CODE_COVERAGE": False})
    645        self.assertEqual(
    646            out,
    647            textwrap.dedent(
    648                """\
    649             checking for java... %s
    650        """
    651                % alt_java
    652            ),
    653        )
    654 
    655        # --enable-java-coverage should set MOZ_JAVA_CODE_COVERAGE.
    656        alt_java_home = mozpath.dirname(mozpath.dirname(java))
    657        config, out, status = run_configure_java(
    658            paths,
    659            mock_java_home=alt_java_home,
    660            mock_path=mozpath.dirname(java),
    661            args=["--enable-java-coverage"],
    662        )
    663        self.assertEqual(status, 1)
    664        self.assertEqual(config, {})
    665 
    666        # Any missing tool is fatal when these checks run.
    667        paths = {}
    668        config, out, status = run_configure_java(
    669            mock_fs_paths={},
    670            mock_path=mozpath.dirname(java),
    671            args=["--enable-java-coverage"],
    672        )
    673        self.assertEqual(status, 1)
    674        self.assertEqual(config, {})
    675        self.assertEqual(out, expected_error_message)
    676 
    677    def test_pkg_check_modules(self):
    678        mock_pkg_config_version = "0.10.0"
    679        mock_pkg_config_path = mozpath.abspath("/usr/bin/pkg-config")
    680 
    681        seen_flags = set()
    682 
    683        def mock_pkg_config(_, args):
    684            if "--dont-define-prefix" in args:
    685                args = list(args)
    686                seen_flags.add(args.pop(args.index("--dont-define-prefix")))
    687                args = tuple(args)
    688            if args[0:2] == ("--errors-to-stdout", "--print-errors"):
    689                assert len(args) == 3
    690                package = args[2]
    691                if package == "unknown":
    692                    return (
    693                        1,
    694                        "Package unknown was not found in the pkg-config search path.\n"
    695                        "Perhaps you should add the directory containing `unknown.pc'\n"
    696                        "to the PKG_CONFIG_PATH environment variable\n"
    697                        "No package 'unknown' found",
    698                        "",
    699                    )
    700                if package == "valid":
    701                    return 0, "", ""
    702                if package == "new > 1.1":
    703                    return 1, "Requested 'new > 1.1' but version of new is 1.1", ""
    704            if args[0] == "--cflags":
    705                assert len(args) == 2
    706                return 0, "-I/usr/include/%s" % args[1], ""
    707            if args[0] == "--libs":
    708                assert len(args) == 2
    709                return 0, "-l%s" % args[1], ""
    710            if args[0] == "--version":
    711                return 0, mock_pkg_config_version, ""
    712            if args[0] == "--about":
    713                return 1, "Unknown option --about", ""
    714            self.fail("Unexpected arguments to mock_pkg_config: %s" % (args,))
    715 
    716        def mock_pkgconf(_, args):
    717            if args[0] == "--shared":
    718                seen_flags.add(args[0])
    719                args = args[1:]
    720            if args[0] == "--about":
    721                return 0, f"pkgconf {mock_pkg_config_version}", ""
    722            return mock_pkg_config(_, args)
    723 
    724        def get_result(cmd, args=[], bootstrapped_sysroot=False, extra_paths=None):
    725            return self.get_result(
    726                textwrap.dedent(
    727                    """\
    728                option('--disable-compile-environment', help='Compile env')
    729                compile_environment = depends(when='--enable-compile-environment')(lambda: True)
    730                toolchain_prefix = depends(when=True)(lambda: None)
    731                target_multiarch_dir = depends(when=True)(lambda: None)
    732                target_sysroot = depends(when=True)(lambda: %(sysroot)s)
    733                target = depends(when=True)(lambda: namespace(os="unknown"))
    734                include('%(topsrcdir)s/build/moz.configure/util.configure')
    735                include('%(topsrcdir)s/build/moz.configure/checks.configure')
    736                # Skip bootstrapping.
    737                @template
    738                def check_prog(*args, **kwargs):
    739                    del kwargs["bootstrap"]
    740                    return check_prog(*args, **kwargs)
    741                include('%(topsrcdir)s/build/moz.configure/pkg.configure')
    742            """
    743                    % {
    744                        "topsrcdir": topsrcdir,
    745                        "sysroot": (
    746                            "namespace(bootstrapped=True)"
    747                            if bootstrapped_sysroot
    748                            else "None"
    749                        ),
    750                    }
    751                )
    752                + cmd,
    753                args=args,
    754                extra_paths=extra_paths,
    755                includes=(),
    756            )
    757 
    758        extra_paths = {mock_pkg_config_path: mock_pkg_config}
    759 
    760        config, output, status = get_result("pkg_check_modules('MOZ_VALID', 'valid')")
    761        self.assertEqual(status, 1)
    762        self.assertEqual(
    763            output,
    764            textwrap.dedent(
    765                """\
    766            checking for pkg_config... not found
    767            ERROR: *** The pkg-config script could not be found. Make sure it is
    768            *** in your path, or set the PKG_CONFIG environment variable
    769            *** to the full path to pkg-config.
    770        """
    771            ),
    772        )
    773 
    774        for pkg_config, version, bootstrapped_sysroot, is_pkgconf in (
    775            (mock_pkg_config, "0.10.0", False, False),
    776            (mock_pkg_config, "0.30.0", False, False),
    777            (mock_pkg_config, "0.30.0", True, False),
    778            (mock_pkgconf, "1.1.0", True, True),
    779            (mock_pkgconf, "1.6.0", False, True),
    780            (mock_pkgconf, "1.8.0", False, True),
    781            (mock_pkgconf, "1.8.0", True, True),
    782        ):
    783            seen_flags = set()
    784            mock_pkg_config_version = version
    785            config, output, status = get_result(
    786                "pkg_check_modules('MOZ_VALID', 'valid')",
    787                bootstrapped_sysroot=bootstrapped_sysroot,
    788                extra_paths={mock_pkg_config_path: pkg_config},
    789            )
    790            self.assertEqual(status, 0)
    791            self.assertEqual(
    792                output,
    793                textwrap.dedent(
    794                    """\
    795                checking for pkg_config... %s
    796                checking for pkg-config version... %s
    797                checking whether pkg-config is pkgconf... %s
    798                checking for valid... yes
    799                checking MOZ_VALID_CFLAGS... -I/usr/include/valid
    800                checking MOZ_VALID_LIBS... -lvalid
    801            """
    802                    % (
    803                        mock_pkg_config_path,
    804                        mock_pkg_config_version,
    805                        "yes" if is_pkgconf else "no",
    806                    )
    807                ),
    808            )
    809            self.assertEqual(
    810                config,
    811                {
    812                    "PKG_CONFIG": mock_pkg_config_path,
    813                    "MOZ_VALID_CFLAGS": ("-I/usr/include/valid",),
    814                    "MOZ_VALID_LIBS": ("-lvalid",),
    815                },
    816            )
    817            if version == "1.8.0" and bootstrapped_sysroot:
    818                self.assertEqual(seen_flags, set(["--shared", "--dont-define-prefix"]))
    819            elif version == "1.8.0":
    820                self.assertEqual(seen_flags, set(["--shared"]))
    821            elif version in ("1.6.0", "0.30.0") and bootstrapped_sysroot:
    822                self.assertEqual(seen_flags, set(["--dont-define-prefix"]))
    823            else:
    824                self.assertEqual(seen_flags, set())
    825 
    826        config, output, status = get_result(
    827            "pkg_check_modules('MOZ_UKNOWN', 'unknown')", extra_paths=extra_paths
    828        )
    829        self.assertEqual(status, 1)
    830        self.assertEqual(
    831            output,
    832            textwrap.dedent(
    833                """\
    834            checking for pkg_config... %s
    835            checking for pkg-config version... %s
    836            checking whether pkg-config is pkgconf... no
    837            checking for unknown... no
    838            ERROR: Package unknown was not found in the pkg-config search path.
    839            ERROR: Perhaps you should add the directory containing `unknown.pc'
    840            ERROR: to the PKG_CONFIG_PATH environment variable
    841            ERROR: No package 'unknown' found
    842        """
    843                % (mock_pkg_config_path, mock_pkg_config_version)
    844            ),
    845        )
    846        self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path})
    847 
    848        config, output, status = get_result(
    849            "pkg_check_modules('MOZ_NEW', 'new > 1.1')", extra_paths=extra_paths
    850        )
    851        self.assertEqual(status, 1)
    852        self.assertEqual(
    853            output,
    854            textwrap.dedent(
    855                """\
    856            checking for pkg_config... %s
    857            checking for pkg-config version... %s
    858            checking whether pkg-config is pkgconf... no
    859            checking for new > 1.1... no
    860            ERROR: Requested 'new > 1.1' but version of new is 1.1
    861        """
    862                % (mock_pkg_config_path, mock_pkg_config_version)
    863            ),
    864        )
    865        self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path})
    866 
    867        # allow_missing makes missing packages non-fatal.
    868        cmd = textwrap.dedent(
    869            """\
    870        have_new_module = pkg_check_modules('MOZ_NEW', 'new > 1.1', allow_missing=True)
    871        @depends(have_new_module)
    872        def log_new_module_error(mod):
    873            if mod is not True:
    874                log.info('Module not found.')
    875        """
    876        )
    877 
    878        config, output, status = get_result(cmd, extra_paths=extra_paths)
    879        self.assertEqual(status, 0)
    880        self.assertEqual(
    881            output,
    882            textwrap.dedent(
    883                """\
    884            checking for pkg_config... %s
    885            checking for pkg-config version... %s
    886            checking whether pkg-config is pkgconf... no
    887            checking for new > 1.1... no
    888            WARNING: Requested 'new > 1.1' but version of new is 1.1
    889            Module not found.
    890        """
    891                % (mock_pkg_config_path, mock_pkg_config_version)
    892            ),
    893        )
    894        self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path})
    895 
    896        config, output, status = get_result(
    897            cmd, args=["--disable-compile-environment"], extra_paths=extra_paths
    898        )
    899        self.assertEqual(status, 0)
    900        self.assertEqual(output, "Module not found.\n")
    901        self.assertEqual(config, {})
    902 
    903        def mock_old_pkg_config(_, args):
    904            if args[0] == "--version":
    905                return 0, "0.8.10", ""
    906            if args[0] == "--about":
    907                return 1, "Unknown option --about", ""
    908            self.fail("Unexpected arguments to mock_old_pkg_config: %s" % args)
    909 
    910        extra_paths = {mock_pkg_config_path: mock_old_pkg_config}
    911 
    912        config, output, status = get_result(
    913            "pkg_check_modules('MOZ_VALID', 'valid')", extra_paths=extra_paths
    914        )
    915        self.assertEqual(status, 1)
    916        self.assertEqual(
    917            output,
    918            textwrap.dedent(
    919                """\
    920            checking for pkg_config... %s
    921            checking for pkg-config version... 0.8.10
    922            checking whether pkg-config is pkgconf... no
    923            ERROR: *** Your version of pkg-config is too old. You need version 0.9.0 or newer.
    924        """
    925                % mock_pkg_config_path
    926            ),
    927        )
    928 
    929    def test_simple_keyfile(self):
    930        includes = ("util.configure", "checks.configure", "keyfiles.configure")
    931 
    932        config, output, status = self.get_result(
    933            "simple_keyfile('Mozilla API')", includes=includes
    934        )
    935        self.assertEqual(status, 0)
    936        self.assertEqual(
    937            output,
    938            textwrap.dedent(
    939                """\
    940            checking for the Mozilla API key... no
    941        """
    942            ),
    943        )
    944        self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "no-mozilla-api-key"})
    945 
    946        config, output, status = self.get_result(
    947            "simple_keyfile('Mozilla API')",
    948            args=["--with-mozilla-api-keyfile=/foo/bar/does/not/exist"],
    949            includes=includes,
    950        )
    951        self.assertEqual(status, 1)
    952        self.assertEqual(
    953            output,
    954            textwrap.dedent(
    955                """\
    956            checking for the Mozilla API key... no
    957            ERROR: '/foo/bar/does/not/exist': No such file or directory.
    958        """
    959            ),
    960        )
    961        self.assertEqual(config, {})
    962 
    963        with MockedOpen({"key": ""}):
    964            config, output, status = self.get_result(
    965                "simple_keyfile('Mozilla API')",
    966                args=["--with-mozilla-api-keyfile=key"],
    967                includes=includes,
    968            )
    969            self.assertEqual(status, 1)
    970            self.assertEqual(
    971                output,
    972                textwrap.dedent(
    973                    """\
    974                checking for the Mozilla API key... no
    975                ERROR: 'key' is empty.
    976            """
    977                ),
    978            )
    979            self.assertEqual(config, {})
    980 
    981        with MockedOpen({"key": "fake-key\n"}):
    982            config, output, status = self.get_result(
    983                "simple_keyfile('Mozilla API')",
    984                args=["--with-mozilla-api-keyfile=key"],
    985                includes=includes,
    986            )
    987            self.assertEqual(status, 0)
    988            self.assertEqual(
    989                output,
    990                textwrap.dedent(
    991                    """\
    992                checking for the Mozilla API key... yes
    993            """
    994                ),
    995            )
    996            self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "fake-key"})
    997 
    998        with MockedOpen({"default": "default-key\n"}):
    999            config, output, status = self.get_result(
   1000                "simple_keyfile('Mozilla API', default='default')", includes=includes
   1001            )
   1002            self.assertEqual(status, 0)
   1003            self.assertEqual(
   1004                output,
   1005                textwrap.dedent(
   1006                    """\
   1007                checking for the Mozilla API key... yes
   1008            """
   1009                ),
   1010            )
   1011            self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "default-key"})
   1012 
   1013        with MockedOpen({"default": "default-key\n", "key": "fake-key\n"}):
   1014            config, output, status = self.get_result(
   1015                "simple_keyfile('Mozilla API', default='key')", includes=includes
   1016            )
   1017            self.assertEqual(status, 0)
   1018            self.assertEqual(
   1019                output,
   1020                textwrap.dedent(
   1021                    """\
   1022                checking for the Mozilla API key... yes
   1023            """
   1024                ),
   1025            )
   1026            self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "fake-key"})
   1027 
   1028    def test_id_and_secret_keyfile(self):
   1029        includes = ("util.configure", "checks.configure", "keyfiles.configure")
   1030 
   1031        config, output, status = self.get_result(
   1032            "id_and_secret_keyfile('Bing API')", includes=includes
   1033        )
   1034        self.assertEqual(status, 0)
   1035        self.assertEqual(
   1036            output,
   1037            textwrap.dedent(
   1038                """\
   1039            checking for the Bing API key... no
   1040        """
   1041            ),
   1042        )
   1043        self.assertEqual(
   1044            config,
   1045            {
   1046                "MOZ_BING_API_CLIENTID": "no-bing-api-clientid",
   1047                "MOZ_BING_API_KEY": "no-bing-api-key",
   1048            },
   1049        )
   1050 
   1051        config, output, status = self.get_result(
   1052            "id_and_secret_keyfile('Bing API')",
   1053            args=["--with-bing-api-keyfile=/foo/bar/does/not/exist"],
   1054            includes=includes,
   1055        )
   1056        self.assertEqual(status, 1)
   1057        self.assertEqual(
   1058            output,
   1059            textwrap.dedent(
   1060                """\
   1061            checking for the Bing API key... no
   1062            ERROR: '/foo/bar/does/not/exist': No such file or directory.
   1063        """
   1064            ),
   1065        )
   1066        self.assertEqual(config, {})
   1067 
   1068        with MockedOpen({"key": ""}):
   1069            config, output, status = self.get_result(
   1070                "id_and_secret_keyfile('Bing API')",
   1071                args=["--with-bing-api-keyfile=key"],
   1072                includes=includes,
   1073            )
   1074            self.assertEqual(status, 1)
   1075            self.assertEqual(
   1076                output,
   1077                textwrap.dedent(
   1078                    """\
   1079                checking for the Bing API key... no
   1080                ERROR: 'key' is empty.
   1081            """
   1082                ),
   1083            )
   1084            self.assertEqual(config, {})
   1085 
   1086        with MockedOpen({"key": "fake-id fake-key\n"}):
   1087            config, output, status = self.get_result(
   1088                "id_and_secret_keyfile('Bing API')",
   1089                args=["--with-bing-api-keyfile=key"],
   1090                includes=includes,
   1091            )
   1092            self.assertEqual(status, 0)
   1093            self.assertEqual(
   1094                output,
   1095                textwrap.dedent(
   1096                    """\
   1097                checking for the Bing API key... yes
   1098            """
   1099                ),
   1100            )
   1101            self.assertEqual(
   1102                config,
   1103                {"MOZ_BING_API_CLIENTID": "fake-id", "MOZ_BING_API_KEY": "fake-key"},
   1104            )
   1105 
   1106        with MockedOpen({"key": "fake-key\n"}):
   1107            config, output, status = self.get_result(
   1108                "id_and_secret_keyfile('Bing API')",
   1109                args=["--with-bing-api-keyfile=key"],
   1110                includes=includes,
   1111            )
   1112            self.assertEqual(status, 1)
   1113            self.assertEqual(
   1114                output,
   1115                textwrap.dedent(
   1116                    """\
   1117                checking for the Bing API key... no
   1118                ERROR: Bing API key file has an invalid format.
   1119            """
   1120                ),
   1121            )
   1122            self.assertEqual(config, {})
   1123 
   1124        with MockedOpen({"default-key": "default-id default-key\n"}):
   1125            config, output, status = self.get_result(
   1126                "id_and_secret_keyfile('Bing API', default='default-key')",
   1127                includes=includes,
   1128            )
   1129            self.assertEqual(status, 0)
   1130            self.assertEqual(
   1131                output,
   1132                textwrap.dedent(
   1133                    """\
   1134                checking for the Bing API key... yes
   1135            """
   1136                ),
   1137            )
   1138            self.assertEqual(
   1139                config,
   1140                {
   1141                    "MOZ_BING_API_CLIENTID": "default-id",
   1142                    "MOZ_BING_API_KEY": "default-key",
   1143                },
   1144            )
   1145 
   1146        with MockedOpen({
   1147            "default-key": "default-id default-key\n",
   1148            "key": "fake-id fake-key\n",
   1149        }):
   1150            config, output, status = self.get_result(
   1151                "id_and_secret_keyfile('Bing API', default='default-key')",
   1152                args=["--with-bing-api-keyfile=key"],
   1153                includes=includes,
   1154            )
   1155            self.assertEqual(status, 0)
   1156            self.assertEqual(
   1157                output,
   1158                textwrap.dedent(
   1159                    """\
   1160                checking for the Bing API key... yes
   1161            """
   1162                ),
   1163            )
   1164            self.assertEqual(
   1165                config,
   1166                {"MOZ_BING_API_CLIENTID": "fake-id", "MOZ_BING_API_KEY": "fake-key"},
   1167            )
   1168 
   1169 
   1170 if __name__ == "__main__":
   1171    main()