addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:94d690665ec3
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 unittest
7 import zipfile
8 import pprint
9 import shutil
10
11 import simplejson as json
12 from cuddlefish import xpi, packaging, manifest, buildJID
13 from cuddlefish.tests import test_packaging
14 from test_linker import up
15
16 import xml.etree.ElementTree as ElementTree
17
18 xpi_template_path = os.path.join(test_packaging.static_files_path,
19 'xpi-template')
20
21 fake_manifest = '<RDF><!-- Extension metadata is here. --></RDF>'
22
23 class PrefsTests(unittest.TestCase):
24 def makexpi(self, pkg_name):
25 self.xpiname = "%s.xpi" % pkg_name
26 create_xpi(self.xpiname, pkg_name, 'preferences-files')
27 self.xpi = zipfile.ZipFile(self.xpiname, 'r')
28 options = self.xpi.read('harness-options.json')
29 self.xpi_harness_options = json.loads(options)
30
31 def setUp(self):
32 self.xpiname = None
33 self.xpi = None
34
35 def tearDown(self):
36 if self.xpi:
37 self.xpi.close()
38 if self.xpiname and os.path.exists(self.xpiname):
39 os.remove(self.xpiname)
40
41 def testPackageWithSimplePrefs(self):
42 self.makexpi('simple-prefs')
43 packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
44 self.failUnless('options.xul' in self.xpi.namelist())
45 optsxul = self.xpi.read('options.xul').decode("utf-8")
46 self.failUnlessEqual(self.xpi_harness_options["jetpackID"], packageName)
47 self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], packageName)
48
49 root = ElementTree.XML(optsxul.encode('utf-8'))
50
51 xulNamespacePrefix = \
52 "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
53
54 settings = root.findall(xulNamespacePrefix + 'setting')
55
56 def assertPref(setting, name, prefType, title):
57 self.failUnlessEqual(setting.get('data-jetpack-id'), packageName)
58 self.failUnlessEqual(setting.get('pref'),
59 'extensions.' + packageName + '.' + name)
60 self.failUnlessEqual(setting.get('pref-name'), name)
61 self.failUnlessEqual(setting.get('type'), prefType)
62 self.failUnlessEqual(setting.get('title'), title)
63
64 assertPref(settings[0], 'test', 'bool', u't\u00EBst')
65 assertPref(settings[1], 'test2', 'string', u't\u00EBst')
66 assertPref(settings[2], 'test3', 'menulist', '"><test')
67 assertPref(settings[3], 'test4', 'radio', u't\u00EBst')
68
69 menuItems = settings[2].findall(
70 '%(0)smenulist/%(0)smenupopup/%(0)smenuitem' % { "0": xulNamespacePrefix })
71 radios = settings[3].findall(
72 '%(0)sradiogroup/%(0)sradio' % { "0": xulNamespacePrefix })
73
74 def assertOption(option, value, label):
75 self.failUnlessEqual(option.get('value'), value)
76 self.failUnlessEqual(option.get('label'), label)
77
78 assertOption(menuItems[0], "0", "label1")
79 assertOption(menuItems[1], "1", "label2")
80 assertOption(radios[0], "red", "rouge")
81 assertOption(radios[1], "blue", "bleu")
82
83 prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
84 exp = [u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test", false);',
85 u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test2", "\u00FCnic\u00F8d\u00E9");',
86 u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test3", "1");',
87 u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test4", "red");',
88 ]
89 self.failUnlessEqual(prefsjs, "\n".join(exp)+"\n")
90
91 def testPackageWithPreferencesBranch(self):
92 self.makexpi('preferences-branch')
93 self.failUnless('options.xul' in self.xpi.namelist())
94 optsxul = self.xpi.read('options.xul').decode("utf-8")
95 self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"],
96 "human-readable")
97
98 root = ElementTree.XML(optsxul.encode('utf-8'))
99 xulNamespacePrefix = \
100 "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
101
102 setting = root.find(xulNamespacePrefix + 'setting')
103 self.failUnlessEqual(setting.get('pref'),
104 'extensions.human-readable.test42')
105
106 prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
107 self.failUnlessEqual(prefsjs,
108 'pref("extensions.human-readable.test42", true);\n')
109
110 def testPackageWithNoPrefs(self):
111 self.makexpi('no-prefs')
112 self.failIf('options.xul' in self.xpi.namelist())
113 self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
114 "jid1-fZHqN9JfrDBa8A@jetpack")
115 prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
116 self.failUnlessEqual(prefsjs, "")
117
118 def testPackageWithInvalidPreferencesBranch(self):
119 self.makexpi('curly-id')
120 self.failIfEqual(self.xpi_harness_options["preferencesBranch"],
121 "invalid^branch*name")
122 self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"],
123 "{34a1eae1-c20a-464f-9b0e-000000000000}")
124
125 def testPackageWithCurlyID(self):
126 self.makexpi('curly-id')
127 self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
128 "{34a1eae1-c20a-464f-9b0e-000000000000}")
129
130 self.failUnless('options.xul' in self.xpi.namelist())
131 optsxul = self.xpi.read('options.xul').decode("utf-8")
132
133 root = ElementTree.XML(optsxul.encode('utf-8'))
134 xulNamespacePrefix = \
135 "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
136
137 setting = root.find(xulNamespacePrefix + 'setting')
138 self.failUnlessEqual(setting.get('pref'),
139 'extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13')
140
141 prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
142 self.failUnlessEqual(prefsjs,
143 'pref("extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13", 26);\n')
144
145
146 class Bug588119Tests(unittest.TestCase):
147 def makexpi(self, pkg_name):
148 self.xpiname = "%s.xpi" % pkg_name
149 create_xpi(self.xpiname, pkg_name, 'bug-588119-files')
150 self.xpi = zipfile.ZipFile(self.xpiname, 'r')
151 options = self.xpi.read('harness-options.json')
152 self.xpi_harness_options = json.loads(options)
153
154 def setUp(self):
155 self.xpiname = None
156 self.xpi = None
157
158 def tearDown(self):
159 if self.xpi:
160 self.xpi.close()
161 if self.xpiname and os.path.exists(self.xpiname):
162 os.remove(self.xpiname)
163
164 def testPackageWithImplicitIcon(self):
165 self.makexpi('implicit-icon')
166 assert 'icon.png' in self.xpi.namelist()
167
168 def testPackageWithImplicitIcon64(self):
169 self.makexpi('implicit-icon')
170 assert 'icon64.png' in self.xpi.namelist()
171
172 def testPackageWithExplicitIcon(self):
173 self.makexpi('explicit-icon')
174 assert 'icon.png' in self.xpi.namelist()
175
176 def testPackageWithExplicitIcon64(self):
177 self.makexpi('explicit-icon')
178 assert 'icon64.png' in self.xpi.namelist()
179
180 def testPackageWithNoIcon(self):
181 self.makexpi('no-icon')
182 assert 'icon.png' not in self.xpi.namelist()
183
184 def testIconPathNotInHarnessOptions(self):
185 self.makexpi('implicit-icon')
186 assert 'icon' not in self.xpi_harness_options
187
188 def testIcon64PathNotInHarnessOptions(self):
189 self.makexpi('implicit-icon')
190 assert 'icon64' not in self.xpi_harness_options
191
192 class ExtraHarnessOptions(unittest.TestCase):
193 def setUp(self):
194 self.xpiname = None
195 self.xpi = None
196
197 def tearDown(self):
198 if self.xpi:
199 self.xpi.close()
200 if self.xpiname and os.path.exists(self.xpiname):
201 os.remove(self.xpiname)
202
203 def testOptions(self):
204 pkg_name = "extra-options"
205 self.xpiname = "%s.xpi" % pkg_name
206 create_xpi(self.xpiname, pkg_name, "bug-669274-files",
207 extra_harness_options={"builderVersion": "futuristic"})
208 self.xpi = zipfile.ZipFile(self.xpiname, 'r')
209 options = self.xpi.read('harness-options.json')
210 hopts = json.loads(options)
211 self.failUnless("builderVersion" in hopts)
212 self.failUnlessEqual(hopts["builderVersion"], "futuristic")
213
214 def testBadOptionName(self):
215 pkg_name = "extra-options"
216 self.xpiname = "%s.xpi" % pkg_name
217 self.failUnlessRaises(xpi.HarnessOptionAlreadyDefinedError,
218 create_xpi,
219 self.xpiname, pkg_name, "bug-669274-files",
220 extra_harness_options={"main": "already in use"})
221
222 class SmallXPI(unittest.TestCase):
223 def setUp(self):
224 self.root = up(os.path.abspath(__file__), 4)
225 def get_linker_files_dir(self, name):
226 return os.path.join(up(os.path.abspath(__file__)), "linker-files", name)
227 def get_pkg(self, name):
228 d = self.get_linker_files_dir(name)
229 return packaging.get_config_in_dir(d)
230
231 def get_basedir(self):
232 return os.path.join(".test_tmp", self.id())
233 def make_basedir(self):
234 basedir = self.get_basedir()
235 if os.path.isdir(basedir):
236 here = os.path.abspath(os.getcwd())
237 assert os.path.abspath(basedir).startswith(here) # safety
238 shutil.rmtree(basedir)
239 os.makedirs(basedir)
240 return basedir
241
242 def test_contents(self):
243 target_cfg = self.get_pkg("three")
244 package_path = [self.get_linker_files_dir("three-deps")]
245 pkg_cfg = packaging.build_config(self.root, target_cfg,
246 packagepath=package_path)
247 deps = packaging.get_deps_for_targets(pkg_cfg,
248 [target_cfg.name, "addon-sdk"])
249 addon_sdk_dir = pkg_cfg.packages["addon-sdk"].lib[0]
250 m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
251 used_files = list(m.get_used_files(True))
252 here = up(os.path.abspath(__file__))
253 def absify(*parts):
254 fn = os.path.join(here, "linker-files", *parts)
255 return os.path.abspath(fn)
256 expected = [absify(*parts) for parts in
257 [("three", "lib", "main.js"),
258 ("three-deps", "three-a", "lib", "main.js"),
259 ("three-deps", "three-a", "lib", "subdir", "subfile.js"),
260 ("three", "data", "msg.txt"),
261 ("three", "data", "subdir", "submsg.txt"),
262 ("three-deps", "three-b", "lib", "main.js"),
263 ("three-deps", "three-c", "lib", "main.js"),
264 ("three-deps", "three-c", "lib", "sub", "foo.js")
265 ]]
266
267 add_addon_sdk= lambda path: os.path.join(addon_sdk_dir, path)
268 expected.extend([add_addon_sdk(module) for module in [
269 os.path.join("sdk", "self.js"),
270 os.path.join("sdk", "core", "promise.js"),
271 os.path.join("sdk", "net", "url.js"),
272 os.path.join("sdk", "util", "object.js"),
273 os.path.join("sdk", "util", "array.js")
274 ]])
275
276 missing = set(expected) - set(used_files)
277 extra = set(used_files) - set(expected)
278
279 self.failUnlessEqual(list(missing), [])
280 self.failUnlessEqual(list(extra), [])
281 used_deps = m.get_used_packages()
282
283 build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
284 used_deps,
285 include_tests=False)
286 options = {'main': target_cfg.main}
287 options.update(build)
288 basedir = self.make_basedir()
289 xpi_name = os.path.join(basedir, "contents.xpi")
290 xpi.build_xpi(template_root_dir=xpi_template_path,
291 manifest=fake_manifest,
292 xpi_path=xpi_name,
293 harness_options=options,
294 limit_to=used_files)
295 x = zipfile.ZipFile(xpi_name, "r")
296 names = x.namelist()
297 expected = ["components/",
298 "components/harness.js",
299 # the real template also has 'bootstrap.js', but the fake
300 # one in tests/static-files/xpi-template doesn't
301 "harness-options.json",
302 "install.rdf",
303 "defaults/preferences/prefs.js",
304 "resources/",
305 "resources/addon-sdk/",
306 "resources/addon-sdk/lib/",
307 "resources/addon-sdk/lib/sdk/",
308 "resources/addon-sdk/lib/sdk/self.js",
309 "resources/addon-sdk/lib/sdk/core/",
310 "resources/addon-sdk/lib/sdk/util/",
311 "resources/addon-sdk/lib/sdk/net/",
312 "resources/addon-sdk/lib/sdk/core/promise.js",
313 "resources/addon-sdk/lib/sdk/util/object.js",
314 "resources/addon-sdk/lib/sdk/util/array.js",
315 "resources/addon-sdk/lib/sdk/net/url.js",
316 "resources/three/",
317 "resources/three/lib/",
318 "resources/three/lib/main.js",
319 "resources/three/data/",
320 "resources/three/data/msg.txt",
321 "resources/three/data/subdir/",
322 "resources/three/data/subdir/submsg.txt",
323 "resources/three-a/",
324 "resources/three-a/lib/",
325 "resources/three-a/lib/main.js",
326 "resources/three-a/lib/subdir/",
327 "resources/three-a/lib/subdir/subfile.js",
328 "resources/three-b/",
329 "resources/three-b/lib/",
330 "resources/three-b/lib/main.js",
331 "resources/three-c/",
332 "resources/three-c/lib/",
333 "resources/three-c/lib/main.js",
334 "resources/three-c/lib/sub/",
335 "resources/three-c/lib/sub/foo.js",
336 # notably absent: three-a/lib/unused.js
337 "locale/",
338 "locale/fr-FR.json",
339 "locales.json",
340 ]
341 # showing deltas makes failures easier to investigate
342 missing = set(expected) - set(names)
343 extra = set(names) - set(expected)
344 self.failUnlessEqual((list(missing), list(extra)), ([], []))
345 self.failUnlessEqual(sorted(names), sorted(expected))
346
347 # check locale files
348 localedata = json.loads(x.read("locales.json"))
349 self.failUnlessEqual(sorted(localedata["locales"]), sorted(["fr-FR"]))
350 content = x.read("locale/fr-FR.json")
351 locales = json.loads(content)
352 # Locale files are merged into one.
353 # Conflicts are silently resolved by taking last package translation,
354 # so that we get "No" translation from three-c instead of three-b one.
355 self.failUnlessEqual(locales, json.loads(u'''
356 {
357 "No": "Nein",
358 "one": "un",
359 "What?": "Quoi?",
360 "Yes": "Oui",
361 "plural": {
362 "other": "other",
363 "one": "one"
364 },
365 "uft8_value": "\u00e9"
366 }'''))
367
368 def test_scantests(self):
369 target_cfg = self.get_pkg("three")
370 package_path = [self.get_linker_files_dir("three-deps")]
371 pkg_cfg = packaging.build_config(self.root, target_cfg,
372 packagepath=package_path)
373
374 deps = packaging.get_deps_for_targets(pkg_cfg,
375 [target_cfg.name, "addon-sdk"])
376 m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True)
377 self.failUnlessEqual(sorted(m.get_all_test_modules()),
378 sorted(["three/tests/test-one", "three/tests/test-two"]))
379 # the current __init__.py code omits limit_to=used_files for 'cfx
380 # test', so all test files are included in the XPI. But the test
381 # runner will only execute the tests that m.get_all_test_modules()
382 # tells us about (which are put into the .allTestModules property of
383 # harness-options.json).
384 used_deps = m.get_used_packages()
385
386 build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
387 used_deps,
388 include_tests=True)
389 options = {'main': target_cfg.main}
390 options.update(build)
391 basedir = self.make_basedir()
392 xpi_name = os.path.join(basedir, "contents.xpi")
393 xpi.build_xpi(template_root_dir=xpi_template_path,
394 manifest=fake_manifest,
395 xpi_path=xpi_name,
396 harness_options=options,
397 limit_to=None)
398 x = zipfile.ZipFile(xpi_name, "r")
399 names = x.namelist()
400 self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test.js" in names, names)
401 self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test-finder.js" in names, names)
402 self.failUnless("resources/addon-sdk/lib/sdk/test/harness.js" in names, names)
403 self.failUnless("resources/addon-sdk/lib/sdk/test/runner.js" in names, names)
404 # all files are copied into the XPI, even the things that don't look
405 # like tests.
406 self.failUnless("resources/three/tests/test-one.js" in names, names)
407 self.failUnless("resources/three/tests/test-two.js" in names, names)
408 self.failUnless("resources/three/tests/nontest.js" in names, names)
409
410 def test_scantests_filter(self):
411 target_cfg = self.get_pkg("three")
412 package_path = [self.get_linker_files_dir("three-deps")]
413 pkg_cfg = packaging.build_config(self.root, target_cfg,
414 packagepath=package_path)
415 deps = packaging.get_deps_for_targets(pkg_cfg,
416 [target_cfg.name, "addon-sdk"])
417 FILTER = ".*one.*"
418 m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True,
419 test_filter_re=FILTER)
420 self.failUnlessEqual(sorted(m.get_all_test_modules()),
421 sorted(["three/tests/test-one"]))
422 # the current __init__.py code omits limit_to=used_files for 'cfx
423 # test', so all test files are included in the XPI. But the test
424 # runner will only execute the tests that m.get_all_test_modules()
425 # tells us about (which are put into the .allTestModules property of
426 # harness-options.json).
427 used_deps = m.get_used_packages()
428
429 build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
430 used_deps,
431 include_tests=True)
432 options = {'main': target_cfg.main}
433 options.update(build)
434 basedir = self.make_basedir()
435 xpi_name = os.path.join(basedir, "contents.xpi")
436 xpi.build_xpi(template_root_dir=xpi_template_path,
437 manifest=fake_manifest,
438 xpi_path=xpi_name,
439 harness_options=options,
440 limit_to=None)
441 x = zipfile.ZipFile(xpi_name, "r")
442 names = x.namelist()
443 self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test.js" in names, names)
444 self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test-finder.js" in names, names)
445 self.failUnless("resources/addon-sdk/lib/sdk/test/harness.js" in names, names)
446 self.failUnless("resources/addon-sdk/lib/sdk/test/runner.js" in names, names)
447 # get_all_test_modules() respects the filter. But all files are still
448 # copied into the XPI.
449 self.failUnless("resources/three/tests/test-one.js" in names, names)
450 self.failUnless("resources/three/tests/test-two.js" in names, names)
451 self.failUnless("resources/three/tests/nontest.js" in names, names)
452
453
454 def document_dir(name):
455 if name in ['packages', 'xpi-template']:
456 dirname = os.path.join(test_packaging.static_files_path, name)
457 document_dir_files(dirname)
458 elif name == 'xpi-output':
459 create_xpi('test-xpi.xpi')
460 document_zip_file('test-xpi.xpi')
461 os.remove('test-xpi.xpi')
462 else:
463 raise Exception('unknown dir: %s' % name)
464
465 def normpath(path):
466 """
467 Make a platform-specific relative path use '/' as a separator.
468 """
469
470 return path.replace(os.path.sep, '/')
471
472 def document_zip_file(path):
473 zip = zipfile.ZipFile(path, 'r')
474 for name in sorted(zip.namelist()):
475 contents = zip.read(name)
476 lines = contents.splitlines()
477 if len(lines) == 1 and name.endswith('.json') and len(lines[0]) > 75:
478 # Ideally we would json-decode this, but it results
479 # in an annoying 'u' before every string literal,
480 # since json decoding makes all strings unicode.
481 contents = eval(contents)
482 contents = pprint.pformat(contents)
483 lines = contents.splitlines()
484 contents = "\n ".join(lines)
485 print "%s:\n %s" % (normpath(name), contents)
486 zip.close()
487
488 def document_dir_files(path):
489 filename_contents_tuples = []
490 for dirpath, dirnames, filenames in os.walk(path):
491 relpath = dirpath[len(path)+1:]
492 for filename in filenames:
493 abspath = os.path.join(dirpath, filename)
494 contents = open(abspath, 'r').read()
495 contents = "\n ".join(contents.splitlines())
496 relfilename = os.path.join(relpath, filename)
497 filename_contents_tuples.append((normpath(relfilename), contents))
498 filename_contents_tuples.sort()
499 for filename, contents in filename_contents_tuples:
500 print "%s:" % filename
501 print " %s" % contents
502
503 def create_xpi(xpiname, pkg_name='aardvark', dirname='static-files',
504 extra_harness_options={}):
505 configs = test_packaging.get_configs(pkg_name, dirname)
506 options = {'main': configs.target_cfg.main,
507 'jetpackID': buildJID(configs.target_cfg), }
508 options.update(configs.build)
509 xpi.build_xpi(template_root_dir=xpi_template_path,
510 manifest=fake_manifest,
511 xpi_path=xpiname,
512 harness_options=options,
513 extra_harness_options=extra_harness_options)
514
515 if __name__ == '__main__':
516 unittest.main()

mercurial