 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 | |
| 6 import pytest | |
| 7 | |
| 8 import json | |
| 9 | |
| 10 from zipfile import ZipFile | |
| 11 | |
| 12 from xml.etree import ElementTree | |
| 13 from itertools import product | |
| 14 | |
| 15 from buildtools import packagerGecko | |
| 16 from buildtools import localeTools | |
| 17 | |
| 18 from buildtools.packager import readMetadata, getBuildVersion, Files | |
| 19 from functools import reduce | |
| 20 | |
| 21 TR_FA = [True, False] | |
| 22 | |
| 23 MESSAGES = '\n'.join(( | |
| 24 'name=Name {0}', | |
| 25 'description=Awesome description {0}', | |
| 26 )) | |
| 27 | |
| 28 | |
| 29 @pytest.fixture | |
| 30 def scripts(tmp_dir): | |
| 31 """Examplary scripts for testing addMissingFiles""" | |
| 
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') | |
| 33 lib_dir.join('ext.js').write('require("hooks");') | |
| 34 | |
| 35 content_dir = tmp_dir.mkdir('chrome').mkdir('content') | |
| 36 content_dir.join('common.js').write('require("hooks");') | |
| 37 | |
| 38 | |
| 39 @pytest.fixture | |
| 40 def prefs_json(tmp_dir): | |
| 41 """Minimal .json file for testing processJSONFiles""" | |
| 42 lib_dir = tmp_dir.mkdir('lib') | |
| 43 lib_dir.join('prefs.json').write(json.dumps( | |
| 44 {'foo': 'bar'} | |
| 45 )) | |
| 46 | |
| 47 | |
| 48 @pytest.fixture | |
| 49 def locales(tmp_dir): | |
| 50 """Minimal locales for testing locale-processing""" | |
| 51 chrome_dir = tmp_dir.mkdir('chrome') | |
| 52 locale_dir = chrome_dir.mkdir('locale') | |
| 53 | |
| 54 data = { | |
| 55 'name': {'message': 'Name translated'}, | |
| 56 '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 } | |
| 58 | |
| 59 for locale in ['en-US', 'de', 'kn']: | |
| 60 new_dir = locale_dir.mkdir(locale) | |
| 61 new_dir.join('meta.properties').write(MESSAGES.format(locale)) | |
| 62 new_dir.join('test.json').write(json.dumps(data)) | |
| 63 if locale == 'kn': | |
| 64 new_dir.join('.incomplete').write('') | |
| 65 | |
| 66 | |
| 67 @pytest.fixture | |
| 68 def subscriptions(tmp_dir): | |
| 69 """Examplary sbuscription-configuration""" | |
| 70 tmp_dir.join('subs.xml').write('\n'.join(( | |
| 
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>', | |
| 72 '<subscription title="EasyList"', | |
| 73 'specialization="English"', | |
| 74 'url="https://easylist-downloads.adblockplus.org/easylist.txt"', | |
| 75 'homepage="https://easylist.adblockplus.org/"', | |
| 76 'prefixes="en"', | |
| 77 'author="fanboy, MonztA, Famlam, Khrin"', | |
| 78 'type="ads"/>', | |
| 79 '</subscriptions>', | |
| 80 ))) | |
| 81 | |
| 82 | |
| 83 def test_package_files(tmpdir): | |
| 84 tmpdir.join('foo.xml').write('') | |
| 85 tmpdir.join('foo.txt').write('') | |
| 86 tmpdir.join('foo.js').write('') | |
| 87 | |
| 88 params = { | |
| 89 'baseDir': str(tmpdir) | |
| 90 } | |
| 91 | |
| 92 files = packagerGecko.getPackageFiles(params) | |
| 93 assert 'foo.xml' in files | |
| 94 assert 'foo.js' in files | |
| 95 assert 'foo.txt' not in files | |
| 96 | |
| 97 | |
| 98 @pytest.mark.usefixtures('locales') | |
| 99 def test_get_locales(tmp_dir): | |
| 100 for incomplete in [True, False]: | |
| 
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) | |
| 102 | |
| 103 assert 'de' in locales | |
| 104 assert 'en-US' in locales | |
| 105 assert ('kn' in locales) == incomplete | |
| 106 | |
| 107 | |
| 108 @pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) | |
| 109 @pytest.mark.usefixtures('locales', 'metadata_files', 'subscriptions') | |
| 110 def test_create_manifest(tmp_dir): | |
| 
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): | |
| 112 return elem[0] | |
| 113 | |
| 114 def text(elem): | |
| 115 return elem.text | |
| 116 | |
| 117 def iteritems(func=None): | |
| 118 def wrapper(elements): | |
| 119 for elem in elements: | |
| 120 if func: | |
| 121 yield func(elem) | |
| 122 else: | |
| 123 yield elem | |
| 124 return wrapper | |
| 125 | |
| 126 metadata = readMetadata(str(tmp_dir), 'gecko') | |
| 127 locales = packagerGecko.getLocales(str(tmp_dir)) | |
| 128 contributors = packagerGecko.getContributors(metadata) | |
| 129 | |
| 130 namespaces = { | |
| 131 'em': 'http://www.mozilla.org/2004/em-rdf#', | |
| 132 'ns': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', | |
| 133 } | |
| 134 | |
| 135 base = [ | |
| 136 ('.//*', [len], 54), | |
| 137 ('./ns:Description/em:id', [first, text], | |
| 138 '{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}'), | |
| 139 ('./ns:Description/em:optionsURL', [first, text], | |
| 140 'chrome://adblockplus/content/ui/settings.xul'), | |
| 141 ('./ns:Description/em:optionsType', [first, text], '2'), | |
| 142 ('./ns:Description/em:bootstrap', [first, text], 'true'), | |
| 143 ('./ns:Description/em:multiprocessCompatible', [first, text], 'true'), | |
| 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')\ | |
| 227 .write(MESSAGES.format('fr')) | |
| 228 | |
| 229 metadata = readMetadata(str(tmp_dir), 'gecko') | |
| 230 locales = packagerGecko.getLocales(str(tmp_dir), False) | |
| 231 | |
| 232 params = { | |
| 233 'metadata': metadata, | |
| 234 'locales': locales, | |
| 235 'baseDir': str(tmp_dir) | |
| 236 } | |
| 237 | |
| 238 # Should add missing fr/test.properties to files | |
| 239 packagerGecko.fixupLocales(params, files) | |
| 240 | |
| 241 packagerGecko.importLocales(params, files) | |
| 242 for locale in locales: | |
| 243 properties = files['chrome/locale/{}/test.properties'.format(locale)] | |
| 244 translation_data = list( | |
| 245 localeTools.parsePropertiesString(properties, '')) | |
| 246 | |
| 247 for trans in [ | |
| 248 ('name', None, 'Name translated'), | |
| 249 ('description', None, 'Description translated')]: | |
| 250 assert trans in translation_data | |
| 251 | |
| 252 | |
| 253 def test_process_json_files(tmp_dir, prefs_json): | |
| 254 params = { | |
| 255 'baseDir': str(tmp_dir), | |
| 256 'jsonRequires': {}, | |
| 257 } | |
| 258 | |
| 259 files = Files(packagerGecko.getPackageFiles(params), set()) | |
| 260 files.read(str(tmp_dir)) | |
| 261 | |
| 262 packagerGecko.processJSONFiles(params, files) | |
| 263 | |
| 264 assert params['jsonRequires'] == {'prefs.json': {'foo': 'bar'}} | |
| 265 | |
| 266 | |
| 267 @pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) | |
| 268 @pytest.mark.usefixtures('scripts', 'metadata_files') | |
| 269 def test_add_missing_files(tmp_dir): | |
| 270 metadata = readMetadata(str(tmp_dir), 'gecko') | |
| 271 | |
| 272 params = { | |
| 273 'baseDir': str(tmp_dir), | |
| 274 'metadata': metadata, | |
| 275 'jsonRequires': {}, | |
| 276 'multicompartment': True, | |
| 277 'hasWebExtension': False, | |
| 278 } | |
| 279 | |
| 280 files = Files(packagerGecko.getPackageFiles(params), set()) | |
| 281 files.read(str(tmp_dir)) | |
| 282 | |
| 283 packagerGecko.addMissingFiles(params, files) | |
| 284 | |
| 285 assert 'let shutdownHandlers = [];' in files['bootstrap.js'] | |
| 286 assert 'Services.obs.addObserver(' in files['bootstrap.js'] | |
| 287 for filename in ['bootstrap.js', 'chrome/content/common.js', | |
| 288 'lib/ext.js', 'lib/hooks.js']: | |
| 289 assert filename in files | |
| 290 | |
| 291 | |
| 292 @pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) | |
| 293 @pytest.mark.usefixtures('metadata_files', 'locales', 'subscriptions') | |
| 294 def test_create_build(tmp_dir, capsys): | |
| 295 base_files = [ | |
| 296 'bootstrap.js', | |
| 297 'chrome/locale/de/test.json', | |
| 298 'chrome/locale/de/test.properties', | |
| 299 'chrome/locale/en-US/test.json', | |
| 300 'chrome/locale/en-US/test.properties', | |
| 301 'install.rdf', | |
| 302 'subs.xml' | |
| 303 ] | |
| 304 | |
| 305 for all_locales, release, multicompartment in product( | |
| 
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): | |
| 307 | |
| 308 if all_locales is None: | |
| 309 expected = base_files | |
| 310 else: | |
| 311 expected = base_files + [ | |
| 312 'chrome/locale/kn/test.json', | |
| 313 'chrome/locale/kn/test.properties' | |
| 314 ] | |
| 315 | |
| 316 out_file = tmp_dir.join('{}_{}_{}.zip'.format( | |
| 317 all_locales, release, multicompartment)) | |
| 318 | |
| 319 out_file = str(out_file) | |
| 320 | |
| 321 packagerGecko.createBuild( | |
| 322 str(tmp_dir), | |
| 323 locales=all_locales, | |
| 324 outFile=out_file, | |
| 325 releaseBuild=release, | |
| 326 multicompartment=multicompartment) | |
| 327 | |
| 328 out, err = capsys.readouterr() | |
| 329 | |
| 330 assert err ==\ | |
| 331 "Warning: Mapped file adblockplusui/firstRun.html doesn't exist\n" | |
| 332 | |
| 333 with ZipFile(out_file, 'r') as zipfp: | |
| 334 zipfp.testzip() | |
| 335 | |
| 336 filenames = [zipinfo.filename for zipinfo in zipfp.infolist()] | |
| 337 | |
| 338 for name in expected: | |
| 339 assert name in filenames | |
| OLD | NEW |