test_packager_formats.py (18345B)
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 unittest 6 from itertools import chain 7 8 import mozunit 9 10 import mozpack.path as mozpath 11 from mozpack.chrome.manifest import ( 12 ManifestBinaryComponent, 13 ManifestComponent, 14 ManifestContent, 15 ManifestLocale, 16 ManifestResource, 17 ManifestSkin, 18 ) 19 from mozpack.copier import FileRegistry 20 from mozpack.errors import ErrorMessage 21 from mozpack.files import GeneratedFile, ManifestFile 22 from mozpack.packager.formats import FlatFormatter, JarFormatter, OmniJarFormatter 23 from mozpack.test.test_files import bar_xpt, foo2_xpt, foo_xpt 24 from test_errors import TestErrors 25 26 CONTENTS = { 27 "bases": { 28 # base_path: is_addon? 29 "": False, 30 "app": False, 31 "addon0": "unpacked", 32 "addon1": True, 33 "app/chrome/addons/addon2": True, 34 }, 35 "manifests": [ 36 ManifestContent("chrome/f", "oo", "oo/"), 37 ManifestContent("chrome/f", "bar", "oo/bar/"), 38 ManifestResource("chrome/f", "foo", "resource://bar/"), 39 ManifestBinaryComponent("components", "foo.so"), 40 ManifestContent("app/chrome", "content", "foo/"), 41 ManifestComponent("app/components", "{foo-id}", "foo.js"), 42 ManifestContent("addon0/chrome", "addon0", "foo/bar/"), 43 ManifestContent("addon1/chrome", "addon1", "foo/bar/"), 44 ManifestContent("app/chrome/addons/addon2/chrome", "addon2", "foo/bar/"), 45 ], 46 "files": { 47 "chrome/f/oo/bar/baz": GeneratedFile(b"foobarbaz"), 48 "chrome/f/oo/baz": GeneratedFile(b"foobaz"), 49 "chrome/f/oo/qux": GeneratedFile(b"fooqux"), 50 "components/foo.so": GeneratedFile(b"foo.so"), 51 "components/foo.xpt": foo_xpt, 52 "components/bar.xpt": bar_xpt, 53 "foo": GeneratedFile(b"foo"), 54 "app/chrome/foo/foo": GeneratedFile(b"appfoo"), 55 "app/components/foo.js": GeneratedFile(b"foo.js"), 56 "addon0/chrome/foo/bar/baz": GeneratedFile(b"foobarbaz"), 57 "addon0/components/foo.xpt": foo2_xpt, 58 "addon0/components/bar.xpt": bar_xpt, 59 "addon1/chrome/foo/bar/baz": GeneratedFile(b"foobarbaz"), 60 "addon1/components/foo.xpt": foo2_xpt, 61 "addon1/components/bar.xpt": bar_xpt, 62 "app/chrome/addons/addon2/chrome/foo/bar/baz": GeneratedFile(b"foobarbaz"), 63 "app/chrome/addons/addon2/components/foo.xpt": foo2_xpt, 64 "app/chrome/addons/addon2/components/bar.xpt": bar_xpt, 65 }, 66 } 67 68 FILES = CONTENTS["files"] 69 70 RESULT_FLAT = { 71 "chrome.manifest": [ 72 "manifest chrome/chrome.manifest", 73 "manifest components/components.manifest", 74 ], 75 "chrome/chrome.manifest": [ 76 "manifest f/f.manifest", 77 ], 78 "chrome/f/f.manifest": [ 79 "content oo oo/", 80 "content bar oo/bar/", 81 "resource foo resource://bar/", 82 ], 83 "chrome/f/oo/bar/baz": FILES["chrome/f/oo/bar/baz"], 84 "chrome/f/oo/baz": FILES["chrome/f/oo/baz"], 85 "chrome/f/oo/qux": FILES["chrome/f/oo/qux"], 86 "components/components.manifest": [ 87 "binary-component foo.so", 88 "interfaces bar.xpt", 89 "interfaces foo.xpt", 90 ], 91 "components/foo.so": FILES["components/foo.so"], 92 "components/foo.xpt": foo_xpt, 93 "components/bar.xpt": bar_xpt, 94 "foo": FILES["foo"], 95 "app/chrome.manifest": [ 96 "manifest chrome/chrome.manifest", 97 "manifest components/components.manifest", 98 ], 99 "app/chrome/chrome.manifest": [ 100 "content content foo/", 101 ], 102 "app/chrome/foo/foo": FILES["app/chrome/foo/foo"], 103 "app/components/components.manifest": [ 104 "component {foo-id} foo.js", 105 ], 106 "app/components/foo.js": FILES["app/components/foo.js"], 107 } 108 109 for addon in ("addon0", "addon1", "app/chrome/addons/addon2"): 110 RESULT_FLAT.update({ 111 mozpath.join(addon, p): f 112 for p, f in { 113 "chrome.manifest": [ 114 "manifest chrome/chrome.manifest", 115 "manifest components/components.manifest", 116 ], 117 "chrome/chrome.manifest": ["content %s foo/bar/" % mozpath.basename(addon)], 118 "chrome/foo/bar/baz": FILES[mozpath.join(addon, "chrome/foo/bar/baz")], 119 "components/components.manifest": [ 120 "interfaces bar.xpt", 121 "interfaces foo.xpt", 122 ], 123 "components/bar.xpt": bar_xpt, 124 "components/foo.xpt": foo2_xpt, 125 }.items() 126 }) 127 128 RESULT_JAR = { 129 p: RESULT_FLAT[p] 130 for p in ( 131 "chrome.manifest", 132 "chrome/chrome.manifest", 133 "components/components.manifest", 134 "components/foo.so", 135 "components/foo.xpt", 136 "components/bar.xpt", 137 "foo", 138 "app/chrome.manifest", 139 "app/components/components.manifest", 140 "app/components/foo.js", 141 "addon0/chrome.manifest", 142 "addon0/components/components.manifest", 143 "addon0/components/foo.xpt", 144 "addon0/components/bar.xpt", 145 ) 146 } 147 148 RESULT_JAR.update({ 149 "chrome/f/f.manifest": [ 150 "content oo jar:oo.jar!/", 151 "content bar jar:oo.jar!/bar/", 152 "resource foo resource://bar/", 153 ], 154 "chrome/f/oo.jar": { 155 "bar/baz": FILES["chrome/f/oo/bar/baz"], 156 "baz": FILES["chrome/f/oo/baz"], 157 "qux": FILES["chrome/f/oo/qux"], 158 }, 159 "app/chrome/chrome.manifest": [ 160 "content content jar:foo.jar!/", 161 ], 162 "app/chrome/foo.jar": { 163 "foo": FILES["app/chrome/foo/foo"], 164 }, 165 "addon0/chrome/chrome.manifest": [ 166 "content addon0 jar:foo.jar!/bar/", 167 ], 168 "addon0/chrome/foo.jar": { 169 "bar/baz": FILES["addon0/chrome/foo/bar/baz"], 170 }, 171 "addon1.xpi": { 172 mozpath.relpath(p, "addon1"): f 173 for p, f in RESULT_FLAT.items() 174 if p.startswith("addon1/") 175 }, 176 "app/chrome/addons/addon2.xpi": { 177 mozpath.relpath(p, "app/chrome/addons/addon2"): f 178 for p, f in RESULT_FLAT.items() 179 if p.startswith("app/chrome/addons/addon2/") 180 }, 181 }) 182 183 RESULT_OMNIJAR = { 184 p: RESULT_FLAT[p] 185 for p in ( 186 "components/foo.so", 187 "foo", 188 ) 189 } 190 191 RESULT_OMNIJAR.update({p: RESULT_JAR[p] for p in RESULT_JAR if p.startswith("addon")}) 192 193 RESULT_OMNIJAR.update({ 194 "omni.foo": { 195 "components/components.manifest": [ 196 "interfaces bar.xpt", 197 "interfaces foo.xpt", 198 ], 199 }, 200 "chrome.manifest": [ 201 "manifest components/components.manifest", 202 ], 203 "components/components.manifest": [ 204 "binary-component foo.so", 205 ], 206 "app/omni.foo": { 207 p: RESULT_FLAT["app/" + p] 208 for p in chain( 209 ( 210 "chrome.manifest", 211 "chrome/chrome.manifest", 212 "chrome/foo/foo", 213 "components/components.manifest", 214 "components/foo.js", 215 ), 216 ( 217 mozpath.relpath(p, "app") 218 for p in RESULT_FLAT.keys() 219 if p.startswith("app/chrome/addons/addon2/") 220 ), 221 ) 222 }, 223 }) 224 225 RESULT_OMNIJAR["omni.foo"].update({ 226 p: RESULT_FLAT[p] 227 for p in ( 228 "chrome.manifest", 229 "chrome/chrome.manifest", 230 "chrome/f/f.manifest", 231 "chrome/f/oo/bar/baz", 232 "chrome/f/oo/baz", 233 "chrome/f/oo/qux", 234 "components/foo.xpt", 235 "components/bar.xpt", 236 ) 237 }) 238 239 RESULT_OMNIJAR_WITH_SUBPATH = { 240 k.replace("omni.foo", "bar/omni.foo"): v for k, v in RESULT_OMNIJAR.items() 241 } 242 243 CONTENTS_WITH_BASE = { 244 "bases": { 245 mozpath.join("base/root", b) if b else "base/root": a 246 for b, a in CONTENTS["bases"].items() 247 }, 248 "manifests": [ 249 m.move(mozpath.join("base/root", m.base)) for m in CONTENTS["manifests"] 250 ], 251 "files": {mozpath.join("base/root", p): f for p, f in CONTENTS["files"].items()}, 252 } 253 254 EXTRA_CONTENTS = { 255 "extra/file": GeneratedFile(b"extra file"), 256 } 257 258 CONTENTS_WITH_BASE["files"].update(EXTRA_CONTENTS) 259 260 261 def result_with_base(results): 262 result = {mozpath.join("base/root", p): v for p, v in results.items()} 263 result.update(EXTRA_CONTENTS) 264 return result 265 266 267 RESULT_FLAT_WITH_BASE = result_with_base(RESULT_FLAT) 268 RESULT_JAR_WITH_BASE = result_with_base(RESULT_JAR) 269 RESULT_OMNIJAR_WITH_BASE = result_with_base(RESULT_OMNIJAR) 270 271 272 def fill_formatter(formatter, contents): 273 for base, is_addon in sorted(contents["bases"].items()): 274 formatter.add_base(base, is_addon) 275 276 for manifest in contents["manifests"]: 277 formatter.add_manifest(manifest) 278 279 for k, v in sorted(contents["files"].items()): 280 if k.endswith(".xpt"): 281 formatter.add_interfaces(k, v) 282 else: 283 formatter.add(k, v) 284 285 286 def get_contents(registry, read_all=False, mode="rt"): 287 result = {} 288 for k, v in registry: 289 if isinstance(v, FileRegistry): 290 result[k] = get_contents(v) 291 elif isinstance(v, ManifestFile) or read_all: 292 if "b" in mode: 293 result[k] = v.open().read() 294 else: 295 result[k] = v.open().read().decode().splitlines() 296 else: 297 result[k] = v 298 return result 299 300 301 class TestFormatters(TestErrors, unittest.TestCase): 302 maxDiff = None 303 304 def test_bases(self): 305 formatter = FlatFormatter(FileRegistry()) 306 formatter.add_base("") 307 formatter.add_base("addon0", addon=True) 308 formatter.add_base("browser") 309 self.assertEqual(formatter._get_base("platform.ini"), ("", "platform.ini")) 310 self.assertEqual( 311 formatter._get_base("browser/application.ini"), 312 ("browser", "application.ini"), 313 ) 314 self.assertEqual( 315 formatter._get_base("addon0/install.rdf"), ("addon0", "install.rdf") 316 ) 317 318 def do_test_contents(self, formatter, contents): 319 for f in contents["files"]: 320 # .xpt files are merged, so skip them. 321 if not f.endswith(".xpt"): 322 self.assertTrue(formatter.contains(f)) 323 324 def test_flat_formatter(self): 325 registry = FileRegistry() 326 formatter = FlatFormatter(registry) 327 328 fill_formatter(formatter, CONTENTS) 329 self.assertEqual(get_contents(registry), RESULT_FLAT) 330 self.do_test_contents(formatter, CONTENTS) 331 332 def test_jar_formatter(self): 333 registry = FileRegistry() 334 formatter = JarFormatter(registry) 335 336 fill_formatter(formatter, CONTENTS) 337 self.assertEqual(get_contents(registry), RESULT_JAR) 338 self.do_test_contents(formatter, CONTENTS) 339 340 def test_omnijar_formatter(self): 341 registry = FileRegistry() 342 formatter = OmniJarFormatter(registry, "omni.foo") 343 344 fill_formatter(formatter, CONTENTS) 345 self.assertEqual(get_contents(registry), RESULT_OMNIJAR) 346 self.do_test_contents(formatter, CONTENTS) 347 348 def test_flat_formatter_with_base(self): 349 registry = FileRegistry() 350 formatter = FlatFormatter(registry) 351 352 fill_formatter(formatter, CONTENTS_WITH_BASE) 353 self.assertEqual(get_contents(registry), RESULT_FLAT_WITH_BASE) 354 self.do_test_contents(formatter, CONTENTS_WITH_BASE) 355 356 def test_jar_formatter_with_base(self): 357 registry = FileRegistry() 358 formatter = JarFormatter(registry) 359 360 fill_formatter(formatter, CONTENTS_WITH_BASE) 361 self.assertEqual(get_contents(registry), RESULT_JAR_WITH_BASE) 362 self.do_test_contents(formatter, CONTENTS_WITH_BASE) 363 364 def test_omnijar_formatter_with_base(self): 365 registry = FileRegistry() 366 formatter = OmniJarFormatter(registry, "omni.foo") 367 368 fill_formatter(formatter, CONTENTS_WITH_BASE) 369 self.assertEqual(get_contents(registry), RESULT_OMNIJAR_WITH_BASE) 370 self.do_test_contents(formatter, CONTENTS_WITH_BASE) 371 372 def test_omnijar_formatter_with_subpath(self): 373 registry = FileRegistry() 374 formatter = OmniJarFormatter(registry, "bar/omni.foo") 375 376 fill_formatter(formatter, CONTENTS) 377 self.assertEqual(get_contents(registry), RESULT_OMNIJAR_WITH_SUBPATH) 378 self.do_test_contents(formatter, CONTENTS) 379 380 def test_omnijar_is_resource(self): 381 def is_resource(base, path): 382 registry = FileRegistry() 383 f = OmniJarFormatter( 384 registry, 385 "omni.foo", 386 non_resources=[ 387 "defaults/messenger/mailViews.dat", 388 "defaults/foo/*", 389 "*/dummy", 390 ], 391 ) 392 f.add_base("") 393 f.add_base("app") 394 f.add(mozpath.join(base, path), GeneratedFile(b"")) 395 if f.copier.contains(mozpath.join(base, path)): 396 return False 397 self.assertTrue(f.copier.contains(mozpath.join(base, "omni.foo"))) 398 self.assertTrue(f.copier[mozpath.join(base, "omni.foo")].contains(path)) 399 return True 400 401 for base in ["", "app/"]: 402 self.assertTrue(is_resource(base, "chrome")) 403 self.assertTrue(is_resource(base, "chrome/foo/bar/baz.properties")) 404 self.assertFalse(is_resource(base, "chrome/icons/foo.png")) 405 self.assertTrue(is_resource(base, "components/foo.js")) 406 self.assertFalse(is_resource(base, "components/foo.so")) 407 self.assertTrue(is_resource(base, "res/foo.css")) 408 self.assertFalse(is_resource(base, "res/cursors/foo.png")) 409 self.assertFalse(is_resource(base, "res/MainMenu.nib/foo")) 410 self.assertTrue(is_resource(base, "defaults/pref/foo.js")) 411 self.assertFalse(is_resource(base, "defaults/pref/channel-prefs.js")) 412 self.assertTrue(is_resource(base, "defaults/preferences/foo.js")) 413 self.assertFalse(is_resource(base, "defaults/preferences/channel-prefs.js")) 414 self.assertTrue(is_resource(base, "modules/foo.jsm")) 415 self.assertTrue(is_resource(base, "greprefs.js")) 416 self.assertTrue(is_resource(base, "hyphenation/foo")) 417 self.assertTrue(is_resource(base, "default.locale")) 418 self.assertFalse(is_resource(base, "foo")) 419 self.assertFalse(is_resource(base, "foo/bar/greprefs.js")) 420 self.assertTrue(is_resource(base, "defaults/messenger/foo.dat")) 421 self.assertFalse(is_resource(base, "defaults/messenger/mailViews.dat")) 422 self.assertTrue(is_resource(base, "defaults/pref/foo.js")) 423 self.assertFalse(is_resource(base, "defaults/foo/bar.dat")) 424 self.assertFalse(is_resource(base, "defaults/foo/bar/baz.dat")) 425 self.assertTrue(is_resource(base, "chrome/foo/bar/baz/dummy_")) 426 self.assertFalse(is_resource(base, "chrome/foo/bar/baz/dummy")) 427 self.assertTrue(is_resource(base, "chrome/foo/bar/dummy_")) 428 self.assertFalse(is_resource(base, "chrome/foo/bar/dummy")) 429 430 def test_chrome_override(self): 431 registry = FileRegistry() 432 f = FlatFormatter(registry) 433 f.add_base("") 434 f.add_manifest(ManifestContent("chrome", "foo", "foo/unix")) 435 # A more specific entry for a given chrome name can override a more 436 # generic one. 437 f.add_manifest(ManifestContent("chrome", "foo", "foo/win", "os=WINNT")) 438 f.add_manifest(ManifestContent("chrome", "foo", "foo/osx", "os=Darwin")) 439 440 # Chrome with the same name overrides the previous registration. 441 with self.assertRaises(ErrorMessage) as e: 442 f.add_manifest(ManifestContent("chrome", "foo", "foo/")) 443 444 self.assertEqual( 445 str(e.exception), 446 'error: "content foo foo/" overrides "content foo foo/unix"', 447 ) 448 449 # Chrome with the same name and same flags overrides the previous 450 # registration. 451 with self.assertRaises(ErrorMessage) as e: 452 f.add_manifest(ManifestContent("chrome", "foo", "foo/", "os=WINNT")) 453 454 self.assertEqual( 455 str(e.exception), 456 'error: "content foo foo/ os=WINNT" overrides ' 457 '"content foo foo/win os=WINNT"', 458 ) 459 460 # We may start with the more specific entry first 461 f.add_manifest(ManifestContent("chrome", "bar", "bar/win", "os=WINNT")) 462 # Then adding a more generic one overrides it. 463 with self.assertRaises(ErrorMessage) as e: 464 f.add_manifest(ManifestContent("chrome", "bar", "bar/unix")) 465 466 self.assertEqual( 467 str(e.exception), 468 'error: "content bar bar/unix" overrides "content bar bar/win os=WINNT"', 469 ) 470 471 # Adding something more specific still works. 472 f.add_manifest( 473 ManifestContent("chrome", "bar", "bar/win", "os=WINNT osversion>=7.0") 474 ) 475 476 # Variations of skin/locales are allowed. 477 f.add_manifest( 478 ManifestSkin("chrome", "foo", "classic/1.0", "foo/skin/classic/") 479 ) 480 f.add_manifest(ManifestSkin("chrome", "foo", "modern/1.0", "foo/skin/modern/")) 481 482 f.add_manifest(ManifestLocale("chrome", "foo", "en-US", "foo/locale/en-US/")) 483 f.add_manifest(ManifestLocale("chrome", "foo", "ja-JP", "foo/locale/ja-JP/")) 484 485 # But same-skin/locale still error out. 486 with self.assertRaises(ErrorMessage) as e: 487 f.add_manifest( 488 ManifestSkin("chrome", "foo", "classic/1.0", "foo/skin/classic/foo") 489 ) 490 491 self.assertEqual( 492 str(e.exception), 493 'error: "skin foo classic/1.0 foo/skin/classic/foo" overrides ' 494 '"skin foo classic/1.0 foo/skin/classic/"', 495 ) 496 497 with self.assertRaises(ErrorMessage) as e: 498 f.add_manifest( 499 ManifestLocale("chrome", "foo", "en-US", "foo/locale/en-US/foo") 500 ) 501 502 self.assertEqual( 503 str(e.exception), 504 'error: "locale foo en-US foo/locale/en-US/foo" overrides ' 505 '"locale foo en-US foo/locale/en-US/"', 506 ) 507 508 # Duplicating existing manifest entries is not an error. 509 f.add_manifest(ManifestContent("chrome", "foo", "foo/unix")) 510 511 self.assertEqual( 512 self.get_output(), 513 [ 514 'warning: "content foo foo/unix" is duplicated. Skipping.', 515 ], 516 ) 517 518 519 if __name__ == "__main__": 520 mozunit.main()