1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,307 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +from __future__ import unicode_literals 1.9 + 1.10 +import imp 1.11 +import json 1.12 +import os 1.13 +import shutil 1.14 +import sys 1.15 +import tempfile 1.16 +import unittest 1.17 + 1.18 +import mozpack.path as mozpath 1.19 + 1.20 +from mozwebidlcodegen import ( 1.21 + WebIDLCodegenManager, 1.22 + WebIDLCodegenManagerState, 1.23 +) 1.24 + 1.25 +from mozfile import NamedTemporaryFile 1.26 + 1.27 +from mozunit import ( 1.28 + MockedOpen, 1.29 + main, 1.30 +) 1.31 + 1.32 + 1.33 +OUR_DIR = mozpath.abspath(mozpath.dirname(__file__)) 1.34 +TOPSRCDIR = mozpath.normpath(mozpath.join(OUR_DIR, '..', '..', '..', '..')) 1.35 + 1.36 + 1.37 +class TestWebIDLCodegenManager(unittest.TestCase): 1.38 + TEST_STEMS = { 1.39 + 'Child', 1.40 + 'Parent', 1.41 + 'ExampleBinding', 1.42 + 'TestEvent', 1.43 + } 1.44 + 1.45 + @property 1.46 + def _static_input_paths(self): 1.47 + s = {mozpath.join(OUR_DIR, p) for p in os.listdir(OUR_DIR) 1.48 + if p.endswith('.webidl')} 1.49 + 1.50 + return s 1.51 + 1.52 + @property 1.53 + def _config_path(self): 1.54 + config = mozpath.join(TOPSRCDIR, 'dom', 'bindings', 'Bindings.conf') 1.55 + self.assertTrue(os.path.exists(config)) 1.56 + 1.57 + return config 1.58 + 1.59 + def _get_manager_args(self): 1.60 + tmp = tempfile.mkdtemp() 1.61 + self.addCleanup(shutil.rmtree, tmp) 1.62 + 1.63 + cache_dir = mozpath.join(tmp, 'cache') 1.64 + os.mkdir(cache_dir) 1.65 + 1.66 + ip = self._static_input_paths 1.67 + 1.68 + inputs = ( 1.69 + ip, 1.70 + {mozpath.splitext(mozpath.basename(p))[0] for p in ip}, 1.71 + set(), 1.72 + set(), 1.73 + ) 1.74 + 1.75 + return dict( 1.76 + config_path=self._config_path, 1.77 + inputs=inputs, 1.78 + exported_header_dir=mozpath.join(tmp, 'exports'), 1.79 + codegen_dir=mozpath.join(tmp, 'codegen'), 1.80 + state_path=mozpath.join(tmp, 'state.json'), 1.81 + make_deps_path=mozpath.join(tmp, 'codegen.pp'), 1.82 + make_deps_target='codegen.pp', 1.83 + cache_dir=cache_dir, 1.84 + ) 1.85 + 1.86 + def _get_manager(self): 1.87 + return WebIDLCodegenManager(**self._get_manager_args()) 1.88 + 1.89 + def test_unknown_state_version(self): 1.90 + """Loading a state file with a too new version resets state.""" 1.91 + args = self._get_manager_args() 1.92 + 1.93 + p = args['state_path'] 1.94 + 1.95 + with open(p, 'wb') as fh: 1.96 + json.dump({ 1.97 + 'version': WebIDLCodegenManagerState.VERSION + 1, 1.98 + 'foobar': '1', 1.99 + }, fh) 1.100 + 1.101 + manager = WebIDLCodegenManager(**args) 1.102 + 1.103 + self.assertEqual(manager._state['version'], 1.104 + WebIDLCodegenManagerState.VERSION) 1.105 + self.assertNotIn('foobar', manager._state) 1.106 + 1.107 + def test_generate_build_files(self): 1.108 + """generate_build_files() does the right thing from empty.""" 1.109 + manager = self._get_manager() 1.110 + result = manager.generate_build_files() 1.111 + self.assertEqual(len(result.inputs), 5) 1.112 + 1.113 + output = manager.expected_build_output_files() 1.114 + self.assertEqual(result.created, output) 1.115 + self.assertEqual(len(result.updated), 0) 1.116 + self.assertEqual(len(result.unchanged), 0) 1.117 + 1.118 + for f in output: 1.119 + self.assertTrue(os.path.isfile(f)) 1.120 + 1.121 + for f in manager.GLOBAL_DECLARE_FILES: 1.122 + self.assertIn(mozpath.join(manager._exported_header_dir, f), output) 1.123 + 1.124 + for f in manager.GLOBAL_DEFINE_FILES: 1.125 + self.assertIn(mozpath.join(manager._codegen_dir, f), output) 1.126 + 1.127 + for s in self.TEST_STEMS: 1.128 + self.assertTrue(os.path.isfile(mozpath.join( 1.129 + manager._exported_header_dir, '%sBinding.h' % s))) 1.130 + self.assertTrue(os.path.isfile(mozpath.join( 1.131 + manager._codegen_dir, '%sBinding.cpp' % s))) 1.132 + 1.133 + self.assertTrue(os.path.isfile(manager._state_path)) 1.134 + 1.135 + with open(manager._state_path, 'rb') as fh: 1.136 + state = json.load(fh) 1.137 + self.assertEqual(state['version'], 1) 1.138 + self.assertIn('webidls', state) 1.139 + 1.140 + child = state['webidls']['Child.webidl'] 1.141 + self.assertEqual(len(child['inputs']), 2) 1.142 + self.assertEqual(len(child['outputs']), 2) 1.143 + self.assertEqual(child['sha1'], 'c41527cad3bc161fa6e7909e48fa11f9eca0468b') 1.144 + 1.145 + def test_generate_build_files_load_state(self): 1.146 + """State should be equivalent when instantiating a new instance.""" 1.147 + args = self._get_manager_args() 1.148 + m1 = WebIDLCodegenManager(**args) 1.149 + self.assertEqual(len(m1._state['webidls']), 0) 1.150 + m1.generate_build_files() 1.151 + 1.152 + m2 = WebIDLCodegenManager(**args) 1.153 + self.assertGreater(len(m2._state['webidls']), 2) 1.154 + self.assertEqual(m1._state, m2._state) 1.155 + 1.156 + def test_no_change_no_writes(self): 1.157 + """If nothing changes, no files should be updated.""" 1.158 + args = self._get_manager_args() 1.159 + m1 = WebIDLCodegenManager(**args) 1.160 + m1.generate_build_files() 1.161 + 1.162 + m2 = WebIDLCodegenManager(**args) 1.163 + result = m2.generate_build_files() 1.164 + 1.165 + self.assertEqual(len(result.inputs), 0) 1.166 + self.assertEqual(len(result.created), 0) 1.167 + self.assertEqual(len(result.updated), 0) 1.168 + 1.169 + def test_output_file_regenerated(self): 1.170 + """If an output file disappears, it is regenerated.""" 1.171 + args = self._get_manager_args() 1.172 + m1 = WebIDLCodegenManager(**args) 1.173 + m1.generate_build_files() 1.174 + 1.175 + rm_count = 0 1.176 + for p in m1._state['webidls']['Child.webidl']['outputs']: 1.177 + rm_count += 1 1.178 + os.unlink(p) 1.179 + 1.180 + for p in m1.GLOBAL_DECLARE_FILES: 1.181 + rm_count += 1 1.182 + os.unlink(mozpath.join(m1._exported_header_dir, p)) 1.183 + 1.184 + m2 = WebIDLCodegenManager(**args) 1.185 + result = m2.generate_build_files() 1.186 + self.assertEqual(len(result.created), rm_count) 1.187 + 1.188 + def test_only_rebuild_self(self): 1.189 + """If an input file changes, only rebuild that one file.""" 1.190 + args = self._get_manager_args() 1.191 + m1 = WebIDLCodegenManager(**args) 1.192 + m1.generate_build_files() 1.193 + 1.194 + child_path = None 1.195 + for p in m1._input_paths: 1.196 + if p.endswith('Child.webidl'): 1.197 + child_path = p 1.198 + break 1.199 + 1.200 + self.assertIsNotNone(child_path) 1.201 + child_content = open(child_path, 'rb').read() 1.202 + 1.203 + with MockedOpen({child_path: child_content + '\n/* */'}): 1.204 + m2 = WebIDLCodegenManager(**args) 1.205 + result = m2.generate_build_files() 1.206 + self.assertEqual(result.inputs, set([child_path])) 1.207 + self.assertEqual(len(result.updated), 0) 1.208 + self.assertEqual(len(result.created), 0) 1.209 + 1.210 + def test_rebuild_dependencies(self): 1.211 + """Ensure an input file used by others results in others rebuilding.""" 1.212 + args = self._get_manager_args() 1.213 + m1 = WebIDLCodegenManager(**args) 1.214 + m1.generate_build_files() 1.215 + 1.216 + parent_path = None 1.217 + child_path = None 1.218 + for p in m1._input_paths: 1.219 + if p.endswith('Parent.webidl'): 1.220 + parent_path = p 1.221 + elif p.endswith('Child.webidl'): 1.222 + child_path = p 1.223 + 1.224 + self.assertIsNotNone(parent_path) 1.225 + parent_content = open(parent_path, 'rb').read() 1.226 + 1.227 + with MockedOpen({parent_path: parent_content + '\n/* */'}): 1.228 + m2 = WebIDLCodegenManager(**args) 1.229 + result = m2.generate_build_files() 1.230 + self.assertEqual(result.inputs, {child_path, parent_path}) 1.231 + self.assertEqual(len(result.updated), 0) 1.232 + self.assertEqual(len(result.created), 0) 1.233 + 1.234 + def test_python_change_regenerate_everything(self): 1.235 + """If a Python file changes, we should attempt to rebuild everything.""" 1.236 + 1.237 + # We don't want to mutate files in the source directory because we want 1.238 + # to be able to build from a read-only filesystem. So, we install a 1.239 + # dummy module and rewrite the metadata to say it comes from the source 1.240 + # directory. 1.241 + # 1.242 + # Hacking imp to accept a MockedFile doesn't appear possible. So for 1.243 + # the first iteration we read from a temp file. The second iteration 1.244 + # doesn't need to import, so we are fine with a mocked file. 1.245 + fake_path = mozpath.join(OUR_DIR, 'fakemodule.py') 1.246 + with NamedTemporaryFile('wt') as fh: 1.247 + fh.write('# Original content') 1.248 + fh.flush() 1.249 + mod = imp.load_source('mozwebidlcodegen.fakemodule', fh.name) 1.250 + mod.__file__ = fake_path 1.251 + 1.252 + args = self._get_manager_args() 1.253 + m1 = WebIDLCodegenManager(**args) 1.254 + with MockedOpen({fake_path: '# Original content'}): 1.255 + old_exists = os.path.exists 1.256 + try: 1.257 + def exists(p): 1.258 + if p == fake_path: 1.259 + return True 1.260 + return old_exists(p) 1.261 + 1.262 + os.path.exists = exists 1.263 + 1.264 + result = m1.generate_build_files() 1.265 + l = len(result.inputs) 1.266 + 1.267 + with open(fake_path, 'wt') as fh: 1.268 + fh.write('# Modified content') 1.269 + 1.270 + m2 = WebIDLCodegenManager(**args) 1.271 + result = m2.generate_build_files() 1.272 + self.assertEqual(len(result.inputs), l) 1.273 + 1.274 + result = m2.generate_build_files() 1.275 + self.assertEqual(len(result.inputs), 0) 1.276 + finally: 1.277 + os.path.exists = old_exists 1.278 + del sys.modules['mozwebidlcodegen.fakemodule'] 1.279 + 1.280 + def test_copy_input(self): 1.281 + """Ensure a copied .webidl file is handled properly.""" 1.282 + 1.283 + # This test simulates changing the type of a WebIDL from static to 1.284 + # preprocessed. In that scenario, the original file still exists but 1.285 + # it should no longer be consulted during codegen. 1.286 + 1.287 + args = self._get_manager_args() 1.288 + m1 = WebIDLCodegenManager(**args) 1.289 + m1.generate_build_files() 1.290 + 1.291 + old_path = None 1.292 + for p in args['inputs'][0]: 1.293 + if p.endswith('Parent.webidl'): 1.294 + old_path = p 1.295 + break 1.296 + self.assertIsNotNone(old_path) 1.297 + 1.298 + new_path = mozpath.join(args['cache_dir'], 'Parent.webidl') 1.299 + shutil.copy2(old_path, new_path) 1.300 + 1.301 + args['inputs'][0].remove(old_path) 1.302 + args['inputs'][0].add(new_path) 1.303 + 1.304 + m2 = WebIDLCodegenManager(**args) 1.305 + result = m2.generate_build_files() 1.306 + self.assertEqual(len(result.updated), 0) 1.307 + 1.308 + 1.309 +if __name__ == '__main__': 1.310 + main()