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

Side by Side Diff: tests/tools.py

Issue 29501558: Issue 5383 - Add tests for the Chrome and Firefox packagers (Closed)
Patch Set: Adressing comments Created Sept. 20, 2017, 8:47 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5 import os
6 import shutil
7 import zipfile
8 import json
9 import difflib
10
11 from xml.etree import ElementTree
12
13 from buildtools.packagerChrome import defaultLocale
14 from buildtools.tests.conftest import MESSAGES_EN_US
15
16
17 ALL_LANGUAGES = ['en_US', 'de', 'it']
18
19
20 class Content(object):
21 """Base class for a unified ZipFile / Directory interface.
22
23 Base class for providing a unified context manager interface for
24 accessing files. This class is subclassed by ZipContent and DirContent,
25 which provide the additional methods "namelist()" and "read(path)".
26 """
27
28 def __enter__(self):
29 return self
30
31 def __exit__(self, exc_type, exc_value, exc_tb):
32 self._close()
33
34
35 class ZipContent(Content):
36 """Provides a unified context manager for ZipFile access.
37
38 Inherits the context manager API from Content.
39 If desired, the specified ZipFile is deleted on exiting the manager.
40 """
41
42 def __init__(self, zip_path, delete_on_close=True):
43 """Constructor of ZipContent handling the file <zip_path>.
44
45 The parameter 'delete_on_close' causes the context manager to
46 delete the handled ZipFile (specified by zip_path) if set to
47 True (default).
48 """
49
50 self._zip_path = zip_path
51 self._zip_file = zipfile.ZipFile(zip_path)
52 self._delete_on_close = delete_on_close
53 super(ZipContent, self).__init__()
54
55 def _close(self):
56 self._zip_file.close()
57 if self._delete_on_close:
58 # if specified, delete the handled file
59 os.remove(self._zip_path)
60
61 def namelist(self):
62 return self._zip_file.namelist()
63
64 def read(self, path):
65 return self._zip_file.read(path)
66
67
68 class DirContent(Content):
69 """Provides a unified context manager for directory access.
70
71 Inherits the context managet API from Content.
72 """
73
74 def __init__(self, path):
75 """Constructor of DirContent handling <path>.
76 """
77
78 self._path = path
79 super(DirContent, self).__init__()
80
81 def _close(self):
82 pass
83
84 def namelist(self):
85 """Generate a list of filenames, as expected from ZipFile.nameslist().
86 """
87
88 result = []
89 for parent, directories, files in os.walk(self._path):
90 for filename in files:
91 file_path = os.path.join(parent, filename)
92 rel_path = os.path.relpath(file_path, self._path)
93 result.append(rel_path)
94 return result
95
96 def read(self, path):
97 content = None
98 with open(os.path.join(self._path, path)) as fp:
99 content = fp.read()
100 return content
101
102
103 def copy_metadata(filename, tmpdir):
104 """Copy the used metadata to the used temporary directory."""
105 path = os.path.join(os.path.dirname(__file__), filename)
106 destination = str(tmpdir.join(filename))
107 shutil.copy(path, destination)
108
109
110 def run_webext_build(ext_type, build_type, srcdir, packager_module,
111 buildnum=None, keyfile=None):
112 """Run a build process.
113
114 Run a desired build process without unintenionally invoking the
115 releaseAutomation.
116 """
117 from buildtools.build import processArgs
118 args = ['build.py', '-t', ext_type, build_type]
119
120 if keyfile:
121 args += ['-k', keyfile]
122 if buildnum:
123 args += ['-b', buildnum]
124
125 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
126 processArgs(str(srcdir), args)
127 else:
128 # We don't want to accidentaly run releaseAutomation in tests
129 packager_module.createBuild(
130 str(srcdir),
131 ext_type,
132 releaseBuild=True,
133 keyFile=keyfile,
134 buildNum=buildnum,
135 )
136
137
138 def locale_files(languages, rootfolder, srcdir):
139 """Generate example locales.
140
141 languages: tuple, list or set of languages to include
142 rootdir: folder-name to create the locale-folders in
143 """
144 for lang in languages:
145 subfolders = rootfolder.split(os.pathsep) + [lang, 'messages.json']
146 json_file = srcdir.ensure(*subfolders)
147 if lang == defaultLocale:
148 json_file.write(MESSAGES_EN_US)
149 else:
150 json_file.write('{}')
151
152
153 def assert_all_locales_present(package, locales_path):
154 names = {x for x in package.namelist() if x.startswith(locales_path)}
155 assert names == {
156 os.path.join(locales_path, lang, 'messages.json')
157 for lang in ALL_LANGUAGES
158 }
159
160
161 def compare_xml(a, b):
162 """Assert equal content in two manifests in XML format."""
163 def get_leafs_string(tree):
164 """Recursively build a string representing all xml leaf-nodes."""
165 root_str = '{}|{}|{}'.format(tree.tag, tree.tail, tree.text).strip()
166 result = []
167
168 if len(tree) > 0:
169 for subtree in tree:
170 for leaf in get_leafs_string(subtree):
171 result.append('{}__{}'.format(root_str, leaf))
172 for k, v in tree.attrib.items():
173 result.append('{}__{}:{}'.format(root_str, k, v))
174 return result
175
176 # XML data itself shall not be sorted, hence we can safely sort
177 # our string representations in order to produce a valid unified diff.
178 strs_a = sorted(get_leafs_string(ElementTree.fromstring(a)))
179 strs_b = sorted(get_leafs_string(ElementTree.fromstring(b)))
180
181 diff = list(difflib.unified_diff(strs_a, strs_b, n=0))
182 assert len(diff) == 0, '\n'.join(diff)
183
184
185 def compare_json(a, b):
186 """Assert equal content in two manifests in JSON format.
187
188 Compare the content of two JSON strings, respecting the order of items in
189 a list as well as possible duplicates.
190 Raise a unified diff if equality could not be asserted.
191 """
192 json_a = json.dumps(json.loads(a), sort_keys=True, indent=0).split('\n')
193 json_b = json.dumps(json.loads(b), sort_keys=True, indent=0).split('\n')
194
195 diff = list(difflib.unified_diff(json_a, json_b, n=0))
196 assert len(diff) == 0, '\n'.join(diff)
197
198
199 def assert_manifest_content(manifest, expected_path):
200 extension = os.path.basename(expected_path).split('.')[-1]
201
202 with open(expected_path, 'r') as fp:
203 if extension == 'xml':
204 compare_xml(manifest, fp.read())
205 else:
206 compare_json(manifest, fp.read())
OLDNEW
« packagerChrome.py ('K') | « tests/test_packagerWebExt.py ('k') | tox.ini » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld