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 |