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

Delta Between Two Patch Sets: tests/test_packagerWebExt.py

Issue 29501558: Issue 5383 - Add tests for the Chrome and Firefox packagers (Closed)
Left Patch Set: NO CHANGE Rebasing against #4720 @ PS 4 Created Oct. 18, 2017, 11:18 a.m.
Right Patch Set: Addressing Vasily's comments Created Oct. 22, 2017, 11:11 a.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
« no previous file with change/comment | « tests/test_packagerEdge.py ('k') | tox.ini » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 difflib
6 import json
5 import os 7 import os
8 import re
6 import shutil 9 import shutil
7 import zipfile 10 import zipfile
8 import json 11 from xml.etree import ElementTree
9 import re
10 from struct import unpack 12 from struct import unpack
11 import difflib
12 13
13 import pytest 14 import pytest
15 from Crypto.Signature import PKCS1_v1_5
16 from Crypto.PublicKey import RSA
14 from Crypto.Hash import SHA 17 from Crypto.Hash import SHA
15 from Crypto.PublicKey import RSA
16 from Crypto.Signature import PKCS1_v1_5
17 from xml.etree import ElementTree
18
19 18
20 from buildtools import packager 19 from buildtools import packager
21 from buildtools.packagerChrome import defaultLocale 20 from buildtools.packagerChrome import defaultLocale
22 from buildtools.build import processArgs 21 from buildtools.build import processArgs
23 22
24 LOCALES_MODULE = { 23 LOCALES_MODULE = {
25 'test.Foobar': { 24 'test.Foobar': {
26 'message': 'Ensuring dict-copy from modules for $domain$', 25 'message': 'Ensuring dict-copy from modules for $domain$',
27 'description': 'test description', 26 'description': 'test description',
28 'placeholders': {'content': '$1', 'example': 'www.adblockplus.org'} 27 'placeholders': {'content': '$1', 'example': 'www.adblockplus.org'}
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 return content 119 return content
121 120
122 121
123 def copy_metadata(filename, tmpdir): 122 def copy_metadata(filename, tmpdir):
124 """Copy the used metadata to the used temporary directory.""" 123 """Copy the used metadata to the used temporary directory."""
125 path = os.path.join(os.path.dirname(__file__), filename) 124 path = os.path.join(os.path.dirname(__file__), filename)
126 destination = str(tmpdir.join(filename)) 125 destination = str(tmpdir.join(filename))
127 shutil.copy(path, destination) 126 shutil.copy(path, destination)
128 127
129 128
130 def run_webext_build(ext_type, build_opt, srcdir, buildnum=None, keyfile=None): 129 def run_webext_build(ext_type, build_opt, srcdir, keyfile=None):
131 """Run a build process.""" 130 """Run a build process."""
132 if build_opt == 'build': 131 cmd_mapping = {
133 build_args = ['build'] 132 'devenv': ['devenv'],
134 elif build_opt == 'release': 133 'development_build': ['build', '-b', '1337'],
135 build_args = ['build', '-r'] 134 'release_build': ['build', '-r'],
136 else: 135 }
137 build_args = ['devenv'] 136
138 137 args = ['build.py', '-t', ext_type] + cmd_mapping[build_opt]
139 args = ['build.py', '-t', ext_type] + build_args
140 138
141 if keyfile: 139 if keyfile:
142 args += ['-k', keyfile] 140 args += ['-k', keyfile]
143 if buildnum:
144 args += ['-b', buildnum]
145 141
146 processArgs(str(srcdir), args) 142 processArgs(str(srcdir), args)
147 143
148 144
149 def locale_files(languages, rootfolder, srcdir): 145 def locale_files(languages, rootfolder, srcdir):
150 """Generate example locales. 146 """Generate example locales.
151 147
152 languages: tuple, list or set of languages to include 148 languages: tuple, list or set of languages to include
153 rootdir: folder-name to create the locale-folders in 149 rootdir: folder-name to create the locale-folders in
154 """ 150 """
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 234
239 tmpdir.mkdir('lib').join('b.js').write(files['lib/b.js']) 235 tmpdir.mkdir('lib').join('b.js').write(files['lib/b.js'])
240 ext_dir = tmpdir.mkdir('ext') 236 ext_dir = tmpdir.mkdir('ext')
241 ext_dir.join('a.js').write(files['ext/a.js']) 237 ext_dir.join('a.js').write(files['ext/a.js'])
242 ext_dir.join('c.js').write(files['ext/c.js']) 238 ext_dir.join('c.js').write(files['ext/c.js'])
243 239
244 return files 240 return files
245 241
246 242
247 def comparable_xml(xml): 243 def comparable_xml(xml):
248 """Create a nonambiguous representation of a given XML tree.""" 244 """Create a nonambiguous representation of a given XML tree.
245
246 Note that this function is not safe against ambiguous tags
247 containing differently distributed children, e.g.:
248
249 '<a><b><c/></b><b><d/></b></a>'
250 vs.
251 '<a><b/><b><c/><d/></b></a>'
252
253 This is irrelevant for comparing the XML used by the tests of this
254 module.
255 """
249 def get_leafs_string(tree): 256 def get_leafs_string(tree):
250 """Recursively build a string representing all xml leaf-nodes.""" 257 """Recursively build a string representing all xml leaf-nodes."""
251 root_str = '{}|{}|{}'.format(tree.tag, tree.tail, tree.text).strip() 258 root_str = '{}|{}|{}'.format(tree.tag, tree.tail, tree.text).strip()
252 result = [] 259 result = []
253 260
254 if len(tree) > 0: 261 if len(tree) > 0:
255 for subtree in tree: 262 for subtree in tree:
256 for leaf in get_leafs_string(subtree): 263 for leaf in get_leafs_string(subtree):
257 result.append('{}__{}'.format(root_str, leaf)) 264 result.append('{}__{}'.format(root_str, leaf))
258 for k, v in tree.attrib.items(): 265 for k, v in tree.attrib.items():
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 assert 'var bar;' in libfoo 302 assert 'var bar;' in libfoo
296 assert 'webpack:///./ext/a.js' in libfoomap 303 assert 'webpack:///./ext/a.js' in libfoomap
297 304
298 assert 'var this_is_c;' in libfoo 305 assert 'var this_is_c;' in libfoo
299 assert 'webpack:///./ext/c.js' in libfoomap 306 assert 'webpack:///./ext/c.js' in libfoomap
300 307
301 assert ('var foo;' in libfoo) != excluded 308 assert ('var foo;' in libfoo) != excluded
302 assert ('webpack:///./lib/b.js' in libfoomap) != excluded 309 assert ('webpack:///./lib/b.js' in libfoomap) != excluded
303 310
304 311
305 def assert_devenv_scripts(package, devenv): 312 def assert_devenv_scripts(package, prefix, devenv):
306 manifest = json.loads(package.read('manifest.json')) 313 manifest = json.loads(package.read(os.path.join(prefix, 'manifest.json')))
307 filenames = package.namelist() 314 filenames = package.namelist()
308 scripts = [ 315 scripts = [
309 'ext/common.js', 316 'ext/common.js',
310 'ext/background.js', 317 'ext/background.js',
311 ] 318 ]
312 319
313 assert ('qunit/index.html' in filenames) == devenv 320 assert (os.path.join(prefix, 'qunit/index.html') in filenames) == devenv
314 assert ('devenvPoller__.js' in filenames) == devenv 321 assert (os.path.join(prefix, 'devenvPoller__.js') in filenames) == devenv
315 assert ('devenvVersion__' in filenames) == devenv 322 assert (os.path.join(prefix, 'devenvVersion__') in filenames) == devenv
316 323
317 if devenv: 324 if devenv:
318 assert '../ext/common.js' in package.read('qunit/index.html') 325 quint_index = package.read(os.path.join(prefix, 'qunit/index.html'))
319 assert '../ext/background.js' in package.read('qunit/index.html') 326 assert '../ext/common.js' in quint_index
327 assert '../ext/background.js' in quint_index
320 328
321 assert set(manifest['background']['scripts']) == set( 329 assert set(manifest['background']['scripts']) == set(
322 scripts + ['devenvPoller__.js'] 330 scripts + ['devenvPoller__.js']
323 ) 331 )
324 else: 332 else:
325 assert set(manifest['background']['scripts']) == set(scripts) 333 assert set(manifest['background']['scripts']) == set(scripts)
326 334
327 335
328 def assert_base_files(package, platform): 336 def assert_base_files(package, platform, prefix):
329 filenames = set(package.namelist()) 337 filenames = set(package.namelist())
330 338
331 if platform in {'chrome', 'gecko'}: 339 if platform == 'edge':
332 assert 'bar.json' in filenames
333 assert 'manifest.json' in filenames
334 assert 'lib/foo.js' in filenames
335 assert 'foo/logo_50.png' in filenames
336 assert 'icons/logo_150.png' in filenames
337 else:
338 assert 'AppxManifest.xml' in filenames 340 assert 'AppxManifest.xml' in filenames
339 assert 'AppxBlockMap.xml' in filenames 341 assert 'AppxBlockMap.xml' in filenames
340 assert '[Content_Types].xml' in filenames 342 assert '[Content_Types].xml' in filenames
341 343
342 assert 'Extension/bar.json' in filenames
343 assert 'Extension/lib/foo.js' in filenames
344
345 assert package.read('Assets/logo_44.png') == '44' 344 assert package.read('Assets/logo_44.png') == '44'
346 assert package.read('Extension/icons/abp-44.png') == '44' 345 assert package.read('Extension/icons/abp-44.png') == '44'
346
347 assert os.path.join(prefix, 'bar.json') in filenames
348 assert os.path.join(prefix, 'manifest.json') in filenames
349 assert os.path.join(prefix, 'lib/foo.js') in filenames
350 assert os.path.join(prefix, 'foo/logo_50.png') in filenames
351 assert os.path.join(prefix, 'icons/logo_150.png') in filenames
347 352
348 353
349 def assert_chrome_signature(filename, keyfile): 354 def assert_chrome_signature(filename, keyfile):
350 with open(filename, 'r') as fp: 355 with open(filename, 'r') as fp:
351 content = fp.read() 356 content = fp.read()
352 357
353 _, _, l_pubkey, l_signature = unpack('<4sIII', content[:16]) 358 _, _, l_pubkey, l_signature = unpack('<4sIII', content[:16])
354 signature = content[16 + l_pubkey: 16 + l_pubkey + l_signature] 359 signature = content[16 + l_pubkey: 16 + l_pubkey + l_signature]
355 360
356 digest = SHA.new() 361 digest = SHA.new()
(...skipping 25 matching lines...) Expand all
382 387
383 @pytest.mark.usefixtures( 388 @pytest.mark.usefixtures(
384 'all_lang_locales', 389 'all_lang_locales',
385 'locale_modules', 390 'locale_modules',
386 'icons', 391 'icons',
387 'lib_files', 392 'lib_files',
388 'chrome_metadata', 393 'chrome_metadata',
389 'gecko_webext_metadata', 394 'gecko_webext_metadata',
390 'edge_metadata', 395 'edge_metadata',
391 ) 396 )
392 @pytest.mark.parametrize('platform,dev_build_release,buildnum', [ 397 @pytest.mark.parametrize('platform,command', [
393 ('chrome', 'build', '1337'), 398 ('chrome', 'development_build'),
394 ('chrome', 'devenv', None), 399 ('chrome', 'devenv'),
395 ('chrome', 'release', None), 400 ('chrome', 'release_build'),
396 ('gecko', 'build', '1337'), 401 ('gecko', 'development_build'),
397 ('gecko', 'devenv', None), 402 ('gecko', 'devenv'),
398 ('gecko', 'release', None), 403 ('gecko', 'release_build'),
399 ('edge', 'build', '1337'), 404 ('edge', 'development_build'),
400 pytest.param('edge', 'devenv', None, marks=pytest.mark.xfail), 405 ('edge', 'devenv'),
401 ('edge', 'release', None), 406 ('edge', 'release_build'),
402 ]) 407 ])
403 def test_build_webext(platform, dev_build_release, keyfile, tmpdir, srcdir, 408 def test_build_webext(platform, command, keyfile, tmpdir, srcdir, capsys):
404 buildnum, capsys): 409 release = command == 'release_build'
405 release = dev_build_release == 'release' 410 devenv = command == 'devenv'
406 devenv = dev_build_release == 'devenv'
407 411
408 if platform == 'chrome' and release: 412 if platform == 'chrome' and release:
409 key = keyfile 413 key = keyfile
410 else: 414 else:
411 key = None 415 key = None
412 416
413 manifests = { 417 manifests = {
414 'gecko': [('', 'manifest', 'json')], 418 'gecko': [('', 'manifest', 'json')],
415 'chrome': [('', 'manifest', 'json')], 419 'chrome': [('', 'manifest', 'json')],
416 'edge': [('', 'AppxManifest', 'xml'), 420 'edge': [('', 'AppxManifest', 'xml'),
417 ('Extension', 'manifest', 'json')], 421 ('Extension', 'manifest', 'json')],
418 } 422 }
419 423
420 filenames = { 424 filenames = {
421 'gecko': 'adblockplusfirefox-1.2.3{}.xpi', 425 'gecko': 'adblockplusfirefox-1.2.3{}.xpi',
422 'chrome': 'adblockpluschrome-1.2.3{{}}.{}'.format( 426 'chrome': 'adblockpluschrome-1.2.3{{}}.{}'.format(
423 {True: 'crx', False: 'zip'}[release] 427 {True: 'crx', False: 'zip'}[release]
424 ), 428 ),
425 'edge': 'adblockplusedge-1.2.3{}.appx', 429 'edge': 'adblockplusedge-1.2.3{}.appx',
426 } 430 }
427 431
428 if platform == 'edge': 432 if platform == 'edge':
429 prefix = 'Extension' 433 prefix = 'Extension'
430 else: 434 else:
431 prefix = '' 435 prefix = ''
432 436
433 run_webext_build(platform, dev_build_release, srcdir, buildnum=buildnum, 437 run_webext_build(platform, command, srcdir, keyfile=key)
434 keyfile=key)
435 438
436 # The makeIcons() in packagerChrome.py should warn about non-square 439 # The makeIcons() in packagerChrome.py should warn about non-square
437 # icons via stderr. 440 # icons via stderr.
438 if platform in {'chrome', 'gecko'}: 441 out, err = capsys.readouterr()
439 out, err = capsys.readouterr() 442 assert 'icon should be square' in err
440 assert 'icon should be square' in err
441 443
442 if devenv: 444 if devenv:
443 content_class = DirContent 445 content_class = DirContent
444 out_file_path = os.path.join(str(srcdir), 'devenv.' + platform) 446 out_file_path = os.path.join(str(srcdir), 'devenv.' + platform)
445 else: 447 else:
446 content_class = ZipContent 448 content_class = ZipContent
447 449
448 if release: 450 if release:
449 add_version = '' 451 add_version = ''
450 else: 452 else:
451 add_version = '.' + buildnum 453 add_version = '.1337'
452 454
453 out_file = filenames[platform].format(add_version) 455 out_file = filenames[platform].format(add_version)
454 456
455 out_file_path = os.path.abspath(os.path.join( 457 out_file_path = os.path.abspath(os.path.join(
456 os.path.dirname(__file__), os.pardir, out_file)) 458 os.path.dirname(__file__), os.pardir, out_file))
457 assert os.path.exists(out_file_path) 459 assert os.path.exists(out_file_path)
458 460
459 if release and platform == 'chrome': 461 if release and platform == 'chrome':
460 assert_chrome_signature(out_file_path, keyfile) 462 assert_chrome_signature(out_file_path, keyfile)
461 463
462 with content_class(out_file_path) as package: 464 with content_class(out_file_path) as package:
463 assert_base_files(package, platform) 465 assert_base_files(package, platform, prefix)
464 assert_all_locales_present(package, prefix) 466 assert_all_locales_present(package, prefix)
465 assert_webpack_bundle(package, prefix, platform == 'gecko') 467 assert_webpack_bundle(package, prefix, platform == 'gecko')
466 468
467 if platform == 'chrome': 469 if platform == 'chrome':
468 assert_locale_upfix(package) 470 assert_locale_upfix(package)
469 if platform in {'chrome', 'gecko'}: 471
470 assert_devenv_scripts(package, devenv) 472 assert_devenv_scripts(package, prefix, devenv)
471 473
472 for folder, name, ext in manifests[platform]: 474 for folder, name, ext in manifests[platform]:
473 filename = '{{}}_{}_{}.{{}}'.format(platform, dev_build_release) 475 filename = '{{}}_{}_{}.{{}}'.format(platform, command)
474 expected = os.path.join( 476 expected = os.path.join(
475 os.path.dirname(__file__), 477 os.path.dirname(__file__),
476 'expecteddata', 478 'expecteddata',
477 filename.format(name, ext), 479 filename.format(name, ext),
478 ) 480 )
479 481
480 assert_manifest_content( 482 assert_manifest_content(
481 package.read(os.path.join(folder, '{}.{}'.format(name, ext))), 483 package.read(os.path.join(folder, '{}.{}'.format(name, ext))),
482 expected, 484 expected,
483 ) 485 )
LEFTRIGHT

Powered by Google App Engine
This is Rietveld