Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: tests/test_packagerGecko.py

Issue 29501558: Issue 5383 - Add tests for the Chrome and Firefox packagers (Closed)
Left Patch Set: Created July 31, 2017, 12:07 p.m.
Right Patch Set: Created Aug. 4, 2017, 3:06 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
1 # This Source Code Form is subject to the terms of the Mozilla Public 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 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/. 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 4
5 import os
6 import io
5 7
6 import pytest 8 import pytest
7 9
8 import json 10 import json
9 11
10 from zipfile import ZipFile 12 from zipfile import ZipFile
11 13
12 from xml.etree import ElementTree 14 from xml.etree import ElementTree
13 from itertools import product
14 15
15 from buildtools import packagerGecko 16 from buildtools import packagerGecko
16 from buildtools import localeTools 17 from buildtools import localeTools
17 18
18 from buildtools.packager import readMetadata, getBuildVersion, Files 19 from buildtools.packager import getBuildVersion, Files, readMetadata
19 from functools import reduce 20 from buildtools.tests.tools import copy_metadata
20
21 TR_FA = [True, False]
22 21
23 MESSAGES = '\n'.join(( 22 MESSAGES = '\n'.join((
24 'name=Name {0}', 23 'name=Name {0}',
25 'description=Awesome description {0}', 24 'description=Awesome description {0}',
26 )) 25 ))
27 26
28 27
29 @pytest.fixture 28 @pytest.fixture
30 def scripts(tmp_dir): 29 def gecko_metadata(tmpdir):
31 """Examplary scripts for testing addMissingFiles""" 30 filename = 'metadata.gecko'
Vasily Kuznetsov 2017/08/03 16:52:32 AFAIK "examplary" nowadays is usually spelled "exe
tlucas 2017/08/03 21:26:02 Your are right - but i like the "outstanding" part
tlucas 2017/08/04 14:52:00 Done.
32 lib_dir = tmp_dir.mkdir('lib') 31 copy_metadata(filename, tmpdir)
32
33 return readMetadata(str(tmpdir), 'gecko')
34
35
36 @pytest.fixture
37 def scripts(tmpdir):
38 """Example scripts for testing addMissingFiles"""
39 lib_dir = tmpdir.mkdir('lib')
33 lib_dir.join('ext.js').write('require("hooks");') 40 lib_dir.join('ext.js').write('require("hooks");')
34 41
35 content_dir = tmp_dir.mkdir('chrome').mkdir('content') 42 content_dir = tmpdir.mkdir('chrome').mkdir('content')
36 content_dir.join('common.js').write('require("hooks");') 43 content_dir.join('common.js').write('require("hooks");')
37 44
38 45
39 @pytest.fixture 46 @pytest.fixture
40 def prefs_json(tmp_dir): 47 def prefs_json(tmpdir):
41 """Minimal .json file for testing processJSONFiles""" 48 """Minimal .json file for testing processJSONFiles"""
42 lib_dir = tmp_dir.mkdir('lib') 49 lib_dir = tmpdir.mkdir('lib')
43 lib_dir.join('prefs.json').write(json.dumps( 50 lib_dir.join('prefs.json').write(json.dumps(
44 {'foo': 'bar'} 51 {'foo': 'bar'}
45 )) 52 ))
46 53
47 54
48 @pytest.fixture 55 @pytest.fixture
49 def locales(tmp_dir): 56 def locales(tmpdir):
50 """Minimal locales for testing locale-processing""" 57 """Minimal locales for testing locale-processing"""
51 chrome_dir = tmp_dir.mkdir('chrome') 58 chrome_dir = tmpdir.mkdir('chrome')
52 locale_dir = chrome_dir.mkdir('locale') 59 locale_dir = chrome_dir.mkdir('locale')
53 60
54 data = { 61 data = {
55 'name': {'message': 'Name translated'}, 62 'name': {'message': 'Name translated'},
56 'description': {'message': 'Description translated'} 63 'description': {'message': 'Description translated'},
Vasily Kuznetsov 2017/08/03 16:52:32 When you use multiline layout for lists, sets and
tlucas 2017/08/03 21:26:03 As you can see in other collection-defintions, i n
tlucas 2017/08/04 14:51:59 Done.
57 } 64 }
58 65
59 for locale in ['en-US', 'de', 'kn']: 66 for locale in ['en-US', 'de', 'kn']:
60 new_dir = locale_dir.mkdir(locale) 67 new_dir = locale_dir.mkdir(locale)
61 new_dir.join('meta.properties').write(MESSAGES.format(locale)) 68 new_dir.join('meta.properties').write(MESSAGES.format(locale))
62 new_dir.join('test.json').write(json.dumps(data)) 69 new_dir.join('test.json').write(json.dumps(data))
63 if locale == 'kn': 70 if locale == 'kn':
64 new_dir.join('.incomplete').write('') 71 new_dir.join('.incomplete').write('')
65 72
66 73
67 @pytest.fixture 74 @pytest.fixture
68 def subscriptions(tmp_dir): 75 def subscriptions(tmpdir):
69 """Examplary sbuscription-configuration""" 76 """Examplary sbuscription-configuration"""
70 tmp_dir.join('subs.xml').write('\n'.join(( 77 tmpdir.join('subs.xml').write(
Vasily Kuznetsov 2017/08/03 16:52:32 Wouldn't it be easier to just use a string literal
tlucas 2017/08/03 21:26:03 Acknowledged.
tlucas 2017/08/04 14:52:00 Done.
71 '<subscriptions>', 78 '<subscriptions>'
72 '<subscription title="EasyList"', 79 '<subscription title="EasyList"'
73 'specialization="English"', 80 ' specialization="English"'
74 'url="https://easylist-downloads.adblockplus.org/easylist.txt"', 81 ' url="https://easylist-downloads.adblockplus.org/easylist.txt"'
75 'homepage="https://easylist.adblockplus.org/"', 82 ' homepage="https://easylist.adblockplus.org/"'
76 'prefixes="en"', 83 ' prefixes="en"'
77 'author="fanboy, MonztA, Famlam, Khrin"', 84 ' author="fanboy, MonztA, Famlam, Khrin"'
78 'type="ads"/>', 85 ' type="ads"/>'
79 '</subscriptions>', 86 '</subscriptions>'
80 ))) 87 )
81 88
82 89
83 def test_package_files(tmpdir): 90 def test_package_files(tmpdir):
84 tmpdir.join('foo.xml').write('') 91 tmpdir.join('foo.xml').write('')
85 tmpdir.join('foo.txt').write('') 92 tmpdir.join('foo.txt').write('')
86 tmpdir.join('foo.js').write('') 93 tmpdir.join('foo.js').write('')
87 94
88 params = { 95 params = {
89 'baseDir': str(tmpdir) 96 'baseDir': str(tmpdir)
90 } 97 }
91 98
92 files = packagerGecko.getPackageFiles(params) 99 files = packagerGecko.getPackageFiles(params)
93 assert 'foo.xml' in files 100 assert 'foo.xml' in files
94 assert 'foo.js' in files 101 assert 'foo.js' in files
95 assert 'foo.txt' not in files 102 assert 'foo.txt' not in files
96 103
97 104
98 @pytest.mark.usefixtures('locales') 105 @pytest.mark.usefixtures('locales')
99 def test_get_locales(tmp_dir): 106 @pytest.mark.parametrize('incomplete', [True, False])
100 for incomplete in [True, False]: 107 def test_get_locales(tmpdir, incomplete):
Vasily Kuznetsov 2017/08/03 16:52:32 This could also be done via parametrize perhaps.
tlucas 2017/08/03 21:26:02 Acknowledged.
tlucas 2017/08/04 14:52:00 Done.
101 locales = packagerGecko.getLocales(str(tmp_dir), incomplete) 108 locales = packagerGecko.getLocales(str(tmpdir), incomplete)
102 109
103 assert 'de' in locales 110 assert 'de' in locales
104 assert 'en-US' in locales 111 assert 'en-US' in locales
105 assert ('kn' in locales) == incomplete 112 assert ('kn' in locales) == incomplete
106 113
107 114
108 @pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) 115 @pytest.mark.usefixtures('locales', 'subscriptions')
109 @pytest.mark.usefixtures('locales', 'metadata_files', 'subscriptions') 116 @pytest.mark.parametrize('release', [True, False])
110 def test_create_manifest(tmp_dir): 117 @pytest.mark.parametrize('multicompartment', [True, False])
Vasily Kuznetsov 2017/08/03 16:52:32 All this code is pretty cool and clever, but it ma
tlucas 2017/08/03 21:26:02 The cool- and cleverness could be a result of foll
tlucas 2017/08/04 14:51:59 Done.
Vasily Kuznetsov 2017/08/10 19:48:27 Acknowledged.
111 def first(elem): 118 def test_create_manifest(tmpdir, release, multicompartment, gecko_metadata):
112 return elem[0] 119 locales = packagerGecko.getLocales(str(tmpdir))
113 120 contributors = packagerGecko.getContributors(gecko_metadata)
114 def text(elem): 121 version = getBuildVersion(str(tmpdir), gecko_metadata, release, None)
115 return elem.text 122
116 123 rdfpath = os.path.join(
117 def iteritems(func=None): 124 os.path.dirname(__file__),
118 def wrapper(elements): 125 'expecteddata',
119 for elem in elements: 126 'manifest_gecko_{}_{}.rdf'.format(release, multicompartment)
Vasily Kuznetsov 2017/08/10 19:48:28 Nit: comma at the end of the line.
tlucas 2017/08/11 12:17:09 Done.
120 if func: 127 )
121 yield func(elem) 128
122 else: 129 with io.open(rdfpath, 'r') as fp:
123 yield elem 130 expected = ElementTree.fromstring(fp.read())
124 return wrapper 131
125 132 params = {
126 metadata = readMetadata(str(tmp_dir), 'gecko') 133 'baseDir': str(tmpdir),
127 locales = packagerGecko.getLocales(str(tmp_dir)) 134 'locales': locales,
128 contributors = packagerGecko.getContributors(metadata) 135 'metadata': gecko_metadata,
129 136 'version': version.encode('utf-8'),
130 namespaces = { 137 'multicompartment': multicompartment,
131 'em': 'http://www.mozilla.org/2004/em-rdf#', 138 'contributors': contributors
Vasily Kuznetsov 2017/08/10 19:48:28 Nit: comma at the end of the line.
tlucas 2017/08/11 12:17:09 Done.
132 'ns': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 139 }
133 } 140 manifest = packagerGecko.createManifest(params)
134 141
135 base = [ 142 tree = ElementTree.fromstring(manifest)
136 ('.//*', [len], 54), 143
137 ('./ns:Description/em:id', [first, text], 144 from buildtools.tests.tools import get_leafs_string
138 '{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}'), 145 assert set(get_leafs_string(tree)) == set(get_leafs_string(expected))
139 ('./ns:Description/em:optionsURL', [first, text], 146
140 'chrome://adblockplus/content/ui/settings.xul'), 147
141 ('./ns:Description/em:optionsType', [first, text], '2'), 148 @pytest.mark.usefixtures('locales')
142 ('./ns:Description/em:bootstrap', [first, text], 'true'), 149 def test_fixup_import_locales(files, tmpdir, gecko_metadata):
143 ('./ns:Description/em:multiprocessCompatible', [first, text], 'true'), 150 locale_dir = tmpdir.dirpath(tmpdir.basename, 'chrome', 'locale')
144 ('./ns:Description/em:homepageURL', [first, text],
145 'http://adblockplus.org/'),
146 ('./ns:Description/em:creator', [first, text], 'Wladimir Palant'),
147 ('./ns:Description/em:contributor', [iteritems(text)],
148 ['Pety Pete', 'Neil Armstrong', 'Famlam', 'fanboy', 'Khrin',
149 'MonztA']),
150 ]
151
152 base += [
153 ('./ns:Description/em:localized/ns:Description[em:locale="{}"]/em:{}'
154 .format(locale, tag),
155 [first, text],
156 value.format(locale))
157 for locale in locales
158 for tag, value in [
159 ('name', 'Name {}'),
160 ('description', 'Awesome description {}')
161 ]
162 ]
163
164 tags = ['minVersion', 'maxVersion']
165 apps = metadata.items('compat')
166 comp = [
167 (
168 packagerGecko.KNOWN_APPS.get(app[0]),
169 tags[i],
170 app[1].split('/')[i]
171 ) for app in apps for i in range(2)
172 ]
173
174 base += [
175 (''.join((
176 './ns:Description/em:targetApplication/',
177 'ns:Description[em:id="{}"]/em:{}'
178 ))
179 .format(mapped_id, tag),
180 [first, text],
181 value)
182 for mapped_id, tag, value in comp
183 ]
184
185 for release, multicompartment in product(TR_FA, TR_FA):
Vasily Kuznetsov 2017/08/03 16:52:32 Maybe better do this with parametrization?
tlucas 2017/08/03 21:26:02 Acknowledged.
tlucas 2017/08/04 14:52:00 Done.
186 version = getBuildVersion(str(tmp_dir), metadata, release, None)
187 expected = base + [
188 ('./ns:Description/em:version', [first, text], version),
189 ]
190 params = {
191 'baseDir': str(tmp_dir),
192 'locales': locales,
193 'metadata': metadata,
194 'version': version.encode('utf-8'),
195 'multicompartment': multicompartment,
196 'contributors': contributors
197 }
198 manifest = packagerGecko.createManifest(params)
199
200 tree = ElementTree.fromstring(manifest)
201
202 with open('/tmp/test.xml', 'w') as fp:
Vasily Kuznetsov 2017/08/03 16:52:32 Why use '/tmp' instead of tmpdir fixture? Also kee
tlucas 2017/08/03 21:26:02 Acknowledged.
tlucas 2017/08/04 14:52:00 Done (removed).
203 fp.write(manifest)
204
205 for expression, modifiers, value in expected:
206 res = reduce(
207 lambda val, func: func(val),
208 modifiers,
209 tree.findall(expression, namespaces=namespaces))
210
211 from collections import Iterable
212
213 if isinstance(res, Iterable) and not isinstance(res, str):
214 res = list(res)
215 for x in res:
Vasily Kuznetsov 2017/08/03 16:52:32 Seems like you just want to compare `res` to `valu
tlucas 2017/08/03 21:26:02 I agree with the assert set() == set() part, but o
tlucas 2017/08/04 14:52:00 I totally missed something. Done.
216 assert x in value
217 for x in value:
218 assert x in res
219 assert res == value
220
221
222 @pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True)
223 @pytest.mark.usefixtures('locales', 'metadata_files')
224 def test_fixup_import_locales(files, tmp_dir):
225 locale_dir = tmp_dir.dirpath(tmp_dir.basename, 'chrome', 'locale')
226 locale_dir.mkdir('fr').join('meta.properties')\ 151 locale_dir.mkdir('fr').join('meta.properties')\
Vasily Kuznetsov 2017/08/10 19:48:28 You don't need to break this line, it actually fit
tlucas 2017/08/11 12:17:09 Done.
227 .write(MESSAGES.format('fr')) 152 .write(MESSAGES.format('fr'))
228 153
229 metadata = readMetadata(str(tmp_dir), 'gecko') 154 locales = packagerGecko.getLocales(str(tmpdir), False)
230 locales = packagerGecko.getLocales(str(tmp_dir), False) 155
231 156 params = {
232 params = { 157 'metadata': gecko_metadata,
233 'metadata': metadata,
234 'locales': locales, 158 'locales': locales,
235 'baseDir': str(tmp_dir) 159 'baseDir': str(tmpdir)
236 } 160 }
237 161
238 # Should add missing fr/test.properties to files 162 # Should add missing fr/test.properties to files
239 packagerGecko.fixupLocales(params, files) 163 packagerGecko.fixupLocales(params, files)
240 164
241 packagerGecko.importLocales(params, files) 165 packagerGecko.importLocales(params, files)
242 for locale in locales: 166 for locale in locales:
243 properties = files['chrome/locale/{}/test.properties'.format(locale)] 167 properties = files['chrome/locale/{}/test.properties'.format(locale)]
244 translation_data = list( 168 translation_data = list(
245 localeTools.parsePropertiesString(properties, '')) 169 localeTools.parsePropertiesString(properties, ''))
246 170
247 for trans in [ 171 for trans in [
248 ('name', None, 'Name translated'), 172 ('name', None, 'Name translated'),
249 ('description', None, 'Description translated')]: 173 ('description', None, 'Description translated')]:
250 assert trans in translation_data 174 assert trans in translation_data
251 175
252 176
253 def test_process_json_files(tmp_dir, prefs_json): 177 def test_process_json_files(tmpdir, prefs_json):
254 params = { 178 params = {
255 'baseDir': str(tmp_dir), 179 'baseDir': str(tmpdir),
256 'jsonRequires': {}, 180 'jsonRequires': {},
257 } 181 }
258 182
259 files = Files(packagerGecko.getPackageFiles(params), set()) 183 files = Files(packagerGecko.getPackageFiles(params), set())
260 files.read(str(tmp_dir)) 184 files.read(str(tmpdir))
261 185
262 packagerGecko.processJSONFiles(params, files) 186 packagerGecko.processJSONFiles(params, files)
263 187
264 assert params['jsonRequires'] == {'prefs.json': {'foo': 'bar'}} 188 assert params['jsonRequires'] == {'prefs.json': {'foo': 'bar'}}
265 189
266 190
267 @pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) 191 @pytest.mark.usefixtures('scripts')
268 @pytest.mark.usefixtures('scripts', 'metadata_files') 192 def test_add_missing_files(tmpdir, gecko_metadata):
269 def test_add_missing_files(tmp_dir): 193 params = {
270 metadata = readMetadata(str(tmp_dir), 'gecko') 194 'baseDir': str(tmpdir),
271 195 'metadata': gecko_metadata,
272 params = {
273 'baseDir': str(tmp_dir),
274 'metadata': metadata,
275 'jsonRequires': {}, 196 'jsonRequires': {},
276 'multicompartment': True, 197 'multicompartment': True,
277 'hasWebExtension': False, 198 'hasWebExtension': False,
278 } 199 }
279 200
280 files = Files(packagerGecko.getPackageFiles(params), set()) 201 files = Files(packagerGecko.getPackageFiles(params), set())
281 files.read(str(tmp_dir)) 202 files.read(str(tmpdir))
282 203
283 packagerGecko.addMissingFiles(params, files) 204 packagerGecko.addMissingFiles(params, files)
284 205
285 assert 'let shutdownHandlers = [];' in files['bootstrap.js'] 206 assert 'let shutdownHandlers = [];' in files['bootstrap.js']
286 assert 'Services.obs.addObserver(' in files['bootstrap.js'] 207 assert 'Services.obs.addObserver(' in files['bootstrap.js']
287 for filename in ['bootstrap.js', 'chrome/content/common.js', 208 for filename in ['bootstrap.js', 'chrome/content/common.js',
288 'lib/ext.js', 'lib/hooks.js']: 209 'lib/ext.js', 'lib/hooks.js']:
289 assert filename in files 210 assert filename in files
290 211
291 212
292 @pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) 213 @pytest.mark.usefixtures('gecko_metadata', 'locales', 'subscriptions')
293 @pytest.mark.usefixtures('metadata_files', 'locales', 'subscriptions') 214 @pytest.mark.parametrize('release', [True, False])
294 def test_create_build(tmp_dir, capsys): 215 @pytest.mark.parametrize('multicompartment', [True, False])
216 @pytest.mark.parametrize('all_locales', ['all', None])
217 def test_create_build(tmpdir, capsys, release, multicompartment, all_locales):
295 base_files = [ 218 base_files = [
296 'bootstrap.js', 219 'bootstrap.js',
297 'chrome/locale/de/test.json', 220 'chrome/locale/de/test.json',
298 'chrome/locale/de/test.properties', 221 'chrome/locale/de/test.properties',
299 'chrome/locale/en-US/test.json', 222 'chrome/locale/en-US/test.json',
300 'chrome/locale/en-US/test.properties', 223 'chrome/locale/en-US/test.properties',
301 'install.rdf', 224 'install.rdf',
302 'subs.xml' 225 'subs.xml'
303 ] 226 ]
304 227
305 for all_locales, release, multicompartment in product( 228 if all_locales is None:
Vasily Kuznetsov 2017/08/03 16:52:32 Also maybe parametrization?
tlucas 2017/08/03 21:26:03 Acknowledged.
tlucas 2017/08/04 14:51:59 Done.
306 ['all', None], TR_FA, TR_FA): 229 expected = base_files
307 230 else:
308 if all_locales is None: 231 expected = base_files + [
309 expected = base_files 232 'chrome/locale/kn/test.json',
310 else: 233 'chrome/locale/kn/test.properties'
311 expected = base_files + [ 234 ]
312 'chrome/locale/kn/test.json', 235
313 'chrome/locale/kn/test.properties' 236 out_file = tmpdir.join('{}_{}_{}.zip'.format(
314 ] 237 all_locales, release, multicompartment))
315 238
316 out_file = tmp_dir.join('{}_{}_{}.zip'.format( 239 out_file = str(out_file)
317 all_locales, release, multicompartment)) 240
318 241 packagerGecko.createBuild(
319 out_file = str(out_file) 242 str(tmpdir),
320 243 locales=all_locales,
321 packagerGecko.createBuild( 244 outFile=out_file,
322 str(tmp_dir), 245 releaseBuild=release,
323 locales=all_locales, 246 multicompartment=multicompartment)
324 outFile=out_file, 247
325 releaseBuild=release, 248 out, err = capsys.readouterr()
326 multicompartment=multicompartment) 249
327 250 assert err ==\
328 out, err = capsys.readouterr() 251 "Warning: Mapped file adblockplusui/firstRun.html doesn't exist\n"
329 252
330 assert err ==\ 253 with ZipFile(out_file, 'r') as zipfp:
331 "Warning: Mapped file adblockplusui/firstRun.html doesn't exist\n" 254 zipfp.testzip()
332 255
333 with ZipFile(out_file, 'r') as zipfp: 256 filenames = [zipinfo.filename for zipinfo in zipfp.infolist()]
334 zipfp.testzip() 257
335 258 for name in expected:
336 filenames = [zipinfo.filename for zipinfo in zipfp.infolist()] 259 assert name in filenames
337
338 for name in expected:
339 assert name in filenames
LEFTRIGHT

Powered by Google App Engine
This is Rietveld