| Left: | ||
| Right: |
| 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 json | |
| 7 import zipfile | |
| 8 | |
| 9 from xml.etree import ElementTree | |
| 10 | |
| 11 import pytest | |
| 12 | |
| 13 from buildtools import packager, packagerChrome | |
| 14 from buildtools.tests.tools import copy_metadata | |
| 15 | |
| 16 ICON_SIZES = [32, 48, 64, 256, 53] | |
| 17 | |
| 18 MINIMUM_MANIFEST = [ | |
|
Vasily Kuznetsov
2017/08/11 16:46:02
Would it perhaps be more elegant to make this a di
tlucas
2017/08/14 14:23:16
Done.
| |
| 19 ('manifest_version', 2), | |
| 20 ('author', 'Eyeo GmbH'), | |
| 21 ('permissions', [ | |
| 22 'tabs', | |
| 23 '<all_urls>', | |
| 24 'contextMenus', | |
| 25 'webRequest', | |
| 26 'webRequestBlocking', | |
| 27 'webNavigation', | |
| 28 'storage', | |
| 29 'unlimitedStorage', | |
| 30 'notifications', | |
| 31 ]), | |
| 32 ('minimum_chrome_version', '49.0'), | |
| 33 ('minimum_opera_version', '36.0'), | |
| 34 ('devtools_page', 'devtools.html'), | |
| 35 ('description', '__MSG_description__'), | |
| 36 ('default_locale', 'en_US'), | |
| 37 ('short_name', '__MSG_name__'), | |
| 38 ('storage', {'managed_schema': 'managed-storage-schema.json'}), | |
| 39 ('options_ui', {'open_in_tab': True, 'page': 'options.html'}), | |
| 40 ] | |
| 41 | |
| 42 | |
| 43 @pytest.fixture | |
| 44 def chrome_metadata(tmpdir): | |
| 45 filename = 'metadata.chrome' | |
| 46 copy_metadata(filename, tmpdir) | |
| 47 | |
| 48 return packager.readMetadata(str(tmpdir), 'chrome') | |
|
Vasily Kuznetsov
2017/08/11 16:46:01
Could we just read the metadata from the original
tlucas
2017/08/14 14:23:17
It doesn't, since the underlying functions almost
| |
| 49 | |
| 50 | |
| 51 @pytest.fixture | |
| 52 def gecko_webext_metadata(tmpdir, chrome_metadata): | |
| 53 filename = 'metadata.gecko-webext' | |
| 54 copy_metadata(filename, tmpdir) | |
| 55 | |
| 56 # gecko-webext metadata inherits from chrome metadata, assure existence | |
| 57 assert chrome_metadata is not None | |
|
Vasily Kuznetsov
2017/08/11 16:46:02
Do you really need this assert? Seems like if this
tlucas
2017/08/14 14:23:17
As mentioned above, using the original-file won't
| |
| 58 | |
| 59 return packager.readMetadata(str(tmpdir), 'gecko-webext') | |
| 60 | |
| 61 | |
| 62 @pytest.fixture | |
| 63 def icons(tmpdir): | |
| 64 """Valid .png files for testing make_icons""" | |
| 65 from PIL import Image | |
| 66 | |
| 67 paths = [] | |
| 68 icon_dir = tmpdir.mkdir('tmp_icons') | |
| 69 | |
| 70 for size in ICON_SIZES: | |
| 71 img_path = str(icon_dir.join('abp-{}.png'.format(size))) | |
| 72 img = Image.new('1', (size, size if size != 53 else size + 1), 0) | |
| 73 img.save(img_path) | |
| 74 paths.append(img_path) | |
| 75 | |
| 76 return paths | |
| 77 | |
| 78 | |
| 79 @pytest.fixture | |
| 80 def keyfile(tmpdir): | |
| 81 """Test-privatekey for signing files""" | |
| 82 | |
| 83 content = """-----BEGIN RSA PRIVATE KEY----- | |
|
Vasily Kuznetsov
2017/08/11 16:46:01
What do you think about putting this into a keyfil
tlucas
2017/08/14 14:23:17
Done.
| |
| 84 MIIEowIBAAKCAQEA0P06DVxOWZ97cvBZWz7DV0DYTFF7Cdrxnu/sCxnHp8IEq+sM | |
| 85 rqodp2i8eTsddnthTnA93wiFiFXz6/9liQKrSBtogEGG2tmrT73jy5mdOehm7h6m | |
| 86 Ujy6wLuZQiuY/RWJ6453/0H3bKCA8IlIdU8Pkkn5DJWH+YiiSUD01U5ZMLn8m/+O | |
| 87 D/r9CLsgWoJV4VCXyzMItcc8hlRbiSe/3DQmZOPbF0Bd1Jv6lNA4RG0U8oA6q5s1 | |
| 88 5w3n//FjNOreVocyxgWWWGk/awlkJme1xn3yB3yh74b51FHsr9vhiTjcs+QNO1BS | |
| 89 9fA/oavzDOs1Q4fLnTbKn6Jlg5OAjWtiq4vjSQIDAQABAoH/fojTnUNGLP1iwTTE | |
| 90 5Xoay7l3PL4YwN7PbGvXfuEdAXV3Xp/yDc7yJWpEsyIXtKT/RX6v91oxf1qLVVhN | |
| 91 Iad8DSyLGRyTie5Aywct5RgdGfKcX5AvI5uhdxAeuvGqr5Fa8ERSYzqNlDeZ2glE | |
| 92 1cIIq4oeQIBI08zmdXPeyUemuNjK8TEWKOTMOjDLhav/DMviI1cvqp5VIh3RseMr | |
| 93 1dwprXe0/LWuCh0BrCZZNH/0smd2etXEq06RhgnE2sZkowktNhSgc2pqls92FT88 | |
| 94 ObjI/jRXf6OwK7HxIJ1tMKlk/VCvAI2PNWi6E7IZbqN7Rg7859uWybGZkP/h1Qf5 | |
| 95 fRitAoGBAOJnP3kgavnrUJ482R8MNjOwtqbLf4obS5Y/olOHUi+mu2x+EwxgDtaE | |
| 96 sXeMjoIAuZvDoAIG0Hn7+J5ZSYkXT9aXQy/odVWDIpFNFFXNFnS73sAWay/FsMJc | |
| 97 NvgbbWJk9fCq23O7ihuSkfM+zh8dAZoHzV4aw3DsZj37yzuTmTJbAoGBAOxPM6Wy | |
| 98 fENT8ECGl9d/j8QBV4LvjrfIbz/RcCxgZHG50ELnxCo7FiYRg3d2DjR6Q+EpjclX | |
| 99 CC+IwCGGf+RBz53WzJPOBkz+plPZzrhhhtpBuWgegeF+TmYT9pPd9XRHJs5qFCak | |
| 100 J3xxgBur3NDtejNLWBc4bgFfrv8hdYeuIKorAoGBAJplWNbssadvv1G6I0NWG5yS | |
| 101 lW0X+Akh5iE3kiaucPDIHqa1L55P366RXUku1Hx5rBo6hWL8bK3TlM/ACjLwb+Ti | |
| 102 0NHaEDJZtHgsfYKp0veWqyiJ4Vz5zzosktwOMEFaopIWooPBUETPZrLgkMaNDGuT | |
| 103 iIz1aXUX8f2xOf6OAHpjAoGBAMNqq9c2xrQW0fNKWn8HKih4w5mv6WHqCrXHyO+p | |
| 104 tualNqhdaUdTHXnVudYsdorHISMubeY2ZrqIZ/pRc6mbEsoAO6VvFp7NZ1aoI98u | |
| 105 J4qOF3kW4WlMPiEEGUEmqDjELj88UfWygkTSx5Iaibzs5cVNZUeujqnsKpcpYDwX | |
| 106 r0OJAoGBAIWgnx72WjcFmL1kgOu2kH8tH5+cIAb/THX8lnCulQIlxss8n26NMMDa | |
| 107 Kmh5gC2SOAe92cXG6jCYHZqZtl5gM6xGky1B9j7TDrXVPj+QDcItCuNN4zrdtiah | |
| 108 US0KNMZKEMjxsaaMDPHj/bP9ULLbMHvtfvt4EnBQoVCDSZdfeBvZ | |
| 109 -----END RSA PRIVATE KEY-----""" | |
| 110 | |
| 111 keyfile_path = str(tmpdir.mkdir('rsakey').join('keyfile')) | |
| 112 | |
| 113 with open(keyfile_path, 'wb') as key_fp: | |
| 114 key_fp.write(content) | |
| 115 | |
| 116 return keyfile_path | |
| 117 | |
| 118 | |
| 119 @pytest.mark.parametrize('release', [True, False]) | |
| 120 @pytest.mark.parametrize('devenv', [True, False]) | |
| 121 @pytest.mark.parametrize('ext_type', ['chrome', 'gecko-webext']) | |
| 122 def test_create_manifest(srcdir, base_files, release, devenv, ext_type, | |
| 123 chrome_metadata, gecko_webext_metadata): | |
| 124 | |
| 125 if ext_type == 'chrome': | |
| 126 metadata = chrome_metadata | |
| 127 else: | |
| 128 metadata = gecko_webext_metadata | |
| 129 | |
| 130 version = packager.getBuildVersion(str(srcdir), metadata, release) | |
| 131 params = { | |
| 132 'type': ext_type, | |
| 133 'baseDir': str(srcdir), | |
| 134 'releaseBuild': release, | |
| 135 'version': version, | |
| 136 'devenv': devenv, | |
| 137 'metadata': metadata, | |
| 138 } | |
| 139 manifest = json.loads(packagerChrome.createManifest(params, base_files)) | |
| 140 | |
| 141 expected_values = MINIMUM_MANIFEST + [ | |
|
Vasily Kuznetsov
2017/08/11 16:46:01
What do you think about just doing these checks di
tlucas
2017/08/14 14:23:16
Looks definitely cleaner, done.
| |
| 142 ('name', '__MSG_name_' + ('devbuild__' if not release else '_')), | |
| 143 ('version', '1.2.3' + ('.0' if not release else '')), | |
| 144 ] | |
| 145 | |
| 146 if ext_type == 'gecko-webext': | |
| 147 expected_values += [ | |
| 148 ('applications', { | |
| 149 'gecko': { | |
| 150 'id': '{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}', | |
| 151 'strict_min_version': '50.0', | |
| 152 } | |
| 153 }) | |
| 154 ] | |
| 155 if not release: | |
| 156 expected_values[-1][1]['gecko'].update({ | |
| 157 'update_url': ''.join(( | |
| 158 'https://downloads.adblockplus.org/devbuilds/', | |
| 159 'adblockplusfirefox/updates.json')) | |
| 160 }) | |
| 161 | |
| 162 for key, value in expected_values: | |
| 163 assert manifest.get(key, None) == value | |
| 164 | |
| 165 | |
| 166 def test_sign_binary(base_files, keyfile): | |
| 167 from Crypto.Hash import SHA | |
| 168 from Crypto.PublicKey import RSA | |
| 169 from Crypto.Signature import PKCS1_v1_5 | |
| 170 | |
| 171 digest = SHA.new() | |
| 172 | |
| 173 signature = packagerChrome.signBinary(base_files.zipToString(), keyfile) | |
| 174 | |
| 175 with open(keyfile, 'r') as fp: | |
| 176 rsa_key = RSA.importKey(fp.read()) | |
| 177 | |
| 178 signer = PKCS1_v1_5.new(rsa_key) | |
| 179 | |
| 180 digest.update(base_files.zipToString()) | |
| 181 assert signer.verify(digest, signature) | |
| 182 | |
| 183 | |
| 184 def test_get_public_key(keyfile): | |
| 185 expected = ( | |
| 186 '30820122300d06092a864886f70d01010105000382010f003082010a0282010100d0' | |
| 187 'fd3a0d5c4e599f7b72f0595b3ec35740d84c517b09daf19eefec0b19c7a7c204abeb' | |
| 188 '0caeaa1da768bc793b1d767b614e703ddf08858855f3ebff658902ab481b68804186' | |
| 189 'dad9ab4fbde3cb999d39e866ee1ea6523cbac0bb99422b98fd1589eb8e77ff41f76c' | |
| 190 'a080f08948754f0f9249f90c9587f988a24940f4d54e5930b9fc9bff8e0ffafd08bb' | |
| 191 '205a8255e15097cb3308b5c73c86545b8927bfdc342664e3db17405dd49bfa94d038' | |
| 192 '446d14f2803aab9b35e70de7fff16334eade568732c6059658693f6b09642667b5c6' | |
| 193 '7df2077ca1ef86f9d451ecafdbe18938dcb3e40d3b5052f5f03fa1abf30ceb354387' | |
| 194 'cb9d36ca9fa2658393808d6b62ab8be3490203010001') | |
| 195 | |
| 196 publickey = packagerChrome.getPublicKey(keyfile) | |
| 197 assert publickey.encode('hex') == expected | |
|
Vasily Kuznetsov
2017/08/11 16:46:01
I just had an idea, what if we just take an MD5 or
tlucas
2017/08/14 14:23:16
Good point, i started using SHA-hashes in the othe
| |
| 198 | |
| 199 | |
| 200 @pytest.mark.usefixtures('chrome_metadata') | |
| 201 @pytest.mark.parametrize('devenv', [True, False]) | |
| 202 def test_get_package_files(srcdir, devenv): | |
| 203 filenames = [ | |
| 204 'foo.html', | |
| 205 'foo.js', | |
| 206 'foo.json', | |
| 207 'foo.xml', | |
| 208 ] | |
| 209 | |
| 210 for filename in filenames: | |
| 211 srcdir.join(filename).write('', ensure=True) | |
| 212 | |
| 213 params = { | |
| 214 'baseDir': str(srcdir), | |
| 215 'devenv': devenv, | |
| 216 } | |
| 217 | |
| 218 files = packagerChrome.getPackageFiles(params) | |
| 219 | |
| 220 assert ('qunit' in files) == devenv | |
| 221 | |
| 222 for expected in ['icons', 'ui', 'skin', 'ext', '_locales', 'lib', | |
| 223 'jquery-ui'] + [name for name in filenames]: | |
| 224 assert expected in files | |
| 225 | |
| 226 | |
| 227 def test_make_icons(icons, capsys): | |
| 228 files = packager.Files(set(), set()) | |
| 229 for path in icons: | |
| 230 files[path] = open(path, 'r').read() | |
| 231 result = packagerChrome.makeIcons(files, icons) | |
| 232 | |
| 233 out, err = capsys.readouterr() | |
| 234 | |
| 235 assert 'should be square' in err | |
| 236 assert set(result) == set(ICON_SIZES) | |
| 237 | |
| 238 | |
| 239 def test_create_script_page(srcdir, chrome_metadata): | |
| 240 params = {'metadata': chrome_metadata} | |
| 241 | |
| 242 template = packagerChrome.createScriptPage( | |
| 243 params, 'testIndex.html.tmpl', ('general', 'testScripts') | |
| 244 ) | |
| 245 | |
| 246 template_scripts = {elem.attrib['src'] for elem in | |
| 247 ElementTree.fromstring(template).iter('script')} | |
| 248 | |
| 249 for src in chrome_metadata.get('general', 'testScripts').split(): | |
| 250 assert src in template_scripts | |
| 251 | |
| 252 | |
| 253 def test_convert_js(srcdir, base_files, chrome_metadata): | |
| 254 params = { | |
| 255 'type': 'chrome', | |
| 256 'metadata': chrome_metadata, | |
| 257 } | |
| 258 | |
| 259 packagerChrome.convertJS(params, base_files) | |
| 260 source = base_files['lib/foo.js'] | |
| 261 | |
| 262 assert 'var foo;' in source | |
| 263 assert 'var bar;' in source | |
| 264 | |
| 265 | |
| 266 def test_to_json(): | |
| 267 data = {'bar': ['foo', 'foobar', 1, True]} | |
| 268 data_json = packagerChrome.toJson(data) | |
| 269 | |
| 270 assert data_json.endswith('\n') | |
| 271 json.loads(data_json) | |
|
Vasily Kuznetsov
2017/08/11 16:46:01
Maybe also compare it to the original?
tlucas
2017/08/14 14:23:17
Done.
| |
| 272 | |
| 273 | |
| 274 def test_import_gecko_locales(tmpdir, srcdir, base_files, chrome_metadata): | |
| 275 version = packager.getBuildVersion(str(srcdir), chrome_metadata, False) | |
| 276 params = { | |
| 277 'type': 'chrome', | |
| 278 'baseDir': str(srcdir), | |
| 279 'releaseBuild': False, | |
| 280 'version': version, | |
| 281 'devenv': False, | |
| 282 'metadata': chrome_metadata, | |
| 283 } | |
| 284 | |
| 285 data = {'name': 'foo', 'name_devbuild': 'bar'} | |
| 286 | |
| 287 t_dir = tmpdir.mkdir('_trans').mkdir('en-US') | |
| 288 t_dir.join('test.properties').write( | |
| 289 '\n'.join('{}={}'.format(k, v) for k, v in data.items())) | |
| 290 | |
| 291 packagerChrome.importGeckoLocales(params, base_files) | |
| 292 trans_data = json.loads(base_files['_locales/en_US/messages.json']) | |
| 293 | |
| 294 for key, value in data.items(): | |
| 295 assert trans_data.get('test_' + key, {}).get('message', '') == value | |
| 296 | |
| 297 | |
| 298 def test_fix_translations_for_cws(base_files): | |
| 299 base_files['_locales/de/messages.json'] = packagerChrome.toJson({}) | |
| 300 base_files['manifest.json'] = packagerChrome.toJson(dict(MINIMUM_MANIFEST)) | |
| 301 packagerChrome.fixTranslationsForCWS(base_files) | |
| 302 | |
| 303 ger_messages = json.loads(base_files['_locales/de/messages.json']) | |
| 304 eng_messages = json.loads(base_files['_locales/en_US/messages.json']) | |
| 305 | |
| 306 # Check for inserted translations, none were present in 'de' beforehand | |
| 307 for match in re.finditer(r'__MSG_(\S+)__', base_files['manifest.json']): | |
| 308 name = match.group(1) | |
| 309 assert ger_messages[name]['message'] == eng_messages[name]['message'] | |
| 310 | |
| 311 # CWS enforces max-lengths on string | |
| 312 assert len(ger_messages['description']['message']) <= 132 | |
| 313 assert len(ger_messages['name']['message']) <= 12 | |
| 314 | |
| 315 | |
| 316 @pytest.mark.usefixtures('base_files', 'gecko_webext_metadata', | |
| 317 'chrome_metadata') | |
| 318 @pytest.mark.parametrize('release', [True, False]) | |
| 319 @pytest.mark.parametrize('devenv', [True, False]) | |
| 320 @pytest.mark.parametrize('ext_type', ['chrome', 'gecko-webext']) | |
| 321 @pytest.mark.parametrize('key', [True, False]) | |
| 322 def test_create_build(srcdir, tmpdir, release, devenv, ext_type, | |
| 323 key, keyfile): | |
| 324 | |
| 325 basefiles = [ | |
| 326 'manifest.json', | |
| 327 'lib/foo.js', | |
| 328 'foo/logo_50.png', | |
| 329 '_locales/en_US/messages.json', | |
| 330 '_locales/en-US/test.properties', | |
| 331 'logo_44.png', | |
| 332 'icons/logo_150.png', | |
| 333 ] | |
| 334 | |
| 335 out_file = str(tmpdir.join('out')) | |
| 336 | |
| 337 packagerChrome.createBuild( | |
| 338 outFile=out_file, | |
| 339 baseDir=str(srcdir), | |
| 340 type=ext_type, | |
| 341 releaseBuild=release, | |
| 342 keyFile=keyfile if key else None, | |
| 343 devenv=devenv) | |
| 344 | |
| 345 with zipfile.ZipFile(out_file, 'r') as zipfp: | |
| 346 zipfp.testzip() | |
| 347 | |
| 348 filenames = [zipinfo.filename for zipinfo in zipfp.infolist()] | |
| 349 for filename in basefiles if not devenv else basefiles + [ | |
| 350 'devenvPoller__.js', | |
| 351 'devenvVersion__', | |
| 352 'qunit/index.html', | |
| 353 ]: | |
| 354 assert filename in filenames | |
| OLD | NEW |