| Index: tests/tools.py |
| diff --git a/tests/tools.py b/tests/tools.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d953c486c0c5481e6811ba594280ffebb3924c30 |
| --- /dev/null |
| +++ b/tests/tools.py |
| @@ -0,0 +1,206 @@ |
| +# 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 os |
| +import shutil |
| +import zipfile |
| +import json |
| +import difflib |
| + |
| +from xml.etree import ElementTree |
| + |
| +from buildtools.packagerChrome import defaultLocale |
| +from buildtools.tests.conftest import MESSAGES_EN_US |
| + |
| + |
| +ALL_LANGUAGES = ['en_US', 'de', 'it'] |
| + |
| + |
| +class Content(object): |
| + """Base class for a unified ZipFile / Directory interface. |
| + |
| + Base class for providing a unified context manager interface for |
| + accessing files. This class is subclassed by ZipContent and DirContent, |
| + which provide the additional methods "namelist()" and "read(path)". |
| + """ |
| + |
| + def __enter__(self): |
| + return self |
| + |
| + def __exit__(self, exc_type, exc_value, exc_tb): |
| + self._close() |
| + |
| + |
| +class ZipContent(Content): |
| + """Provides a unified context manager for ZipFile access. |
| + |
| + Inherits the context manager API from Content. |
| + If desired, the specified ZipFile is deleted on exiting the manager. |
| + """ |
| + |
| + def __init__(self, zip_path, delete_on_close=True): |
| + """Constructor of ZipContent handling the file <zip_path>. |
| + |
| + The parameter 'delete_on_close' causes the context manager to |
| + delete the handled ZipFile (specified by zip_path) if set to |
| + True (default). |
| + """ |
| + |
| + self._zip_path = zip_path |
| + self._zip_file = zipfile.ZipFile(zip_path) |
| + self._delete_on_close = delete_on_close |
| + super(ZipContent, self).__init__() |
| + |
| + def _close(self): |
| + self._zip_file.close() |
| + if self._delete_on_close: |
| + # if specified, delete the handled file |
| + os.remove(self._zip_path) |
| + |
| + def namelist(self): |
| + return self._zip_file.namelist() |
| + |
| + def read(self, path): |
| + return self._zip_file.read(path) |
| + |
| + |
| +class DirContent(Content): |
| + """Provides a unified context manager for directory access. |
| + |
| + Inherits the context managet API from Content. |
| + """ |
| + |
| + def __init__(self, path): |
| + """Constructor of DirContent handling <path>. |
| + """ |
| + |
| + self._path = path |
| + super(DirContent, self).__init__() |
| + |
| + def _close(self): |
| + pass |
| + |
| + def namelist(self): |
| + """Generate a list of filenames, as expected from ZipFile.nameslist(). |
| + """ |
| + |
| + result = [] |
| + for parent, directories, files in os.walk(self._path): |
| + for filename in files: |
| + file_path = os.path.join(parent, filename) |
| + rel_path = os.path.relpath(file_path, self._path) |
| + result.append(rel_path) |
| + return result |
| + |
| + def read(self, path): |
| + content = None |
| + with open(os.path.join(self._path, path)) as fp: |
| + content = fp.read() |
| + return content |
| + |
| + |
| +def copy_metadata(filename, tmpdir): |
| + """Copy the used metadata to the used temporary directory.""" |
| + path = os.path.join(os.path.dirname(__file__), filename) |
| + destination = str(tmpdir.join(filename)) |
| + shutil.copy(path, destination) |
| + |
| + |
| +def run_webext_build(ext_type, build_type, srcdir, packager_module, |
| + buildnum=None, keyfile=None): |
| + """Run a build process. |
| + |
| + Run a desired build process without unintenionally invoking the |
| + releaseAutomation. |
| + """ |
| + from buildtools.build import processArgs |
| + args = ['build.py', '-t', ext_type, build_type] |
| + |
| + if keyfile: |
| + args += ['-k', keyfile] |
| + if buildnum: |
| + args += ['-b', buildnum] |
| + |
| + if not build_type == 'release': |
|
Sebastian Noack
2017/09/20 21:36:12
Nit: `a != b` instead of `not a == b`
Is flake8 c
tlucas
2017/09/21 11:34:56
It is - along with all plugins. Since there is no
|
| + processArgs(str(srcdir), args) |
| + else: |
| + # We don't want to accidentaly run releaseAutomation in tests |
| + packager_module.createBuild( |
| + str(srcdir), |
| + ext_type, |
| + releaseBuild=True, |
| + keyFile=keyfile, |
| + buildNum=buildnum, |
| + ) |
| + |
| + |
| +def locale_files(languages, rootfolder, srcdir): |
| + """Generate example locales. |
| + |
| + languages: tuple, list or set of languages to include |
| + rootdir: folder-name to create the locale-folders in |
| + """ |
| + for lang in languages: |
| + subfolders = rootfolder.split(os.pathsep) + [lang, 'messages.json'] |
| + json_file = srcdir.ensure(*subfolders) |
| + if lang == defaultLocale: |
| + json_file.write(MESSAGES_EN_US) |
| + else: |
| + json_file.write('{}') |
| + |
| + |
| +def assert_all_locales_present(package, locales_path): |
| + names = {x for x in package.namelist() if x.startswith(locales_path)} |
| + assert names == { |
| + os.path.join(locales_path, lang, 'messages.json') |
| + for lang in ALL_LANGUAGES |
| + } |
| + |
| + |
| +def compare_xml(a, b): |
| + """Assert equal content in two manifests in XML format.""" |
| + def get_leafs_string(tree): |
| + """Recursively build a string representing all xml leaf-nodes.""" |
| + root_str = '{}|{}|{}'.format(tree.tag, tree.tail, tree.text).strip() |
| + result = [] |
| + |
| + if len(tree) > 0: |
| + for subtree in tree: |
| + for leaf in get_leafs_string(subtree): |
| + result.append('{}__{}'.format(root_str, leaf)) |
| + for k, v in tree.attrib.items(): |
| + result.append('{}__{}:{}'.format(root_str, k, v)) |
| + return result |
| + |
| + # XML data itself shall not be sorted, hence we can safely sort |
| + # our string representations in order to produce a valid unified diff. |
| + strs_a = sorted(get_leafs_string(ElementTree.fromstring(a))) |
| + strs_b = sorted(get_leafs_string(ElementTree.fromstring(b))) |
| + |
| + diff = list(difflib.unified_diff(strs_a, strs_b, n=0)) |
| + assert len(diff) == 0, '\n'.join(diff) |
| + |
| + |
| +def compare_json(a, b): |
| + """Assert equal content in two manifests in JSON format. |
| + |
| + Compare the content of two JSON strings, respecting the order of items in |
| + a list as well as possible duplicates. |
| + Raise a unified diff if equality could not be asserted. |
| + """ |
| + json_a = json.dumps(json.loads(a), sort_keys=True, indent=0).split('\n') |
| + json_b = json.dumps(json.loads(b), sort_keys=True, indent=0).split('\n') |
| + |
| + diff = list(difflib.unified_diff(json_a, json_b, n=0)) |
| + assert len(diff) == 0, '\n'.join(diff) |
| + |
| + |
| +def assert_manifest_content(manifest, expected_path): |
| + extension = os.path.basename(expected_path).split('.')[-1] |
| + |
| + with open(expected_path, 'r') as fp: |
| + if extension == 'xml': |
| + compare_xml(manifest, fp.read()) |
| + else: |
| + compare_json(manifest, fp.read()) |