services/sync/tests/unit/test_addons_store.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 "use strict";
michael@0 5
michael@0 6 Cu.import("resource://gre/modules/Preferences.jsm");
michael@0 7 Cu.import("resource://services-sync/addonutils.js");
michael@0 8 Cu.import("resource://services-sync/engines/addons.js");
michael@0 9 Cu.import("resource://services-sync/service.js");
michael@0 10 Cu.import("resource://services-sync/util.js");
michael@0 11
michael@0 12 const HTTP_PORT = 8888;
michael@0 13
michael@0 14 let prefs = new Preferences();
michael@0 15
michael@0 16 prefs.set("extensions.getAddons.get.url", "http://localhost:8888/search/guid:%IDS%");
michael@0 17 loadAddonTestFunctions();
michael@0 18 startupManager();
michael@0 19
michael@0 20 Service.engineManager.register(AddonsEngine);
michael@0 21 let engine = Service.engineManager.get("addons");
michael@0 22 let tracker = engine._tracker;
michael@0 23 let store = engine._store;
michael@0 24 let reconciler = engine._reconciler;
michael@0 25
michael@0 26 /**
michael@0 27 * Create a AddonsRec for this application with the fields specified.
michael@0 28 *
michael@0 29 * @param id Sync GUID of record
michael@0 30 * @param addonId ID of add-on
michael@0 31 * @param enabled Boolean whether record is enabled
michael@0 32 * @param deleted Boolean whether record was deleted
michael@0 33 */
michael@0 34 function createRecordForThisApp(id, addonId, enabled, deleted) {
michael@0 35 return {
michael@0 36 id: id,
michael@0 37 addonID: addonId,
michael@0 38 enabled: enabled,
michael@0 39 deleted: !!deleted,
michael@0 40 applicationID: Services.appinfo.ID,
michael@0 41 source: "amo"
michael@0 42 };
michael@0 43 }
michael@0 44
michael@0 45 function createAndStartHTTPServer(port) {
michael@0 46 try {
michael@0 47 let server = new HttpServer();
michael@0 48
michael@0 49 let bootstrap1XPI = ExtensionsTestPath("/addons/test_bootstrap1_1.xpi");
michael@0 50
michael@0 51 server.registerFile("/search/guid:bootstrap1%40tests.mozilla.org",
michael@0 52 do_get_file("bootstrap1-search.xml"));
michael@0 53 server.registerFile("/bootstrap1.xpi", do_get_file(bootstrap1XPI));
michael@0 54
michael@0 55 server.registerFile("/search/guid:missing-xpi%40tests.mozilla.org",
michael@0 56 do_get_file("missing-xpi-search.xml"));
michael@0 57
michael@0 58 server.start(port);
michael@0 59
michael@0 60 return server;
michael@0 61 } catch (ex) {
michael@0 62 _("Got exception starting HTTP server on port " + port);
michael@0 63 _("Error: " + Utils.exceptionStr(ex));
michael@0 64 do_throw(ex);
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 function run_test() {
michael@0 69 initTestLogging("Trace");
michael@0 70 Log.repository.getLogger("Sync.Engine.Addons").level = Log.Level.Trace;
michael@0 71 Log.repository.getLogger("Sync.AddonsRepository").level =
michael@0 72 Log.Level.Trace;
michael@0 73
michael@0 74 reconciler.startListening();
michael@0 75
michael@0 76 // Don't flush to disk in the middle of an event listener!
michael@0 77 // This causes test hangs on WinXP.
michael@0 78 reconciler._shouldPersist = false;
michael@0 79
michael@0 80 run_next_test();
michael@0 81 }
michael@0 82
michael@0 83 add_test(function test_remove() {
michael@0 84 _("Ensure removing add-ons from deleted records works.");
michael@0 85
michael@0 86 let addon = installAddon("test_bootstrap1_1");
michael@0 87 let record = createRecordForThisApp(addon.syncGUID, addon.id, true, true);
michael@0 88
michael@0 89 let failed = store.applyIncomingBatch([record]);
michael@0 90 do_check_eq(0, failed.length);
michael@0 91
michael@0 92 let newAddon = getAddonFromAddonManagerByID(addon.id);
michael@0 93 do_check_eq(null, newAddon);
michael@0 94
michael@0 95 run_next_test();
michael@0 96 });
michael@0 97
michael@0 98 add_test(function test_apply_enabled() {
michael@0 99 _("Ensures that changes to the userEnabled flag apply.");
michael@0 100
michael@0 101 let addon = installAddon("test_bootstrap1_1");
michael@0 102 do_check_true(addon.isActive);
michael@0 103 do_check_false(addon.userDisabled);
michael@0 104
michael@0 105 _("Ensure application of a disable record works as expected.");
michael@0 106 let records = [];
michael@0 107 records.push(createRecordForThisApp(addon.syncGUID, addon.id, false, false));
michael@0 108 let failed = store.applyIncomingBatch(records);
michael@0 109 do_check_eq(0, failed.length);
michael@0 110 addon = getAddonFromAddonManagerByID(addon.id);
michael@0 111 do_check_true(addon.userDisabled);
michael@0 112 records = [];
michael@0 113
michael@0 114 _("Ensure enable record works as expected.");
michael@0 115 records.push(createRecordForThisApp(addon.syncGUID, addon.id, true, false));
michael@0 116 failed = store.applyIncomingBatch(records);
michael@0 117 do_check_eq(0, failed.length);
michael@0 118 addon = getAddonFromAddonManagerByID(addon.id);
michael@0 119 do_check_false(addon.userDisabled);
michael@0 120 records = [];
michael@0 121
michael@0 122 _("Ensure enabled state updates don't apply if the ignore pref is set.");
michael@0 123 records.push(createRecordForThisApp(addon.syncGUID, addon.id, false, false));
michael@0 124 Svc.Prefs.set("addons.ignoreUserEnabledChanges", true);
michael@0 125 failed = store.applyIncomingBatch(records);
michael@0 126 do_check_eq(0, failed.length);
michael@0 127 addon = getAddonFromAddonManagerByID(addon.id);
michael@0 128 do_check_false(addon.userDisabled);
michael@0 129 records = [];
michael@0 130
michael@0 131 uninstallAddon(addon);
michael@0 132 Svc.Prefs.reset("addons.ignoreUserEnabledChanges");
michael@0 133 run_next_test();
michael@0 134 });
michael@0 135
michael@0 136 add_test(function test_ignore_different_appid() {
michael@0 137 _("Ensure that incoming records with a different application ID are ignored.");
michael@0 138
michael@0 139 // We test by creating a record that should result in an update.
michael@0 140 let addon = installAddon("test_bootstrap1_1");
michael@0 141 do_check_false(addon.userDisabled);
michael@0 142
michael@0 143 let record = createRecordForThisApp(addon.syncGUID, addon.id, false, false);
michael@0 144 record.applicationID = "FAKE_ID";
michael@0 145
michael@0 146 let failed = store.applyIncomingBatch([record]);
michael@0 147 do_check_eq(0, failed.length);
michael@0 148
michael@0 149 let newAddon = getAddonFromAddonManagerByID(addon.id);
michael@0 150 do_check_false(addon.userDisabled);
michael@0 151
michael@0 152 uninstallAddon(addon);
michael@0 153
michael@0 154 run_next_test();
michael@0 155 });
michael@0 156
michael@0 157 add_test(function test_ignore_unknown_source() {
michael@0 158 _("Ensure incoming records with unknown source are ignored.");
michael@0 159
michael@0 160 let addon = installAddon("test_bootstrap1_1");
michael@0 161
michael@0 162 let record = createRecordForThisApp(addon.syncGUID, addon.id, false, false);
michael@0 163 record.source = "DUMMY_SOURCE";
michael@0 164
michael@0 165 let failed = store.applyIncomingBatch([record]);
michael@0 166 do_check_eq(0, failed.length);
michael@0 167
michael@0 168 let newAddon = getAddonFromAddonManagerByID(addon.id);
michael@0 169 do_check_false(addon.userDisabled);
michael@0 170
michael@0 171 uninstallAddon(addon);
michael@0 172
michael@0 173 run_next_test();
michael@0 174 });
michael@0 175
michael@0 176 add_test(function test_apply_uninstall() {
michael@0 177 _("Ensures that uninstalling an add-on from a record works.");
michael@0 178
michael@0 179 let addon = installAddon("test_bootstrap1_1");
michael@0 180
michael@0 181 let records = [];
michael@0 182 records.push(createRecordForThisApp(addon.syncGUID, addon.id, true, true));
michael@0 183 let failed = store.applyIncomingBatch(records);
michael@0 184 do_check_eq(0, failed.length);
michael@0 185
michael@0 186 addon = getAddonFromAddonManagerByID(addon.id);
michael@0 187 do_check_eq(null, addon);
michael@0 188
michael@0 189 run_next_test();
michael@0 190 });
michael@0 191
michael@0 192 add_test(function test_addon_syncability() {
michael@0 193 _("Ensure isAddonSyncable functions properly.");
michael@0 194
michael@0 195 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
michael@0 196 Svc.Prefs.set("addons.trustedSourceHostnames",
michael@0 197 "addons.mozilla.org,other.example.com");
michael@0 198
michael@0 199 do_check_false(store.isAddonSyncable(null));
michael@0 200
michael@0 201 let addon = installAddon("test_bootstrap1_1");
michael@0 202 do_check_true(store.isAddonSyncable(addon));
michael@0 203
michael@0 204 let dummy = {};
michael@0 205 const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall"];
michael@0 206 for each (let k in KEYS) {
michael@0 207 dummy[k] = addon[k];
michael@0 208 }
michael@0 209
michael@0 210 do_check_true(store.isAddonSyncable(dummy));
michael@0 211
michael@0 212 dummy.type = "UNSUPPORTED";
michael@0 213 do_check_false(store.isAddonSyncable(dummy));
michael@0 214 dummy.type = addon.type;
michael@0 215
michael@0 216 dummy.scope = 0;
michael@0 217 do_check_false(store.isAddonSyncable(dummy));
michael@0 218 dummy.scope = addon.scope;
michael@0 219
michael@0 220 dummy.foreignInstall = true;
michael@0 221 do_check_false(store.isAddonSyncable(dummy));
michael@0 222 dummy.foreignInstall = false;
michael@0 223
michael@0 224 uninstallAddon(addon);
michael@0 225
michael@0 226 do_check_false(store.isSourceURITrusted(null));
michael@0 227
michael@0 228 function createURI(s) {
michael@0 229 let service = Components.classes["@mozilla.org/network/io-service;1"]
michael@0 230 .getService(Components.interfaces.nsIIOService);
michael@0 231 return service.newURI(s, null, null);
michael@0 232 }
michael@0 233
michael@0 234 let trusted = [
michael@0 235 "https://addons.mozilla.org/foo",
michael@0 236 "https://other.example.com/foo"
michael@0 237 ];
michael@0 238
michael@0 239 let untrusted = [
michael@0 240 "http://addons.mozilla.org/foo", // non-https
michael@0 241 "ftps://addons.mozilla.org/foo", // non-https
michael@0 242 "https://untrusted.example.com/foo", // non-trusted hostname`
michael@0 243 ];
michael@0 244
michael@0 245 for each (let uri in trusted) {
michael@0 246 do_check_true(store.isSourceURITrusted(createURI(uri)));
michael@0 247 }
michael@0 248
michael@0 249 for each (let uri in untrusted) {
michael@0 250 do_check_false(store.isSourceURITrusted(createURI(uri)));
michael@0 251 }
michael@0 252
michael@0 253 Svc.Prefs.set("addons.trustedSourceHostnames", "");
michael@0 254 for each (let uri in trusted) {
michael@0 255 do_check_false(store.isSourceURITrusted(createURI(uri)));
michael@0 256 }
michael@0 257
michael@0 258 Svc.Prefs.set("addons.trustedSourceHostnames", "addons.mozilla.org");
michael@0 259 do_check_true(store.isSourceURITrusted(createURI("https://addons.mozilla.org/foo")));
michael@0 260
michael@0 261 Svc.Prefs.reset("addons.trustedSourceHostnames");
michael@0 262
michael@0 263 run_next_test();
michael@0 264 });
michael@0 265
michael@0 266 add_test(function test_ignore_hotfixes() {
michael@0 267 _("Ensure that hotfix extensions are ignored.");
michael@0 268
michael@0 269 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
michael@0 270
michael@0 271 // A hotfix extension is one that has the id the same as the
michael@0 272 // extensions.hotfix.id pref.
michael@0 273 let prefs = new Preferences("extensions.");
michael@0 274
michael@0 275 let addon = installAddon("test_bootstrap1_1");
michael@0 276 do_check_true(store.isAddonSyncable(addon));
michael@0 277
michael@0 278 let dummy = {};
michael@0 279 const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall"];
michael@0 280 for each (let k in KEYS) {
michael@0 281 dummy[k] = addon[k];
michael@0 282 }
michael@0 283
michael@0 284 // Basic sanity check.
michael@0 285 do_check_true(store.isAddonSyncable(dummy));
michael@0 286
michael@0 287 prefs.set("hotfix.id", dummy.id);
michael@0 288 do_check_false(store.isAddonSyncable(dummy));
michael@0 289
michael@0 290 // Verify that int values don't throw off checking.
michael@0 291 let prefSvc = Cc["@mozilla.org/preferences-service;1"]
michael@0 292 .getService(Ci.nsIPrefService)
michael@0 293 .getBranch("extensions.");
michael@0 294 // Need to delete pref before changing type.
michael@0 295 prefSvc.deleteBranch("hotfix.id");
michael@0 296 prefSvc.setIntPref("hotfix.id", 0xdeadbeef);
michael@0 297
michael@0 298 do_check_true(store.isAddonSyncable(dummy));
michael@0 299
michael@0 300 uninstallAddon(addon);
michael@0 301
michael@0 302 Svc.Prefs.reset("addons.ignoreRepositoryChecking");
michael@0 303 prefs.reset("hotfix.id");
michael@0 304
michael@0 305 run_next_test();
michael@0 306 });
michael@0 307
michael@0 308
michael@0 309 add_test(function test_get_all_ids() {
michael@0 310 _("Ensures that getAllIDs() returns an appropriate set.");
michael@0 311
michael@0 312 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
michael@0 313
michael@0 314 _("Installing two addons.");
michael@0 315 let addon1 = installAddon("test_install1");
michael@0 316 let addon2 = installAddon("test_bootstrap1_1");
michael@0 317
michael@0 318 _("Ensure they're syncable.");
michael@0 319 do_check_true(store.isAddonSyncable(addon1));
michael@0 320 do_check_true(store.isAddonSyncable(addon2));
michael@0 321
michael@0 322 let ids = store.getAllIDs();
michael@0 323
michael@0 324 do_check_eq("object", typeof(ids));
michael@0 325 do_check_eq(2, Object.keys(ids).length);
michael@0 326 do_check_true(addon1.syncGUID in ids);
michael@0 327 do_check_true(addon2.syncGUID in ids);
michael@0 328
michael@0 329 addon1.install.cancel();
michael@0 330 uninstallAddon(addon2);
michael@0 331
michael@0 332 Svc.Prefs.reset("addons.ignoreRepositoryChecking");
michael@0 333 run_next_test();
michael@0 334 });
michael@0 335
michael@0 336 add_test(function test_change_item_id() {
michael@0 337 _("Ensures that changeItemID() works properly.");
michael@0 338
michael@0 339 let addon = installAddon("test_bootstrap1_1");
michael@0 340
michael@0 341 let oldID = addon.syncGUID;
michael@0 342 let newID = Utils.makeGUID();
michael@0 343
michael@0 344 store.changeItemID(oldID, newID);
michael@0 345
michael@0 346 let newAddon = getAddonFromAddonManagerByID(addon.id);
michael@0 347 do_check_neq(null, newAddon);
michael@0 348 do_check_eq(newID, newAddon.syncGUID);
michael@0 349
michael@0 350 uninstallAddon(newAddon);
michael@0 351
michael@0 352 run_next_test();
michael@0 353 });
michael@0 354
michael@0 355 add_test(function test_create() {
michael@0 356 _("Ensure creating/installing an add-on from a record works.");
michael@0 357
michael@0 358 // Set this so that getInstallFromSearchResult doesn't end up
michael@0 359 // failing the install due to an insecure source URI scheme.
michael@0 360 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
michael@0 361 let server = createAndStartHTTPServer(HTTP_PORT);
michael@0 362
michael@0 363 let addon = installAddon("test_bootstrap1_1");
michael@0 364 let id = addon.id;
michael@0 365 uninstallAddon(addon);
michael@0 366
michael@0 367 let guid = Utils.makeGUID();
michael@0 368 let record = createRecordForThisApp(guid, id, true, false);
michael@0 369
michael@0 370 let failed = store.applyIncomingBatch([record]);
michael@0 371 do_check_eq(0, failed.length);
michael@0 372
michael@0 373 let newAddon = getAddonFromAddonManagerByID(id);
michael@0 374 do_check_neq(null, newAddon);
michael@0 375 do_check_eq(guid, newAddon.syncGUID);
michael@0 376 do_check_false(newAddon.userDisabled);
michael@0 377
michael@0 378 uninstallAddon(newAddon);
michael@0 379
michael@0 380 Svc.Prefs.reset("addons.ignoreRepositoryChecking");
michael@0 381 server.stop(run_next_test);
michael@0 382 });
michael@0 383
michael@0 384 add_test(function test_create_missing_search() {
michael@0 385 _("Ensures that failed add-on searches are handled gracefully.");
michael@0 386
michael@0 387 let server = createAndStartHTTPServer(HTTP_PORT);
michael@0 388
michael@0 389 // The handler for this ID is not installed, so a search should 404.
michael@0 390 const id = "missing@tests.mozilla.org";
michael@0 391 let guid = Utils.makeGUID();
michael@0 392 let record = createRecordForThisApp(guid, id, true, false);
michael@0 393
michael@0 394 let failed = store.applyIncomingBatch([record]);
michael@0 395 do_check_eq(1, failed.length);
michael@0 396 do_check_eq(guid, failed[0]);
michael@0 397
michael@0 398 let addon = getAddonFromAddonManagerByID(id);
michael@0 399 do_check_eq(null, addon);
michael@0 400
michael@0 401 server.stop(run_next_test);
michael@0 402 });
michael@0 403
michael@0 404 add_test(function test_create_bad_install() {
michael@0 405 _("Ensures that add-ons without a valid install are handled gracefully.");
michael@0 406
michael@0 407 let server = createAndStartHTTPServer(HTTP_PORT);
michael@0 408
michael@0 409 // The handler returns a search result but the XPI will 404.
michael@0 410 const id = "missing-xpi@tests.mozilla.org";
michael@0 411 let guid = Utils.makeGUID();
michael@0 412 let record = createRecordForThisApp(guid, id, true, false);
michael@0 413
michael@0 414 let failed = store.applyIncomingBatch([record]);
michael@0 415 do_check_eq(1, failed.length);
michael@0 416 do_check_eq(guid, failed[0]);
michael@0 417
michael@0 418 let addon = getAddonFromAddonManagerByID(id);
michael@0 419 do_check_eq(null, addon);
michael@0 420
michael@0 421 server.stop(run_next_test);
michael@0 422 });
michael@0 423
michael@0 424 add_test(function test_wipe() {
michael@0 425 _("Ensures that wiping causes add-ons to be uninstalled.");
michael@0 426
michael@0 427 let addon1 = installAddon("test_bootstrap1_1");
michael@0 428
michael@0 429 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
michael@0 430 store.wipe();
michael@0 431
michael@0 432 let addon = getAddonFromAddonManagerByID(addon1.id);
michael@0 433 do_check_eq(null, addon);
michael@0 434
michael@0 435 Svc.Prefs.reset("addons.ignoreRepositoryChecking");
michael@0 436
michael@0 437 run_next_test();
michael@0 438 });
michael@0 439
michael@0 440 add_test(function test_wipe_and_install() {
michael@0 441 _("Ensure wipe followed by install works.");
michael@0 442
michael@0 443 // This tests the reset sync flow where remote data is replaced by local. The
michael@0 444 // receiving client will see a wipe followed by a record which should undo
michael@0 445 // the wipe.
michael@0 446 let installed = installAddon("test_bootstrap1_1");
michael@0 447
michael@0 448 let record = createRecordForThisApp(installed.syncGUID, installed.id, true,
michael@0 449 false);
michael@0 450
michael@0 451 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
michael@0 452 store.wipe();
michael@0 453
michael@0 454 let deleted = getAddonFromAddonManagerByID(installed.id);
michael@0 455 do_check_null(deleted);
michael@0 456
michael@0 457 // Re-applying the record can require re-fetching the XPI.
michael@0 458 let server = createAndStartHTTPServer(HTTP_PORT);
michael@0 459
michael@0 460 store.applyIncoming(record);
michael@0 461
michael@0 462 let fetched = getAddonFromAddonManagerByID(record.addonID);
michael@0 463 do_check_true(!!fetched);
michael@0 464
michael@0 465 Svc.Prefs.reset("addons.ignoreRepositoryChecking");
michael@0 466 server.stop(run_next_test);
michael@0 467 });
michael@0 468
michael@0 469 add_test(function cleanup() {
michael@0 470 // There's an xpcom-shutdown hook for this, but let's give this a shot.
michael@0 471 reconciler.stopListening();
michael@0 472 run_next_test();
michael@0 473 });
michael@0 474

mercurial