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