tor-browser

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

test_mozwebidlcodegen.py (10595B)


      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 json
      6 import os
      7 import shutil
      8 import sys
      9 import tempfile
     10 import unittest
     11 
     12 import mozpack.path as mozpath
     13 from mozfile import NamedTemporaryFile, load_source
     14 from mozunit import MockedOpen, main
     15 from mozwebidlcodegen import WebIDLCodegenManager, WebIDLCodegenManagerState
     16 
     17 OUR_DIR = mozpath.abspath(mozpath.dirname(__file__))
     18 TOPSRCDIR = mozpath.normpath(mozpath.join(OUR_DIR, "..", "..", "..", ".."))
     19 
     20 
     21 class TestWebIDLCodegenManager(unittest.TestCase):
     22    TEST_STEMS = {
     23        "Child",
     24        "Parent",
     25        "ExampleBinding",
     26        "TestEvent",
     27    }
     28 
     29    @property
     30    def _static_input_paths(self):
     31        s = {
     32            mozpath.join(OUR_DIR, p)
     33            for p in os.listdir(OUR_DIR)
     34            if p.endswith(".webidl")
     35        }
     36 
     37        return s
     38 
     39    @property
     40    def _config_path(self):
     41        config = mozpath.join(OUR_DIR, "TestBindings.conf")
     42        self.assertTrue(os.path.exists(config))
     43 
     44        return config
     45 
     46    def _get_manager_args(self):
     47        tmp = tempfile.mkdtemp()
     48        self.addCleanup(shutil.rmtree, tmp)
     49 
     50        cache_dir = mozpath.join(tmp, "cache")
     51        os.mkdir(cache_dir)
     52 
     53        ip = self._static_input_paths
     54 
     55        inputs = (
     56            ip,
     57            {mozpath.splitext(mozpath.basename(p))[0] for p in ip},
     58            set(),
     59            set(),
     60        )
     61 
     62        return dict(
     63            config_path=self._config_path,
     64            webidl_root=cache_dir,
     65            inputs=inputs,
     66            exported_header_dir=mozpath.join(tmp, "exports"),
     67            codegen_dir=mozpath.join(tmp, "codegen"),
     68            state_path=mozpath.join(tmp, "state.json"),
     69            make_deps_path=mozpath.join(tmp, "codegen.pp"),
     70            make_deps_target="codegen.pp",
     71            cache_dir=cache_dir,
     72        )
     73 
     74    def _get_manager(self):
     75        return WebIDLCodegenManager(**self._get_manager_args())
     76 
     77    def test_unknown_state_version(self):
     78        """Loading a state file with a too new version resets state."""
     79        args = self._get_manager_args()
     80 
     81        p = args["state_path"]
     82 
     83        with open(p, "w", newline="\n") as fh:
     84            json.dump(
     85                {
     86                    "version": WebIDLCodegenManagerState.VERSION + 1,
     87                    "foobar": "1",
     88                },
     89                fh,
     90            )
     91 
     92        manager = WebIDLCodegenManager(**args)
     93 
     94        self.assertEqual(manager._state["version"], WebIDLCodegenManagerState.VERSION)
     95        self.assertNotIn("foobar", manager._state)
     96 
     97    def test_generate_build_files(self):
     98        """generate_build_files() does the right thing from empty."""
     99        manager = self._get_manager()
    100        result = manager.generate_build_files()
    101        self.assertEqual(len(result.inputs), 4)
    102 
    103        output = manager.expected_build_output_files()
    104        self.assertEqual(result.created, output)
    105        self.assertEqual(len(result.updated), 0)
    106        self.assertEqual(len(result.unchanged), 0)
    107 
    108        for f in output:
    109            self.assertTrue(os.path.isfile(f))
    110 
    111        for f in manager.GLOBAL_DECLARE_FILES:
    112            self.assertIn(mozpath.join(manager._exported_header_dir, f), output)
    113 
    114        for f in manager.GLOBAL_DEFINE_FILES:
    115            self.assertIn(mozpath.join(manager._codegen_dir, f), output)
    116 
    117        for s in self.TEST_STEMS:
    118            self.assertTrue(
    119                os.path.isfile(
    120                    mozpath.join(manager._exported_header_dir, "%sBinding.h" % s)
    121                )
    122            )
    123            self.assertTrue(
    124                os.path.isfile(
    125                    mozpath.join(manager._exported_header_dir, "%sBindingFwd.h" % s)
    126                )
    127            )
    128            self.assertTrue(
    129                os.path.isfile(mozpath.join(manager._codegen_dir, "%sBinding.cpp" % s))
    130            )
    131 
    132        self.assertTrue(os.path.isfile(manager._state_path))
    133 
    134        with open(manager._state_path) as fh:
    135            state = json.load(fh)
    136            self.assertEqual(state["version"], 3)
    137            self.assertIn("webidls", state)
    138 
    139            child = state["webidls"]["Child.webidl"]
    140            self.assertEqual(len(child["inputs"]), 2)
    141            self.assertEqual(len(child["outputs"]), 3)
    142            self.assertEqual(child["sha1"], "c34c40b0fa0ac57c2834ee282efe0681e4dacc35")
    143 
    144    def test_generate_build_files_load_state(self):
    145        """State should be equivalent when instantiating a new instance."""
    146        args = self._get_manager_args()
    147        m1 = WebIDLCodegenManager(**args)
    148        self.assertEqual(len(m1._state["webidls"]), 0)
    149        m1.generate_build_files()
    150 
    151        m2 = WebIDLCodegenManager(**args)
    152        self.assertGreater(len(m2._state["webidls"]), 2)
    153        self.assertEqual(m1._state, m2._state)
    154 
    155    def test_no_change_no_writes(self):
    156        """If nothing changes, no files should be updated."""
    157        args = self._get_manager_args()
    158        m1 = WebIDLCodegenManager(**args)
    159        m1.generate_build_files()
    160 
    161        m2 = WebIDLCodegenManager(**args)
    162        result = m2.generate_build_files()
    163 
    164        self.assertEqual(len(result.inputs), 0)
    165        self.assertEqual(len(result.created), 0)
    166        self.assertEqual(len(result.updated), 0)
    167 
    168    def test_output_file_regenerated(self):
    169        """If an output file disappears, it is regenerated."""
    170        args = self._get_manager_args()
    171        m1 = WebIDLCodegenManager(**args)
    172        m1.generate_build_files()
    173 
    174        rm_count = 0
    175        for p in m1._state["webidls"]["Child.webidl"]["outputs"]:
    176            rm_count += 1
    177            os.unlink(p)
    178 
    179        for p in m1.GLOBAL_DECLARE_FILES:
    180            rm_count += 1
    181            os.unlink(mozpath.join(m1._exported_header_dir, p))
    182 
    183        m2 = WebIDLCodegenManager(**args)
    184        result = m2.generate_build_files()
    185        self.assertEqual(len(result.created), rm_count)
    186 
    187    def test_only_rebuild_self(self):
    188        """If an input file changes, only rebuild that one file."""
    189        args = self._get_manager_args()
    190        m1 = WebIDLCodegenManager(**args)
    191        m1.generate_build_files()
    192 
    193        child_path = None
    194        for p in m1._input_paths:
    195            if p.endswith("Child.webidl"):
    196                child_path = p
    197                break
    198 
    199        self.assertIsNotNone(child_path)
    200        child_content = open(child_path).read()
    201 
    202        with MockedOpen({child_path: child_content + "\n/* */"}):
    203            m2 = WebIDLCodegenManager(**args)
    204            result = m2.generate_build_files()
    205            self.assertEqual(result.inputs, set([child_path]))
    206            self.assertEqual(len(result.updated), 0)
    207            self.assertEqual(len(result.created), 0)
    208 
    209    def test_rebuild_dependencies(self):
    210        """Ensure an input file used by others results in others rebuilding."""
    211        args = self._get_manager_args()
    212        m1 = WebIDLCodegenManager(**args)
    213        m1.generate_build_files()
    214 
    215        parent_path = None
    216        child_path = None
    217        for p in m1._input_paths:
    218            if p.endswith("Parent.webidl"):
    219                parent_path = p
    220            elif p.endswith("Child.webidl"):
    221                child_path = p
    222 
    223        self.assertIsNotNone(parent_path)
    224        parent_content = open(parent_path).read()
    225 
    226        with MockedOpen({parent_path: parent_content + "\n/* */"}):
    227            m2 = WebIDLCodegenManager(**args)
    228            result = m2.generate_build_files()
    229            self.assertEqual(result.inputs, {child_path, parent_path})
    230            self.assertEqual(len(result.updated), 0)
    231            self.assertEqual(len(result.created), 0)
    232 
    233    def test_python_change_regenerate_everything(self):
    234        """If a Python file changes, we should attempt to rebuild everything."""
    235 
    236        # We don't want to mutate files in the source directory because we want
    237        # to be able to build from a read-only filesystem. So, we install a
    238        # dummy module and rewrite the metadata to say it comes from the source
    239        # directory.
    240        #
    241        # Hacking imp to accept a MockedFile doesn't appear possible. So for
    242        # the first iteration we read from a temp file. The second iteration
    243        # doesn't need to import, so we are fine with a mocked file.
    244        fake_path = mozpath.join(OUR_DIR, "fakemodule.py")
    245        with NamedTemporaryFile("wt") as fh:
    246            fh.write("# Original content")
    247            fh.flush()
    248            mod = load_source("mozwebidlcodegen.fakemodule", fh.name)
    249            mod.__file__ = fake_path
    250 
    251            args = self._get_manager_args()
    252            m1 = WebIDLCodegenManager(**args)
    253            with MockedOpen({fake_path: "# Original content"}):
    254                # MockedOpen is not compatible with distributed filesystem
    255                # access, so force the number of processes used to generate
    256                # files to 1.
    257                try:
    258                    result = m1.generate_build_files(processes=1)
    259                    l = len(result.inputs)
    260 
    261                    with open(fake_path, "w", newline="\n") as fh:
    262                        fh.write("# Modified content")
    263 
    264                    m2 = WebIDLCodegenManager(**args)
    265                    result = m2.generate_build_files(processes=1)
    266                    self.assertEqual(len(result.inputs), l)
    267 
    268                    result = m2.generate_build_files(processes=1)
    269                    self.assertEqual(len(result.inputs), 0)
    270                finally:
    271                    del sys.modules["mozwebidlcodegen.fakemodule"]
    272 
    273    def test_copy_input(self):
    274        """Ensure a copied .webidl file is handled properly."""
    275 
    276        # This test simulates changing the type of a WebIDL from static to
    277        # preprocessed. In that scenario, the original file still exists but
    278        # it should no longer be consulted during codegen.
    279 
    280        args = self._get_manager_args()
    281        m1 = WebIDLCodegenManager(**args)
    282        m1.generate_build_files()
    283 
    284        old_path = None
    285        for p in args["inputs"][0]:
    286            if p.endswith("Parent.webidl"):
    287                old_path = p
    288                break
    289        self.assertIsNotNone(old_path)
    290 
    291        new_path = mozpath.join(args["cache_dir"], "Parent.webidl")
    292        shutil.copy2(old_path, new_path)
    293 
    294        args["inputs"][0].remove(old_path)
    295        args["inputs"][0].add(new_path)
    296 
    297        m2 = WebIDLCodegenManager(**args)
    298        result = m2.generate_build_files()
    299        self.assertEqual(len(result.updated), 0)
    300 
    301 
    302 if __name__ == "__main__":
    303    main()