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 re |
| 6 import os |
| 7 import json |
| 8 import zipfile |
| 9 |
| 10 from xml.etree import ElementTree |
| 11 from Crypto.Hash import SHA |
| 12 import pytest |
| 13 |
| 14 from buildtools import packager, packagerChrome |
| 15 from buildtools.tests.tools import copy_metadata |
| 16 |
| 17 ICON_SIZES = [32, 48, 64, 256, 53] |
| 18 |
| 19 GECKO_DEV_UPDATE_URL = ( |
| 20 'https://downloads.adblockplus.org/devbuilds/' |
| 21 'adblockplusfirefox/updates.json' |
| 22 ) |
| 23 |
| 24 MINIMUM_MANIFEST = { |
| 25 'manifest_version': 2, |
| 26 'author': 'Eyeo GmbH', |
| 27 'permissions': [ |
| 28 'tabs', |
| 29 '<all_urls>', |
| 30 'contextMenus', |
| 31 'webRequest', |
| 32 'webRequestBlocking', |
| 33 'webNavigation', |
| 34 'storage', |
| 35 'unlimitedStorage', |
| 36 'notifications', |
| 37 ], |
| 38 'minimum_chrome_version': '49.0', |
| 39 'minimum_opera_version': '36.0', |
| 40 'devtools_page': 'devtools.html', |
| 41 'description': '__MSG_description__', |
| 42 'default_locale': 'en_US', |
| 43 'short_name': '__MSG_name__', |
| 44 'storage': {'managed_schema': 'managed-storage-schema.json'}, |
| 45 'options_ui': {'open_in_tab': True, 'page': 'options.html'}, |
| 46 } |
| 47 |
| 48 |
| 49 @pytest.fixture |
| 50 def lib_files(tmpdir): |
| 51 files = packager.Files(['lib'], set()) |
| 52 files['lib/a.js'] = 'var bar;' |
| 53 files['lib/b.js'] = 'var foo;' |
| 54 |
| 55 lib_dir = tmpdir.mkdir('lib') |
| 56 |
| 57 lib_dir.join('a.js').write(files['lib/a.js']) |
| 58 lib_dir.join('b.js').write(files['lib/b.js']) |
| 59 return files |
| 60 |
| 61 |
| 62 @pytest.fixture |
| 63 def chrome_metadata(tmpdir): |
| 64 filename = 'metadata.chrome' |
| 65 copy_metadata(filename, tmpdir) |
| 66 |
| 67 return packager.readMetadata(str(tmpdir), 'chrome') |
| 68 |
| 69 |
| 70 @pytest.fixture |
| 71 def gecko_webext_metadata(tmpdir, chrome_metadata): |
| 72 filename = 'metadata.gecko-webext' |
| 73 copy_metadata(filename, tmpdir) |
| 74 |
| 75 return packager.readMetadata(str(tmpdir), 'gecko-webext') |
| 76 |
| 77 |
| 78 @pytest.fixture |
| 79 def icons(tmpdir): |
| 80 """Valid .png files for testing make_icons""" |
| 81 from PIL import Image |
| 82 |
| 83 paths = [] |
| 84 icon_dir = tmpdir.mkdir('tmp_icons') |
| 85 |
| 86 for size in ICON_SIZES: |
| 87 img_path = str(icon_dir.join('abp-{}.png'.format(size))) |
| 88 img = Image.new('1', (size, size if size != 53 else size + 1), 0) |
| 89 img.save(img_path) |
| 90 paths.append(img_path) |
| 91 |
| 92 return paths |
| 93 |
| 94 |
| 95 @pytest.fixture |
| 96 def keyfile(tmpdir): |
| 97 """Test-privatekey for signing files""" |
| 98 return os.path.join(os.path.dirname(__file__), 'chrome_rsa.pem') |
| 99 |
| 100 |
| 101 @pytest.mark.parametrize('release', [True, False]) |
| 102 @pytest.mark.parametrize('devenv', [True, False]) |
| 103 @pytest.mark.parametrize('ext_type', ['chrome', 'gecko-webext']) |
| 104 def test_create_manifest(srcdir, base_files, release, devenv, ext_type, |
| 105 chrome_metadata, gecko_webext_metadata): |
| 106 |
| 107 if ext_type == 'chrome': |
| 108 metadata = chrome_metadata |
| 109 else: |
| 110 metadata = gecko_webext_metadata |
| 111 |
| 112 version = packager.getBuildVersion(str(srcdir), metadata, release) |
| 113 params = { |
| 114 'type': ext_type, |
| 115 'baseDir': str(srcdir), |
| 116 'releaseBuild': release, |
| 117 'version': version, |
| 118 'devenv': devenv, |
| 119 'metadata': metadata, |
| 120 } |
| 121 manifest = json.loads(packagerChrome.createManifest(params, base_files)) |
| 122 |
| 123 if ext_type == 'gecko-webext': |
| 124 gecko_app = manifest['applications']['gecko'] |
| 125 |
| 126 assert gecko_app['id'] == '{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}' |
| 127 assert gecko_app['strict_min_version'] == '50.0' |
| 128 if not release: |
| 129 assert gecko_app['update_url'] == GECKO_DEV_UPDATE_URL |
| 130 |
| 131 if release: |
| 132 assert manifest['name'] == '__MSG_name__' |
| 133 assert manifest['version'] == '1.2.3' |
| 134 else: |
| 135 assert manifest['name'] == '__MSG_name_devbuild__' |
| 136 assert manifest['version'] == '1.2.3.0' |
| 137 |
| 138 for key, value in MINIMUM_MANIFEST.items(): |
| 139 assert manifest.get(key, None) == value |
| 140 |
| 141 |
| 142 def test_sign_binary(base_files, keyfile): |
| 143 from Crypto.PublicKey import RSA |
| 144 from Crypto.Signature import PKCS1_v1_5 |
| 145 |
| 146 digest = SHA.new() |
| 147 |
| 148 signature = packagerChrome.signBinary(base_files.zipToString(), keyfile) |
| 149 |
| 150 with open(keyfile, 'r') as fp: |
| 151 rsa_key = RSA.importKey(fp.read()) |
| 152 |
| 153 signer = PKCS1_v1_5.new(rsa_key) |
| 154 |
| 155 digest.update(base_files.zipToString()) |
| 156 assert signer.verify(digest, signature) |
| 157 |
| 158 |
| 159 def test_get_public_key(keyfile): |
| 160 expected_hash = '8dfe9486446eb4da8ce15dd18f5dfd6a776c2674' |
| 161 |
| 162 publickey = packagerChrome.getPublicKey(keyfile) |
| 163 assert SHA.new(publickey).hexdigest() == expected_hash |
| 164 |
| 165 |
| 166 @pytest.mark.usefixtures('chrome_metadata') |
| 167 @pytest.mark.parametrize('devenv', [True, False]) |
| 168 def test_get_package_files(srcdir, devenv): |
| 169 filenames = [ |
| 170 'foo.html', |
| 171 'foo.js', |
| 172 'foo.json', |
| 173 'foo.xml', |
| 174 ] |
| 175 |
| 176 for filename in filenames: |
| 177 srcdir.join(filename).write('', ensure=True) |
| 178 |
| 179 params = { |
| 180 'baseDir': str(srcdir), |
| 181 'devenv': devenv, |
| 182 } |
| 183 |
| 184 files = packagerChrome.getPackageFiles(params) |
| 185 |
| 186 assert ('qunit' in files) == devenv |
| 187 |
| 188 for expected in ['icons', 'ui', 'skin', 'ext', '_locales', 'lib', |
| 189 'jquery-ui'] + [name for name in filenames]: |
| 190 assert expected in files |
| 191 |
| 192 |
| 193 def test_make_icons(icons, capsys): |
| 194 files = packager.Files(set(), set()) |
| 195 for path in icons: |
| 196 files[path] = open(path, 'r').read() |
| 197 result = packagerChrome.makeIcons(files, icons) |
| 198 |
| 199 out, err = capsys.readouterr() |
| 200 |
| 201 assert 'should be square' in err |
| 202 assert set(result) == set(ICON_SIZES) |
| 203 |
| 204 |
| 205 def test_create_script_page(srcdir, chrome_metadata): |
| 206 params = {'metadata': chrome_metadata} |
| 207 |
| 208 template = packagerChrome.createScriptPage( |
| 209 params, 'testIndex.html.tmpl', ('general', 'testScripts') |
| 210 ) |
| 211 |
| 212 template_scripts = {elem.attrib['src'] for elem in |
| 213 ElementTree.fromstring(template).iter('script')} |
| 214 |
| 215 for src in chrome_metadata.get('general', 'testScripts').split(): |
| 216 assert src in template_scripts |
| 217 |
| 218 |
| 219 def test_convert_js(srcdir, chrome_metadata, lib_files): |
| 220 params = { |
| 221 'type': 'chrome', |
| 222 'metadata': chrome_metadata, |
| 223 } |
| 224 |
| 225 packagerChrome.convertJS(params, lib_files) |
| 226 source = lib_files['lib/foo.js'] |
| 227 |
| 228 assert 'var foo;' in source |
| 229 assert 'var bar;' in source |
| 230 |
| 231 |
| 232 def test_to_json(): |
| 233 data = {'bar': ['foo', 'foobar', 1, True]} |
| 234 data_json = packagerChrome.toJson(data) |
| 235 |
| 236 assert data_json.endswith('\n') |
| 237 assert json.loads(data_json) == data |
| 238 |
| 239 |
| 240 def test_import_gecko_locales(tmpdir, srcdir, base_files, chrome_metadata): |
| 241 version = packager.getBuildVersion(str(srcdir), chrome_metadata, False) |
| 242 params = { |
| 243 'type': 'chrome', |
| 244 'baseDir': str(srcdir), |
| 245 'releaseBuild': False, |
| 246 'version': version, |
| 247 'devenv': False, |
| 248 'metadata': chrome_metadata, |
| 249 } |
| 250 |
| 251 data = {'name': 'foo', 'name_devbuild': 'bar'} |
| 252 |
| 253 t_dir = tmpdir.mkdir('_trans').mkdir('en-US') |
| 254 t_dir.join('test.properties').write( |
| 255 '\n'.join('{}={}'.format(k, v) for k, v in data.items())) |
| 256 |
| 257 packagerChrome.importGeckoLocales(params, base_files) |
| 258 trans_data = json.loads(base_files['_locales/en_US/messages.json']) |
| 259 |
| 260 for key, value in data.items(): |
| 261 assert trans_data.get('test_' + key, {}).get('message', '') == value |
| 262 |
| 263 |
| 264 def test_fix_translations_for_cws(base_files): |
| 265 base_files['_locales/de/messages.json'] = packagerChrome.toJson({}) |
| 266 base_files['manifest.json'] = packagerChrome.toJson(MINIMUM_MANIFEST) |
| 267 packagerChrome.fixTranslationsForCWS(base_files) |
| 268 |
| 269 ger_messages = json.loads(base_files['_locales/de/messages.json']) |
| 270 eng_messages = json.loads(base_files['_locales/en_US/messages.json']) |
| 271 |
| 272 # Check for inserted translations, none were present in 'de' beforehand |
| 273 for match in re.finditer(r'__MSG_(\S+)__', base_files['manifest.json']): |
| 274 name = match.group(1) |
| 275 assert ger_messages[name]['message'] == eng_messages[name]['message'] |
| 276 |
| 277 # CWS enforces max-lengths on string |
| 278 assert len(ger_messages['description']['message']) <= 132 |
| 279 assert len(ger_messages['name']['message']) <= 12 |
| 280 |
| 281 |
| 282 @pytest.mark.usefixtures('base_files', 'lib_files', |
| 283 'gecko_webext_metadata', 'chrome_metadata') |
| 284 @pytest.mark.parametrize('release', [True, False]) |
| 285 @pytest.mark.parametrize('devenv', [True, False]) |
| 286 @pytest.mark.parametrize('ext_type', ['chrome', 'gecko-webext']) |
| 287 @pytest.mark.parametrize('key', [True, False]) |
| 288 def test_create_build(srcdir, tmpdir, release, devenv, ext_type, |
| 289 key, keyfile): |
| 290 |
| 291 basefiles = [ |
| 292 'manifest.json', |
| 293 'lib/foo.js', |
| 294 'foo/logo_50.png', |
| 295 '_locales/en_US/messages.json', |
| 296 '_locales/en-US/test.properties', |
| 297 'logo_44.png', |
| 298 'icons/logo_150.png', |
| 299 ] |
| 300 |
| 301 out_file = str(tmpdir.join('out')) |
| 302 |
| 303 packagerChrome.createBuild( |
| 304 outFile=out_file, |
| 305 baseDir=str(srcdir), |
| 306 type=ext_type, |
| 307 releaseBuild=release, |
| 308 keyFile=keyfile if key else None, |
| 309 devenv=devenv) |
| 310 |
| 311 with zipfile.ZipFile(out_file, 'r') as zipfp: |
| 312 zipfp.testzip() |
| 313 |
| 314 filenames = [zipinfo.filename for zipinfo in zipfp.infolist()] |
| 315 for filename in basefiles if not devenv else basefiles + [ |
| 316 'devenvPoller__.js', |
| 317 'devenvVersion__', |
| 318 'qunit/index.html', |
| 319 ]: |
| 320 assert filename in filenames |
OLD | NEW |