diff options
Diffstat (limited to 'dom/bindings/mozwebidlcodegen/test')
6 files changed, 336 insertions, 0 deletions
diff --git a/dom/bindings/mozwebidlcodegen/test/Child.webidl b/dom/bindings/mozwebidlcodegen/test/Child.webidl new file mode 100644 index 0000000000..f7d0a76c9d --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/Child.webidl @@ -0,0 +1,3 @@ +interface Child : Parent { + void ChildBaz(); +}; diff --git a/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl new file mode 100644 index 0000000000..34794993fe --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl @@ -0,0 +1,3 @@ +/* These interfaces are hard-coded and need to be defined. */ +interface TestExampleInterface {}; +interface TestExampleProxyInterface {}; diff --git a/dom/bindings/mozwebidlcodegen/test/Parent.webidl b/dom/bindings/mozwebidlcodegen/test/Parent.webidl new file mode 100644 index 0000000000..423f364aea --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/Parent.webidl @@ -0,0 +1,3 @@ +interface Parent { + void MethodFoo(); +}; diff --git a/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl new file mode 100644 index 0000000000..db08562918 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl @@ -0,0 +1,13 @@ +interface EventTarget { + void addEventListener(); +}; + +interface Event {}; + +callback EventHandlerNonNull = any (Event event); +typedef EventHandlerNonNull? EventHandler; + +[NoInterfaceObject] +interface TestEvent : EventTarget { + attribute EventHandler onfoo; +}; diff --git a/dom/bindings/mozwebidlcodegen/test/python.ini b/dom/bindings/mozwebidlcodegen/test/python.ini new file mode 100644 index 0000000000..0bfb2e5580 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/python.ini @@ -0,0 +1,5 @@ +[DEFAULT] +skip-if = python == 2 +subsuite = mozbuild + +[test_mozwebidlcodegen.py] diff --git a/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py new file mode 100644 index 0000000000..c0733e8615 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py @@ -0,0 +1,309 @@ +# 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 imp +import io +import json +import os +import shutil +import sys +import tempfile +import unittest + +import mozpack.path as mozpath + +from mozwebidlcodegen import ( + WebIDLCodegenManager, + WebIDLCodegenManagerState, +) + +from mozfile import NamedTemporaryFile + +from mozunit import ( + MockedOpen, + main, +) + + +OUR_DIR = mozpath.abspath(mozpath.dirname(__file__)) +TOPSRCDIR = mozpath.normpath(mozpath.join(OUR_DIR, "..", "..", "..", "..")) + + +class TestWebIDLCodegenManager(unittest.TestCase): + TEST_STEMS = { + "Child", + "Parent", + "ExampleBinding", + "TestEvent", + } + + @property + def _static_input_paths(self): + s = { + mozpath.join(OUR_DIR, p) + for p in os.listdir(OUR_DIR) + if p.endswith(".webidl") + } + + return s + + @property + def _config_path(self): + config = mozpath.join(TOPSRCDIR, "dom", "bindings", "Bindings.conf") + self.assertTrue(os.path.exists(config)) + + return config + + def _get_manager_args(self): + tmp = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tmp) + + cache_dir = mozpath.join(tmp, "cache") + os.mkdir(cache_dir) + + ip = self._static_input_paths + + inputs = ( + ip, + {mozpath.splitext(mozpath.basename(p))[0] for p in ip}, + set(), + set(), + ) + + return dict( + config_path=self._config_path, + webidl_root=cache_dir, + inputs=inputs, + exported_header_dir=mozpath.join(tmp, "exports"), + codegen_dir=mozpath.join(tmp, "codegen"), + state_path=mozpath.join(tmp, "state.json"), + make_deps_path=mozpath.join(tmp, "codegen.pp"), + make_deps_target="codegen.pp", + cache_dir=cache_dir, + ) + + def _get_manager(self): + return WebIDLCodegenManager(**self._get_manager_args()) + + def test_unknown_state_version(self): + """Loading a state file with a too new version resets state.""" + args = self._get_manager_args() + + p = args["state_path"] + + with io.open(p, "w", newline="\n") as fh: + json.dump( + { + "version": WebIDLCodegenManagerState.VERSION + 1, + "foobar": "1", + }, + fh, + ) + + manager = WebIDLCodegenManager(**args) + + self.assertEqual(manager._state["version"], WebIDLCodegenManagerState.VERSION) + self.assertNotIn("foobar", manager._state) + + def test_generate_build_files(self): + """generate_build_files() does the right thing from empty.""" + manager = self._get_manager() + result = manager.generate_build_files() + self.assertEqual(len(result.inputs), 4) + + output = manager.expected_build_output_files() + self.assertEqual(result.created, output) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.unchanged), 0) + + for f in output: + self.assertTrue(os.path.isfile(f)) + + for f in manager.GLOBAL_DECLARE_FILES: + self.assertIn(mozpath.join(manager._exported_header_dir, f), output) + + for f in manager.GLOBAL_DEFINE_FILES: + self.assertIn(mozpath.join(manager._codegen_dir, f), output) + + for s in self.TEST_STEMS: + self.assertTrue( + os.path.isfile( + mozpath.join(manager._exported_header_dir, "%sBinding.h" % s) + ) + ) + self.assertTrue( + os.path.isfile(mozpath.join(manager._codegen_dir, "%sBinding.cpp" % s)) + ) + + self.assertTrue(os.path.isfile(manager._state_path)) + + with io.open(manager._state_path, "r") as fh: + state = json.load(fh) + self.assertEqual(state["version"], 3) + self.assertIn("webidls", state) + + child = state["webidls"]["Child.webidl"] + self.assertEqual(len(child["inputs"]), 2) + self.assertEqual(len(child["outputs"]), 2) + self.assertEqual(child["sha1"], "c41527cad3bc161fa6e7909e48fa11f9eca0468b") + + def test_generate_build_files_load_state(self): + """State should be equivalent when instantiating a new instance.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + self.assertEqual(len(m1._state["webidls"]), 0) + m1.generate_build_files() + + m2 = WebIDLCodegenManager(**args) + self.assertGreater(len(m2._state["webidls"]), 2) + self.assertEqual(m1._state, m2._state) + + def test_no_change_no_writes(self): + """If nothing changes, no files should be updated.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + + self.assertEqual(len(result.inputs), 0) + self.assertEqual(len(result.created), 0) + self.assertEqual(len(result.updated), 0) + + def test_output_file_regenerated(self): + """If an output file disappears, it is regenerated.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + rm_count = 0 + for p in m1._state["webidls"]["Child.webidl"]["outputs"]: + rm_count += 1 + os.unlink(p) + + for p in m1.GLOBAL_DECLARE_FILES: + rm_count += 1 + os.unlink(mozpath.join(m1._exported_header_dir, p)) + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(len(result.created), rm_count) + + def test_only_rebuild_self(self): + """If an input file changes, only rebuild that one file.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + child_path = None + for p in m1._input_paths: + if p.endswith("Child.webidl"): + child_path = p + break + + self.assertIsNotNone(child_path) + child_content = io.open(child_path, "r").read() + + with MockedOpen({child_path: child_content + "\n/* */"}): + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(result.inputs, set([child_path])) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.created), 0) + + def test_rebuild_dependencies(self): + """Ensure an input file used by others results in others rebuilding.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + parent_path = None + child_path = None + for p in m1._input_paths: + if p.endswith("Parent.webidl"): + parent_path = p + elif p.endswith("Child.webidl"): + child_path = p + + self.assertIsNotNone(parent_path) + parent_content = io.open(parent_path, "r").read() + + with MockedOpen({parent_path: parent_content + "\n/* */"}): + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(result.inputs, {child_path, parent_path}) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.created), 0) + + def test_python_change_regenerate_everything(self): + """If a Python file changes, we should attempt to rebuild everything.""" + + # We don't want to mutate files in the source directory because we want + # to be able to build from a read-only filesystem. So, we install a + # dummy module and rewrite the metadata to say it comes from the source + # directory. + # + # Hacking imp to accept a MockedFile doesn't appear possible. So for + # the first iteration we read from a temp file. The second iteration + # doesn't need to import, so we are fine with a mocked file. + fake_path = mozpath.join(OUR_DIR, "fakemodule.py") + with NamedTemporaryFile("wt") as fh: + fh.write("# Original content") + fh.flush() + mod = imp.load_source("mozwebidlcodegen.fakemodule", fh.name) + mod.__file__ = fake_path + + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + with MockedOpen({fake_path: "# Original content"}): + try: + result = m1.generate_build_files() + l = len(result.inputs) + + with io.open(fake_path, "wt", newline="\n") as fh: + fh.write("# Modified content") + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(len(result.inputs), l) + + result = m2.generate_build_files() + self.assertEqual(len(result.inputs), 0) + finally: + del sys.modules["mozwebidlcodegen.fakemodule"] + + def test_copy_input(self): + """Ensure a copied .webidl file is handled properly.""" + + # This test simulates changing the type of a WebIDL from static to + # preprocessed. In that scenario, the original file still exists but + # it should no longer be consulted during codegen. + + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + old_path = None + for p in args["inputs"][0]: + if p.endswith("Parent.webidl"): + old_path = p + break + self.assertIsNotNone(old_path) + + new_path = mozpath.join(args["cache_dir"], "Parent.webidl") + shutil.copy2(old_path, new_path) + + args["inputs"][0].remove(old_path) + args["inputs"][0].add(new_path) + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(len(result.updated), 0) + + +if __name__ == "__main__": + main() |