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

Unified Diff: tests/tools.py

Issue 29501558: Issue 5383 - Add tests for the Chrome and Firefox packagers (Closed)
Patch Set: Simplifying parameters Created Sept. 21, 2017, 11:27 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tests/tools.py
diff --git a/tests/tools.py b/tests/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..7dfbfbc9463af0660bdfc7ad9b001f39e47c6035
--- /dev/null
+++ b/tests/tools.py
@@ -0,0 +1,198 @@
+# 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.build import processArgs
+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_opt, srcdir, buildnum=None, keyfile=None):
+ """Run a build process."""
+ if build_opt == 'build':
+ build_args = ['build']
+ elif build_opt == 'release':
+ build_args = ['build', '-r']
+ else:
+ build_args = ['devenv']
+
+ args = ['build.py', '-t', ext_type] + build_args
+
+ if keyfile:
+ args += ['-k', keyfile]
+ if buildnum:
+ args += ['-b', buildnum]
+
+ processArgs(str(srcdir), args)
+
+
+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))
Sebastian Noack 2017/09/22 01:56:05 This logic is duplicated. Why not just move it to
tlucas 2017/09/22 09:02:20 See https://codereview.adblockplus.org/29501558/di
Sebastian Noack 2017/09/22 18:31:04 I don't see how that discussion is related to the
Sebastian Noack 2017/09/23 21:11:41 If you just make make compare_{xml|json} return a
Sebastian Noack 2017/09/25 20:21:19 Yes, that is essentially what I suggested.
+ 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())
« tests/test_packagerWebExt.py ('K') | « tests/test_packagerWebExt.py ('k') | tox.ini » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld