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 |