| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 shutil | 
|  | 7 import json | 
|  | 8 import re | 
|  | 9 from struct import unpack | 
|  | 10 | 
|  | 11 import pytest | 
|  | 12 from Crypto.Hash import SHA | 
|  | 13 from Crypto.PublicKey import RSA | 
|  | 14 from Crypto.Signature import PKCS1_v1_5 | 
|  | 15 | 
|  | 16 | 
|  | 17 from buildtools import packager | 
|  | 18 from buildtools.tests.tools import (DirContent, ZipContent, copy_metadata, | 
|  | 19                                     run_webext_build, assert_manifest_content, | 
|  | 20                                     assert_all_locales_present, locale_files) | 
|  | 21 from buildtools.tests.conftest import ALL_LANGUAGES | 
|  | 22 | 
|  | 23 | 
|  | 24 LOCALES_MODULE = { | 
|  | 25     'test.Foobar': { | 
|  | 26         'message': 'Ensuring dict-copy from modules for $domain$', | 
|  | 27         'description': 'test description', | 
|  | 28         'placeholders': {'content': '$1', 'example': 'www.adblockplus.org'} | 
|  | 29     } | 
|  | 30 } | 
|  | 31 | 
|  | 32 DTD_TEST = ('<!ENTITY access.key "access key(&a)">' | 
|  | 33             '<!ENTITY ampersand "foo &-bar">') | 
|  | 34 | 
|  | 35 PROPERTIES_TEST = 'description=very descriptive!' | 
|  | 36 | 
|  | 37 | 
|  | 38 @pytest.fixture | 
|  | 39 def gecko_import(tmpdir): | 
|  | 40     tmpdir.mkdir('_imp').mkdir('en-US').join('gecko.dtd').write(DTD_TEST) | 
|  | 41 | 
|  | 42 | 
|  | 43 @pytest.fixture | 
|  | 44 def locale_modules(tmpdir): | 
|  | 45     mod_dir = tmpdir.mkdir('_modules') | 
|  | 46     lang_dir = mod_dir.mkdir('en-US') | 
|  | 47     lang_dir.join('module.json').write(json.dumps(LOCALES_MODULE)) | 
|  | 48     lang_dir.join('unit.properties').write(json.dumps(PROPERTIES_TEST)) | 
|  | 49 | 
|  | 50 | 
|  | 51 @pytest.fixture | 
|  | 52 def icons(srcdir): | 
|  | 53     icons_dir = srcdir.mkdir('icons') | 
|  | 54     for filename in ['abp-16.png', 'abp-19.png', 'abp-53.png']: | 
|  | 55         shutil.copy( | 
|  | 56             os.path.join(os.path.dirname(__file__), filename), | 
|  | 57             os.path.join(str(icons_dir), filename), | 
|  | 58         ) | 
|  | 59 | 
|  | 60 | 
|  | 61 @pytest.fixture | 
|  | 62 def all_lang_locales(tmpdir): | 
|  | 63     return locale_files(ALL_LANGUAGES, '_locales', tmpdir) | 
|  | 64 | 
|  | 65 | 
|  | 66 @pytest.fixture | 
|  | 67 def chrome_metadata(tmpdir): | 
|  | 68     filename = 'metadata.chrome' | 
|  | 69     copy_metadata(filename, tmpdir) | 
|  | 70 | 
|  | 71 | 
|  | 72 @pytest.fixture | 
|  | 73 def gecko_webext_metadata(tmpdir, chrome_metadata): | 
|  | 74     filename = 'metadata.gecko-webext' | 
|  | 75     copy_metadata(filename, tmpdir) | 
|  | 76 | 
|  | 77 | 
|  | 78 @pytest.fixture | 
|  | 79 def keyfile(tmpdir): | 
|  | 80     """Test-privatekey for signing chrome release-package""" | 
|  | 81     return os.path.join(os.path.dirname(__file__), 'chrome_rsa.pem') | 
|  | 82 | 
|  | 83 | 
|  | 84 @pytest.fixture | 
|  | 85 def lib_files(tmpdir): | 
|  | 86     files = packager.Files(['lib'], set()) | 
|  | 87     files['ext/a.js'] = 'var bar;' | 
|  | 88     files['lib/b.js'] = 'var foo;' | 
|  | 89 | 
|  | 90     tmpdir.mkdir('lib').join('b.js').write(files['lib/b.js']) | 
|  | 91     tmpdir.mkdir('ext').join('a.js').write(files['ext/a.js']) | 
|  | 92 | 
|  | 93     return files | 
|  | 94 | 
|  | 95 | 
|  | 96 def assert_gecko_locale_conversion(package): | 
|  | 97     locale = json.loads(package.read('_locales/en_US/messages.json')) | 
|  | 98 | 
|  | 99     assert locale.get('test_Foobar', {}) == LOCALES_MODULE['test.Foobar'] | 
|  | 100     assert locale.get('access_key', {}) == {'message': 'access key'} | 
|  | 101     assert locale.get('ampersand', {}) == {'message': 'foo -bar'} | 
|  | 102     assert locale.get('_description', {}) == {'message': 'very descriptive!"'} | 
|  | 103 | 
|  | 104 | 
|  | 105 def assert_convert_js(package, excluded=False): | 
|  | 106     libfoo = package.read('lib/foo.js') | 
|  | 107 | 
|  | 108     assert 'var bar;' in libfoo | 
|  | 109     assert 'require.modules["ext_a"]' in libfoo | 
|  | 110 | 
|  | 111     assert ('var foo;' in libfoo) != excluded | 
|  | 112     assert ('require.modules["b"]' in libfoo) != excluded | 
|  | 113 | 
|  | 114 | 
|  | 115 def assert_devenv_scripts(package, devenv): | 
|  | 116     manifest = json.loads(package.read('manifest.json')) | 
|  | 117     filenames = package.namelist() | 
|  | 118     scripts = [ | 
|  | 119             'ext/common.js', | 
|  | 120             'ext/background.js', | 
|  | 121     ] | 
|  | 122 | 
|  | 123     assert ('qunit/index.html' in filenames) == devenv | 
|  | 124     assert ('devenvPoller__.js' in filenames) == devenv | 
|  | 125     assert ('devenvVersion__' in filenames) == devenv | 
|  | 126 | 
|  | 127     if devenv: | 
|  | 128         assert '../ext/common.js' in package.read('qunit/index.html') | 
|  | 129         assert '../ext/background.js' in package.read('qunit/index.html') | 
|  | 130 | 
|  | 131         assert set(manifest['background']['scripts']) == set( | 
|  | 132             scripts + ['devenvPoller__.js'] | 
|  | 133         ) | 
|  | 134     else: | 
|  | 135         assert set(manifest['background']['scripts']) == set(scripts) | 
|  | 136 | 
|  | 137 | 
|  | 138 def assert_base_files(package): | 
|  | 139     filenames = set(package.namelist()) | 
|  | 140 | 
|  | 141     assert 'bar.json' in filenames | 
|  | 142     assert 'manifest.json' in filenames | 
|  | 143     assert 'lib/foo.js' in filenames | 
|  | 144     assert 'foo/logo_50.png' in filenames | 
|  | 145     assert 'icons/logo_150.png' in filenames | 
|  | 146 | 
|  | 147 | 
|  | 148 def assert_chrome_signature(filename, keyfile): | 
|  | 149     with open(filename, 'r') as fp: | 
|  | 150         content = fp.read() | 
|  | 151 | 
|  | 152     _, _, l_pubkey, l_signature = unpack('<4sIII', content[:16]) | 
|  | 153     signature = content[16 + l_pubkey: 16 + l_pubkey + l_signature] | 
|  | 154 | 
|  | 155     digest = SHA.new() | 
|  | 156     with open(keyfile, 'r') as fp: | 
|  | 157         rsa_key = RSA.importKey(fp.read()) | 
|  | 158 | 
|  | 159     signer = PKCS1_v1_5.new(rsa_key) | 
|  | 160 | 
|  | 161     digest.update(content[16 + l_pubkey + l_signature:]) | 
|  | 162     assert signer.verify(digest, signature) | 
|  | 163 | 
|  | 164 | 
|  | 165 def assert_locale_upfix(package): | 
|  | 166     translations = [ | 
|  | 167         json.loads(package.read('_locales/{}/messages.json'.format(lang))) | 
|  | 168         for lang in ALL_LANGUAGES | 
|  | 169     ] | 
|  | 170 | 
|  | 171     manifest = package.read('manifest.json') | 
|  | 172 | 
|  | 173     # Chrome Web Store requires descriptive translations to be present in | 
|  | 174     # every language. | 
|  | 175     for match in re.finditer(r'__MSG_(\S+)__', manifest): | 
|  | 176         name = match.group(1) | 
|  | 177 | 
|  | 178         for other in translations[1:]: | 
|  | 179             assert translations[0][name]['message'] == other[name]['message'] | 
|  | 180 | 
|  | 181 | 
|  | 182 @pytest.mark.usefixtures( | 
|  | 183     'all_lang_locales', | 
|  | 184     'locale_modules', | 
|  | 185     'gecko_import', | 
|  | 186     'icons', | 
|  | 187     'lib_files', | 
|  | 188     'chrome_metadata', | 
|  | 189     'gecko_webext_metadata', | 
|  | 190 ) | 
|  | 191 @pytest.mark.parametrize('platform', ['chrome', 'gecko-webext']) | 
|  | 192 @pytest.mark.parametrize('dev_build_release,buildnum', [ | 
|  | 193     ('build', '1337'), | 
|  | 194     ('devenv', None), | 
|  | 195     ('release', None), | 
|  | 196 ]) | 
|  | 197 def test_build_webext(platform, dev_build_release, keyfile, tmpdir, srcdir, | 
|  | 198                       buildnum, capsys): | 
|  | 199     release = dev_build_release == 'release' | 
|  | 200     devenv = dev_build_release == 'devenv' | 
|  | 201 | 
|  | 202     if platform == 'chrome' and release: | 
|  | 203         key = keyfile | 
|  | 204     else: | 
|  | 205         key = None | 
|  | 206 | 
|  | 207     filenames = { | 
|  | 208         'gecko-webext': 'adblockplusfirefox-1.2.3{}.xpi', | 
|  | 209         'chrome': 'adblockpluschrome-1.2.3{{}}.{}'.format( | 
|  | 210             {True: 'crx', False: 'zip'}[release] | 
|  | 211         ), | 
|  | 212     } | 
|  | 213 | 
|  | 214     expected_manifest = os.path.join( | 
|  | 215         os.path.dirname(__file__), | 
|  | 216         'expecteddata', | 
|  | 217         'manifest_{}_{}.json'.format(platform, dev_build_release), | 
|  | 218     ) | 
|  | 219 | 
|  | 220     run_webext_build(platform, dev_build_release, srcdir, buildnum=buildnum, | 
|  | 221                      keyfile=key) | 
|  | 222 | 
|  | 223     # The makeIcons() in packagerChrome.py should warn about non-square | 
|  | 224     # icons via stderr. | 
|  | 225     out, err = capsys.readouterr() | 
|  | 226     assert 'icon should be square' in err | 
|  | 227 | 
|  | 228     if devenv: | 
|  | 229         content_class = DirContent | 
|  | 230         out_file_path = os.path.join(str(srcdir), 'devenv.' + platform) | 
|  | 231     else: | 
|  | 232         content_class = ZipContent | 
|  | 233 | 
|  | 234         if release: | 
|  | 235             add_version = '' | 
|  | 236         else: | 
|  | 237             add_version = '.' + buildnum | 
|  | 238 | 
|  | 239         out_file = filenames[platform].format(add_version) | 
|  | 240 | 
|  | 241         out_file_path = os.path.abspath(os.path.join( | 
|  | 242             os.path.dirname(__file__), os.pardir, out_file)) | 
|  | 243         assert os.path.exists(out_file_path) | 
|  | 244 | 
|  | 245     if release and platform == 'chrome': | 
|  | 246         assert_chrome_signature(out_file_path, keyfile) | 
|  | 247 | 
|  | 248     with content_class(out_file_path) as package: | 
|  | 249         assert_manifest_content( | 
|  | 250             package.read('manifest.json'), expected_manifest | 
|  | 251         ) | 
|  | 252         assert_base_files(package) | 
|  | 253         assert_devenv_scripts(package, devenv) | 
|  | 254         assert_all_locales_present(package, '_locales') | 
|  | 255         assert_gecko_locale_conversion(package) | 
|  | 256         assert_convert_js(package, platform == 'gecko-webext') | 
|  | 257 | 
|  | 258         if platform == 'chrome': | 
|  | 259             assert_locale_upfix(package) | 
| OLD | NEW | 
|---|