 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) 
  | Index: tests/test_packagerGecko.py | 
| diff --git a/tests/test_packagerGecko.py b/tests/test_packagerGecko.py | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..f7df16734762a8b63a7cf3a2d2f9f408adc4faef | 
| --- /dev/null | 
| +++ b/tests/test_packagerGecko.py | 
| @@ -0,0 +1,339 @@ | 
| +# This Source Code Form is subject to the terms of the Mozilla Public | 
| +# License, v. 2.0. If a copy of the MPL was not distributed with this | 
| +# file, You can obtain one at http://mozilla.org/MPL/2.0/. | 
| + | 
| + | 
| +import pytest | 
| + | 
| +import json | 
| + | 
| +from zipfile import ZipFile | 
| + | 
| +from xml.etree import ElementTree | 
| +from itertools import product | 
| + | 
| +from buildtools import packagerGecko | 
| +from buildtools import localeTools | 
| + | 
| +from buildtools.packager import readMetadata, getBuildVersion, Files | 
| +from functools import reduce | 
| + | 
| +TR_FA = [True, False] | 
| + | 
| +MESSAGES = '\n'.join(( | 
| + 'name=Name {0}', | 
| + 'description=Awesome description {0}', | 
| +)) | 
| + | 
| + | 
| +@pytest.fixture | 
| +def scripts(tmp_dir): | 
| + """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.
 | 
| + lib_dir = tmp_dir.mkdir('lib') | 
| + lib_dir.join('ext.js').write('require("hooks");') | 
| + | 
| + content_dir = tmp_dir.mkdir('chrome').mkdir('content') | 
| + content_dir.join('common.js').write('require("hooks");') | 
| + | 
| + | 
| +@pytest.fixture | 
| +def prefs_json(tmp_dir): | 
| + """Minimal .json file for testing processJSONFiles""" | 
| + lib_dir = tmp_dir.mkdir('lib') | 
| + lib_dir.join('prefs.json').write(json.dumps( | 
| + {'foo': 'bar'} | 
| + )) | 
| + | 
| + | 
| +@pytest.fixture | 
| +def locales(tmp_dir): | 
| + """Minimal locales for testing locale-processing""" | 
| + chrome_dir = tmp_dir.mkdir('chrome') | 
| + locale_dir = chrome_dir.mkdir('locale') | 
| + | 
| + data = { | 
| + 'name': {'message': 'Name translated'}, | 
| + '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.
 | 
| + } | 
| + | 
| + for locale in ['en-US', 'de', 'kn']: | 
| + new_dir = locale_dir.mkdir(locale) | 
| + new_dir.join('meta.properties').write(MESSAGES.format(locale)) | 
| + new_dir.join('test.json').write(json.dumps(data)) | 
| + if locale == 'kn': | 
| + new_dir.join('.incomplete').write('') | 
| + | 
| + | 
| +@pytest.fixture | 
| +def subscriptions(tmp_dir): | 
| + """Examplary sbuscription-configuration""" | 
| + 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.
 | 
| + '<subscriptions>', | 
| + '<subscription title="EasyList"', | 
| + 'specialization="English"', | 
| + 'url="https://easylist-downloads.adblockplus.org/easylist.txt"', | 
| + 'homepage="https://easylist.adblockplus.org/"', | 
| + 'prefixes="en"', | 
| + 'author="fanboy, MonztA, Famlam, Khrin"', | 
| + 'type="ads"/>', | 
| + '</subscriptions>', | 
| + ))) | 
| + | 
| + | 
| +def test_package_files(tmpdir): | 
| + tmpdir.join('foo.xml').write('') | 
| + tmpdir.join('foo.txt').write('') | 
| + tmpdir.join('foo.js').write('') | 
| + | 
| + params = { | 
| + 'baseDir': str(tmpdir) | 
| + } | 
| + | 
| + files = packagerGecko.getPackageFiles(params) | 
| + assert 'foo.xml' in files | 
| + assert 'foo.js' in files | 
| + assert 'foo.txt' not in files | 
| + | 
| + | 
| +@pytest.mark.usefixtures('locales') | 
| +def test_get_locales(tmp_dir): | 
| + 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.
 | 
| + locales = packagerGecko.getLocales(str(tmp_dir), incomplete) | 
| + | 
| + assert 'de' in locales | 
| + assert 'en-US' in locales | 
| + assert ('kn' in locales) == incomplete | 
| + | 
| + | 
| +@pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) | 
| +@pytest.mark.usefixtures('locales', 'metadata_files', 'subscriptions') | 
| +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.
 | 
| + def first(elem): | 
| + return elem[0] | 
| + | 
| + def text(elem): | 
| + return elem.text | 
| + | 
| + def iteritems(func=None): | 
| + def wrapper(elements): | 
| + for elem in elements: | 
| + if func: | 
| + yield func(elem) | 
| + else: | 
| + yield elem | 
| + return wrapper | 
| + | 
| + metadata = readMetadata(str(tmp_dir), 'gecko') | 
| + locales = packagerGecko.getLocales(str(tmp_dir)) | 
| + contributors = packagerGecko.getContributors(metadata) | 
| + | 
| + namespaces = { | 
| + 'em': 'http://www.mozilla.org/2004/em-rdf#', | 
| + 'ns': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', | 
| + } | 
| + | 
| + base = [ | 
| + ('.//*', [len], 54), | 
| + ('./ns:Description/em:id', [first, text], | 
| + '{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}'), | 
| + ('./ns:Description/em:optionsURL', [first, text], | 
| + 'chrome://adblockplus/content/ui/settings.xul'), | 
| + ('./ns:Description/em:optionsType', [first, text], '2'), | 
| + ('./ns:Description/em:bootstrap', [first, text], 'true'), | 
| + ('./ns:Description/em:multiprocessCompatible', [first, text], 'true'), | 
| + ('./ns:Description/em:homepageURL', [first, text], | 
| + 'http://adblockplus.org/'), | 
| + ('./ns:Description/em:creator', [first, text], 'Wladimir Palant'), | 
| + ('./ns:Description/em:contributor', [iteritems(text)], | 
| + ['Pety Pete', 'Neil Armstrong', 'Famlam', 'fanboy', 'Khrin', | 
| + 'MonztA']), | 
| + ] | 
| + | 
| + base += [ | 
| + ('./ns:Description/em:localized/ns:Description[em:locale="{}"]/em:{}' | 
| + .format(locale, tag), | 
| + [first, text], | 
| + value.format(locale)) | 
| + for locale in locales | 
| + for tag, value in [ | 
| + ('name', 'Name {}'), | 
| + ('description', 'Awesome description {}') | 
| + ] | 
| + ] | 
| + | 
| + tags = ['minVersion', 'maxVersion'] | 
| + apps = metadata.items('compat') | 
| + comp = [ | 
| + ( | 
| + packagerGecko.KNOWN_APPS.get(app[0]), | 
| + tags[i], | 
| + app[1].split('/')[i] | 
| + ) for app in apps for i in range(2) | 
| + ] | 
| + | 
| + base += [ | 
| + (''.join(( | 
| + './ns:Description/em:targetApplication/', | 
| + 'ns:Description[em:id="{}"]/em:{}' | 
| + )) | 
| + .format(mapped_id, tag), | 
| + [first, text], | 
| + value) | 
| + for mapped_id, tag, value in comp | 
| + ] | 
| + | 
| + 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.
 | 
| + version = getBuildVersion(str(tmp_dir), metadata, release, None) | 
| + expected = base + [ | 
| + ('./ns:Description/em:version', [first, text], version), | 
| + ] | 
| + params = { | 
| + 'baseDir': str(tmp_dir), | 
| + 'locales': locales, | 
| + 'metadata': metadata, | 
| + 'version': version.encode('utf-8'), | 
| + 'multicompartment': multicompartment, | 
| + 'contributors': contributors | 
| + } | 
| + manifest = packagerGecko.createManifest(params) | 
| + | 
| + tree = ElementTree.fromstring(manifest) | 
| + | 
| + 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).
 | 
| + fp.write(manifest) | 
| + | 
| + for expression, modifiers, value in expected: | 
| + res = reduce( | 
| + lambda val, func: func(val), | 
| + modifiers, | 
| + tree.findall(expression, namespaces=namespaces)) | 
| + | 
| + from collections import Iterable | 
| + | 
| + if isinstance(res, Iterable) and not isinstance(res, str): | 
| + res = list(res) | 
| + 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.
 | 
| + assert x in value | 
| + for x in value: | 
| + assert x in res | 
| + assert res == value | 
| + | 
| + | 
| +@pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) | 
| +@pytest.mark.usefixtures('locales', 'metadata_files') | 
| +def test_fixup_import_locales(files, tmp_dir): | 
| + locale_dir = tmp_dir.dirpath(tmp_dir.basename, 'chrome', 'locale') | 
| + locale_dir.mkdir('fr').join('meta.properties')\ | 
| + .write(MESSAGES.format('fr')) | 
| + | 
| + metadata = readMetadata(str(tmp_dir), 'gecko') | 
| + locales = packagerGecko.getLocales(str(tmp_dir), False) | 
| + | 
| + params = { | 
| + 'metadata': metadata, | 
| + 'locales': locales, | 
| + 'baseDir': str(tmp_dir) | 
| + } | 
| + | 
| + # Should add missing fr/test.properties to files | 
| + packagerGecko.fixupLocales(params, files) | 
| + | 
| + packagerGecko.importLocales(params, files) | 
| + for locale in locales: | 
| + properties = files['chrome/locale/{}/test.properties'.format(locale)] | 
| + translation_data = list( | 
| + localeTools.parsePropertiesString(properties, '')) | 
| + | 
| + for trans in [ | 
| + ('name', None, 'Name translated'), | 
| + ('description', None, 'Description translated')]: | 
| + assert trans in translation_data | 
| + | 
| + | 
| +def test_process_json_files(tmp_dir, prefs_json): | 
| + params = { | 
| + 'baseDir': str(tmp_dir), | 
| + 'jsonRequires': {}, | 
| + } | 
| + | 
| + files = Files(packagerGecko.getPackageFiles(params), set()) | 
| + files.read(str(tmp_dir)) | 
| + | 
| + packagerGecko.processJSONFiles(params, files) | 
| + | 
| + assert params['jsonRequires'] == {'prefs.json': {'foo': 'bar'}} | 
| + | 
| + | 
| +@pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) | 
| +@pytest.mark.usefixtures('scripts', 'metadata_files') | 
| +def test_add_missing_files(tmp_dir): | 
| + metadata = readMetadata(str(tmp_dir), 'gecko') | 
| + | 
| + params = { | 
| + 'baseDir': str(tmp_dir), | 
| + 'metadata': metadata, | 
| + 'jsonRequires': {}, | 
| + 'multicompartment': True, | 
| + 'hasWebExtension': False, | 
| + } | 
| + | 
| + files = Files(packagerGecko.getPackageFiles(params), set()) | 
| + files.read(str(tmp_dir)) | 
| + | 
| + packagerGecko.addMissingFiles(params, files) | 
| + | 
| + assert 'let shutdownHandlers = [];' in files['bootstrap.js'] | 
| + assert 'Services.obs.addObserver(' in files['bootstrap.js'] | 
| + for filename in ['bootstrap.js', 'chrome/content/common.js', | 
| + 'lib/ext.js', 'lib/hooks.js']: | 
| + assert filename in files | 
| + | 
| + | 
| +@pytest.mark.parametrize('metadata_files', ['metadata.gecko'], indirect=True) | 
| +@pytest.mark.usefixtures('metadata_files', 'locales', 'subscriptions') | 
| +def test_create_build(tmp_dir, capsys): | 
| + base_files = [ | 
| + 'bootstrap.js', | 
| + 'chrome/locale/de/test.json', | 
| + 'chrome/locale/de/test.properties', | 
| + 'chrome/locale/en-US/test.json', | 
| + 'chrome/locale/en-US/test.properties', | 
| + 'install.rdf', | 
| + 'subs.xml' | 
| + ] | 
| + | 
| + 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.
 | 
| + ['all', None], TR_FA, TR_FA): | 
| + | 
| + if all_locales is None: | 
| + expected = base_files | 
| + else: | 
| + expected = base_files + [ | 
| + 'chrome/locale/kn/test.json', | 
| + 'chrome/locale/kn/test.properties' | 
| + ] | 
| + | 
| + out_file = tmp_dir.join('{}_{}_{}.zip'.format( | 
| + all_locales, release, multicompartment)) | 
| + | 
| + out_file = str(out_file) | 
| + | 
| + packagerGecko.createBuild( | 
| + str(tmp_dir), | 
| + locales=all_locales, | 
| + outFile=out_file, | 
| + releaseBuild=release, | 
| + multicompartment=multicompartment) | 
| + | 
| + out, err = capsys.readouterr() | 
| + | 
| + assert err ==\ | 
| + "Warning: Mapped file adblockplusui/firstRun.html doesn't exist\n" | 
| + | 
| + with ZipFile(out_file, 'r') as zipfp: | 
| + zipfp.testzip() | 
| + | 
| + filenames = [zipinfo.filename for zipinfo in zipfp.infolist()] | 
| + | 
| + for name in expected: | 
| + assert name in filenames |