 Issue 29501558:
  Issue 5383 - Add tests for the Chrome and Firefox packagers  (Closed)
    
  
    Issue 29501558:
  Issue 5383 - Add tests for the Chrome and Firefox packagers  (Closed) 
  | 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 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 if devenv: | |
| 124 assert 'qunit/index.html' in filenames | |
| 125 assert 'devenvPoller__.js' in filenames | |
| 126 assert 'devenvVersion__' in filenames | |
| 127 | |
| 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 'qunit/index.html' not in filenames | |
| 
Sebastian Noack
2017/09/21 19:09:05
Neither should 'devenvPoller__.js' and devenvVersi
 
tlucas
2017/09/22 09:02:19
I agree, although i will switch places, see the di
 | |
| 136 | |
| 137 assert set(manifest['background']['scripts']) == set(scripts) | |
| 138 | |
| 139 | |
| 140 def assert_base_files(package): | |
| 141 filenames = set(package.namelist()) | |
| 142 | |
| 143 assert 'bar.json' in filenames | |
| 144 assert 'manifest.json' in filenames | |
| 145 assert 'lib/foo.js' in filenames | |
| 146 assert 'foo/logo_50.png' in filenames | |
| 147 assert 'icons/logo_150.png' in filenames | |
| 148 | |
| 149 | |
| 150 def assert_chrome_signature(filename, keyfile): | |
| 151 with open(filename, 'r') as fp: | |
| 152 content = fp.read() | |
| 153 | |
| 154 _, _, l_pubkey, l_signature = unpack('<4sIII', content[:16]) | |
| 155 signature = content[16 + l_pubkey: 16 + l_pubkey + l_signature] | |
| 156 | |
| 157 digest = SHA.new() | |
| 158 with open(keyfile, 'r') as fp: | |
| 159 rsa_key = RSA.importKey(fp.read()) | |
| 160 | |
| 161 signer = PKCS1_v1_5.new(rsa_key) | |
| 162 | |
| 163 digest.update(content[16 + l_pubkey + l_signature:]) | |
| 164 assert signer.verify(digest, signature) | |
| 165 | |
| 166 | |
| 167 def assert_locale_upfix(package): | |
| 168 translations = [ | |
| 169 json.loads(package.read('_locales/{}/messages.json'.format(lang))) | |
| 170 for lang in ALL_LANGUAGES | |
| 171 ] | |
| 172 | |
| 173 manifest = package.read('manifest.json') | |
| 174 | |
| 175 # Chrome Web Store requires descriptive translations to be present in | |
| 176 # every language. | |
| 177 for match in re.finditer(r'__MSG_(\S+)__', manifest): | |
| 178 name = match.group(1) | |
| 179 | |
| 180 for other in translations[1:]: | |
| 181 assert translations[0][name]['message'] == other[name]['message'] | |
| 182 | |
| 183 | |
| 184 @pytest.mark.usefixtures( | |
| 185 'all_lang_locales', | |
| 186 'locale_modules', | |
| 187 'gecko_import', | |
| 188 'icons', | |
| 189 'lib_files', | |
| 190 'chrome_metadata', | |
| 191 'gecko_webext_metadata', | |
| 192 ) | |
| 193 @pytest.mark.parametrize('platform', ['chrome', 'gecko-webext']) | |
| 194 @pytest.mark.parametrize('dev_build_release,buildnum', [ | |
| 195 ('build', '1337'), | |
| 196 ('devenv', None), | |
| 197 ('release', None), | |
| 198 ]) | |
| 199 def test_build_webext(platform, dev_build_release, keyfile, tmpdir, srcdir, | |
| 200 buildnum, capsys): | |
| 201 release = dev_build_release == 'release' | |
| 202 devenv = dev_build_release == 'devenv' | |
| 203 | |
| 204 if platform == 'chrome' and release: | |
| 205 key = keyfile | |
| 206 else: | |
| 207 key = None | |
| 208 | |
| 209 filenames = { | |
| 210 'gecko-webext': 'adblockplusfirefox-1.2.3{}.xpi', | |
| 211 'chrome': 'adblockpluschrome-1.2.3{{}}.{}'.format( | |
| 212 {True: 'crx', False: 'zip'}[release] | |
| 213 ), | |
| 214 } | |
| 215 | |
| 216 expected_manifest = os.path.join( | |
| 217 os.path.dirname(__file__), | |
| 218 'expecteddata', | |
| 219 'manifest_{}_{}_{}.json'.format(platform, dev_build_release, buildnum), | |
| 
Sebastian Noack
2017/09/21 19:09:04
The buildnum is redundant here. The filenames are
 
tlucas
2017/09/22 09:02:20
Done.
 | |
| 220 ) | |
| 221 | |
| 222 run_webext_build(platform, dev_build_release, srcdir, buildnum=buildnum, | |
| 223 keyfile=key) | |
| 224 | |
| 225 # The makeIcons() in packagerChrome.py should warn about non-square | |
| 226 # icons via stderr. | |
| 227 out, err = capsys.readouterr() | |
| 228 assert 'icon should be square' in err | |
| 229 | |
| 230 if devenv: | |
| 231 content_class = DirContent | |
| 232 out_file_path = os.path.join(str(srcdir), 'devenv.' + platform) | |
| 233 else: | |
| 234 content_class = ZipContent | |
| 235 | |
| 236 if release: | |
| 237 add_version = '' | |
| 238 else: | |
| 239 add_version = '.' + buildnum | |
| 240 | |
| 241 out_file = filenames[platform].format(add_version) | |
| 242 | |
| 243 out_file_path = os.path.abspath(os.path.join( | |
| 244 os.path.dirname(__file__), os.pardir, out_file)) | |
| 245 assert os.path.exists(out_file_path) | |
| 246 | |
| 247 if release and platform == 'chrome': | |
| 248 assert_chrome_signature(out_file_path, keyfile) | |
| 249 | |
| 250 with content_class(out_file_path) as package: | |
| 251 assert_manifest_content( | |
| 252 package.read('manifest.json'), expected_manifest | |
| 253 ) | |
| 254 assert_base_files(package) | |
| 
Sebastian Noack
2017/09/21 19:09:06
The features tested here are relevant for "edge" b
 
tlucas
2017/09/22 09:02:18
This test happens in edge as well, see the 7 singl
 
Sebastian Noack
2017/09/22 18:31:04
The logic there seems inconsistent. It seems that
 
Sebastian Noack
2017/09/25 20:21:19
Yes, I agree that there is no reason to split out
 | |
| 255 assert_devenv_scripts(package, devenv) | |
| 256 assert_all_locales_present(package, '_locales') | |
| 257 assert_gecko_locale_conversion(package) | |
| 258 assert_convert_js(package, platform == 'gecko-webext') | |
| 259 | |
| 260 if platform == 'chrome': | |
| 261 assert_locale_upfix(package) | |
| OLD | NEW |