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