gecko/services/sync/tests/unit/test_addons_store.js
Gregory Szorc 7ece058f3d Bug 785225 - Part 9: Refactor engines to not use singletons; r=rnewman
Engines now maintain a reference to the service they belong to. This
allows them to obtain references to other engine instances belonging to
that service and that service only.

Stores and trackers now maintain a reference to the engine they belong
to.

Engine managers now maintain a reference back to a service.

The clients singleton has been removed. It now exists as an instance
variable on Service. Parts of ClientsEngine do behave as singletons
(e.g. commands). This will be addressed in future refactoring.
2012-08-29 14:43:41 -07:00

423 lines
12 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://services-common/preferences.js");
Cu.import("resource://services-sync/addonutils.js");
Cu.import("resource://services-sync/engines/addons.js");
Cu.import("resource://services-sync/service.js");
const HTTP_PORT = 8888;
let prefs = new Preferences();
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
prefs.set("extensions.getAddons.get.url", "http://localhost:8888/search/guid:%IDS%");
loadAddonTestFunctions();
startupManager();
Service.engineManager.register(AddonsEngine);
let engine = Service.engineManager.get("addons");
let tracker = engine._tracker;
let store = engine._store;
let reconciler = engine._reconciler;
/**
* Create a AddonsRec for this application with the fields specified.
*
* @param id Sync GUID of record
* @param addonId ID of add-on
* @param enabled Boolean whether record is enabled
* @param deleted Boolean whether record was deleted
*/
function createRecordForThisApp(id, addonId, enabled, deleted) {
return {
id: id,
addonID: addonId,
enabled: enabled,
deleted: !!deleted,
applicationID: Services.appinfo.ID,
source: "amo"
};
}
function createAndStartHTTPServer(port) {
try {
let server = new HttpServer();
let bootstrap1XPI = ExtensionsTestPath("/addons/test_bootstrap1_1.xpi");
server.registerFile("/search/guid:bootstrap1%40tests.mozilla.org",
do_get_file("bootstrap1-search.xml"));
server.registerFile("/bootstrap1.xpi", do_get_file(bootstrap1XPI));
server.registerFile("/search/guid:missing-xpi%40tests.mozilla.org",
do_get_file("missing-xpi-search.xml"));
server.start(port);
return server;
} catch (ex) {
_("Got exception starting HTTP server on port " + port);
_("Error: " + Utils.exceptionStr(ex));
do_throw(ex);
}
}
function run_test() {
initTestLogging("Trace");
Log4Moz.repository.getLogger("Sync.Engine.Addons").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.AddonsRepository").level =
Log4Moz.Level.Trace;
reconciler.startListening();
run_next_test();
}
add_test(function test_get_all_ids() {
_("Ensures that getAllIDs() returns an appropriate set.");
engine._refreshReconcilerState();
let addon1 = installAddon("test_install1");
let addon2 = installAddon("test_bootstrap1_1");
let ids = store.getAllIDs();
do_check_eq("object", typeof(ids));
do_check_eq(2, Object.keys(ids).length);
do_check_true(addon1.syncGUID in ids);
do_check_true(addon2.syncGUID in ids);
addon1.install.cancel();
uninstallAddon(addon2);
run_next_test();
});
add_test(function test_change_item_id() {
_("Ensures that changeItemID() works properly.");
let addon = installAddon("test_bootstrap1_1");
let oldID = addon.syncGUID;
let newID = Utils.makeGUID();
store.changeItemID(oldID, newID);
let newAddon = getAddonFromAddonManagerByID(addon.id);
do_check_neq(null, newAddon);
do_check_eq(newID, newAddon.syncGUID);
uninstallAddon(newAddon);
run_next_test();
});
add_test(function test_create() {
_("Ensure creating/installing an add-on from a record works.");
let server = createAndStartHTTPServer(HTTP_PORT);
let addon = installAddon("test_bootstrap1_1");
let id = addon.id;
uninstallAddon(addon);
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(0, failed.length);
let newAddon = getAddonFromAddonManagerByID(id);
do_check_neq(null, newAddon);
do_check_eq(guid, newAddon.syncGUID);
do_check_false(newAddon.userDisabled);
uninstallAddon(newAddon);
server.stop(run_next_test);
});
add_test(function test_create_missing_search() {
_("Ensures that failed add-on searches are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler for this ID is not installed, so a search should 404.
const id = "missing@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(1, failed.length);
do_check_eq(guid, failed[0]);
let addon = getAddonFromAddonManagerByID(id);
do_check_eq(null, addon);
server.stop(run_next_test);
});
add_test(function test_create_bad_install() {
_("Ensures that add-ons without a valid install are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler returns a search result but the XPI will 404.
const id = "missing-xpi@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(1, failed.length);
do_check_eq(guid, failed[0]);
let addon = getAddonFromAddonManagerByID(id);
do_check_eq(null, addon);
server.stop(run_next_test);
});
add_test(function test_remove() {
_("Ensure removing add-ons from deleted records works.");
let addon = installAddon("test_bootstrap1_1");
let record = createRecordForThisApp(addon.syncGUID, addon.id, true, true);
let failed = store.applyIncomingBatch([record]);
do_check_eq(0, failed.length);
let newAddon = getAddonFromAddonManagerByID(addon.id);
do_check_eq(null, newAddon);
run_next_test();
});
add_test(function test_apply_enabled() {
_("Ensures that changes to the userEnabled flag apply.");
let addon = installAddon("test_bootstrap1_1");
do_check_true(addon.isActive);
do_check_false(addon.userDisabled);
_("Ensure application of a disable record works as expected.");
let records = [];
records.push(createRecordForThisApp(addon.syncGUID, addon.id, false, false));
let failed = store.applyIncomingBatch(records);
do_check_eq(0, failed.length);
addon = getAddonFromAddonManagerByID(addon.id);
do_check_true(addon.userDisabled);
records = [];
_("Ensure enable record works as expected.");
records.push(createRecordForThisApp(addon.syncGUID, addon.id, true, false));
failed = store.applyIncomingBatch(records);
do_check_eq(0, failed.length);
addon = getAddonFromAddonManagerByID(addon.id);
do_check_false(addon.userDisabled);
records = [];
_("Ensure enabled state updates don't apply if the ignore pref is set.");
records.push(createRecordForThisApp(addon.syncGUID, addon.id, false, false));
Svc.Prefs.set("addons.ignoreUserEnabledChanges", true);
failed = store.applyIncomingBatch(records);
do_check_eq(0, failed.length);
addon = getAddonFromAddonManagerByID(addon.id);
do_check_false(addon.userDisabled);
records = [];
uninstallAddon(addon);
Svc.Prefs.reset("addons.ignoreUserEnabledChanges");
run_next_test();
});
add_test(function test_ignore_different_appid() {
_("Ensure that incoming records with a different application ID are ignored.");
// We test by creating a record that should result in an update.
let addon = installAddon("test_bootstrap1_1");
do_check_false(addon.userDisabled);
let record = createRecordForThisApp(addon.syncGUID, addon.id, false, false);
record.applicationID = "FAKE_ID";
let failed = store.applyIncomingBatch([record]);
do_check_eq(0, failed.length);
let newAddon = getAddonFromAddonManagerByID(addon.id);
do_check_false(addon.userDisabled);
uninstallAddon(addon);
run_next_test();
});
add_test(function test_ignore_unknown_source() {
_("Ensure incoming records with unknown source are ignored.");
let addon = installAddon("test_bootstrap1_1");
let record = createRecordForThisApp(addon.syncGUID, addon.id, false, false);
record.source = "DUMMY_SOURCE";
let failed = store.applyIncomingBatch([record]);
do_check_eq(0, failed.length);
let newAddon = getAddonFromAddonManagerByID(addon.id);
do_check_false(addon.userDisabled);
uninstallAddon(addon);
run_next_test();
});
add_test(function test_apply_uninstall() {
_("Ensures that uninstalling an add-on from a record works.");
let addon = installAddon("test_bootstrap1_1");
let records = [];
records.push(createRecordForThisApp(addon.syncGUID, addon.id, true, true));
let failed = store.applyIncomingBatch(records);
do_check_eq(0, failed.length);
addon = getAddonFromAddonManagerByID(addon.id);
do_check_eq(null, addon);
run_next_test();
});
add_test(function test_addon_syncability() {
_("Ensure isAddonSyncable functions properly.");
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
Svc.Prefs.set("addons.trustedSourceHostnames",
"addons.mozilla.org,other.example.com");
do_check_false(store.isAddonSyncable(null));
let addon = installAddon("test_bootstrap1_1");
do_check_true(store.isAddonSyncable(addon));
let dummy = {};
const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall"];
for each (let k in KEYS) {
dummy[k] = addon[k];
}
do_check_true(store.isAddonSyncable(dummy));
dummy.type = "UNSUPPORTED";
do_check_false(store.isAddonSyncable(dummy));
dummy.type = addon.type;
dummy.scope = 0;
do_check_false(store.isAddonSyncable(dummy));
dummy.scope = addon.scope;
dummy.foreignInstall = true;
do_check_false(store.isAddonSyncable(dummy));
dummy.foreignInstall = false;
uninstallAddon(addon);
do_check_false(store.isSourceURITrusted(null));
function createURI(s) {
let service = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
return service.newURI(s, null, null);
}
let trusted = [
"https://addons.mozilla.org/foo",
"https://other.example.com/foo"
];
let untrusted = [
"http://addons.mozilla.org/foo", // non-https
"ftps://addons.mozilla.org/foo", // non-https
"https://untrusted.example.com/foo", // non-trusted hostname`
];
for each (let uri in trusted) {
do_check_true(store.isSourceURITrusted(createURI(uri)));
}
for each (let uri in untrusted) {
do_check_false(store.isSourceURITrusted(createURI(uri)));
}
Svc.Prefs.set("addons.trustedSourceHostnames", "");
for each (let uri in trusted) {
do_check_false(store.isSourceURITrusted(createURI(uri)));
}
Svc.Prefs.set("addons.trustedSourceHostnames", "addons.mozilla.org");
do_check_true(store.isSourceURITrusted(createURI("https://addons.mozilla.org/foo")));
Svc.Prefs.reset("addons.trustedSourceHostnames");
run_next_test();
});
add_test(function test_ignore_hotfixes() {
_("Ensure that hotfix extensions are ignored.");
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
// A hotfix extension is one that has the id the same as the
// extensions.hotfix.id pref.
let prefs = new Preferences("extensions.");
let addon = installAddon("test_bootstrap1_1");
do_check_true(store.isAddonSyncable(addon));
let dummy = {};
const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall"];
for each (let k in KEYS) {
dummy[k] = addon[k];
}
// Basic sanity check.
do_check_true(store.isAddonSyncable(dummy));
prefs.set("hotfix.id", dummy.id);
do_check_false(store.isAddonSyncable(dummy));
// Verify that int values don't throw off checking.
let prefSvc = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService)
.getBranch("extensions.");
// Need to delete pref before changing type.
prefSvc.deleteBranch("hotfix.id");
prefSvc.setIntPref("hotfix.id", 0xdeadbeef);
do_check_true(store.isAddonSyncable(dummy));
uninstallAddon(addon);
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
prefs.reset("hotfix.id");
run_next_test();
});
add_test(function test_wipe() {
_("Ensures that wiping causes add-ons to be uninstalled.");
let addon1 = installAddon("test_bootstrap1_1");
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
store.wipe();
let addon = getAddonFromAddonManagerByID(addon1.id);
do_check_eq(null, addon);
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
run_next_test();
});