Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/log4moz.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://gre/modules/PlacesUtils.jsm"); Engines.register(BookmarksEngine); function makeEngine() { return new BookmarksEngine(); } var syncTesting = new SyncTestingInfrastructure(makeEngine); function test_ID_caching() { _("Ensure that Places IDs are not cached."); let engine = new BookmarksEngine(); let store = engine._store; _("All IDs: " + JSON.stringify(store.getAllIDs())); let mobileID = store.idForGUID("mobile"); _("Change the GUID for that item, and drop the mobile anno."); store._setGUID(mobileID, "abcdefghijkl"); Svc.Annos.removeItemAnnotation(mobileID, "mobile/bookmarksRoot"); let err; let newMobileID; // With noCreate, we don't find an entry. try { newMobileID = store.idForGUID("mobile", true); _("New mobile ID: " + newMobileID); } catch (ex) { err = ex; _("Error: " + Utils.exceptionStr(err)); } do_check_true(!err); // With !noCreate, lookup works, and it's different. newMobileID = store.idForGUID("mobile", false); _("New mobile ID: " + newMobileID); do_check_true(!!newMobileID); do_check_neq(newMobileID, mobileID); // And it's repeatable, even with creation enabled. do_check_eq(newMobileID, store.idForGUID("mobile", false)); do_check_eq(store.GUIDForId(mobileID), "abcdefghijkl"); } function test_processIncoming_error_orderChildren() { _("Ensure that _orderChildren() is called even when _processIncoming() throws an error."); do_test_pending(); Svc.Prefs.set("clusterURL", "http://localhost:8080/"); Svc.Prefs.set("username", "foo"); let collection = new ServerCollection(); let engine = new BookmarksEngine(); let store = engine._store; let global = new ServerWBO('global', {engines: {bookmarks: {version: engine.version, syncID: engine.syncID}}}); let server = httpd_setup({ "/1.0/foo/storage/meta/global": global.handler(), "/1.0/foo/storage/bookmarks": collection.handler() }); try { let folder1_id = Svc.Bookmark.createFolder( Svc.Bookmark.toolbarFolder, "Folder 1", 0); let folder1_guid = store.GUIDForId(folder1_id); let fxuri = Utils.makeURI("http://getfirefox.com/"); let tburi = Utils.makeURI("http://getthunderbird.com/"); let bmk1_id = Svc.Bookmark.insertBookmark( folder1_id, fxuri, Svc.Bookmark.DEFAULT_INDEX, "Get Firefox!"); let bmk1_guid = store.GUIDForId(bmk1_id); let bmk2_id = Svc.Bookmark.insertBookmark( folder1_id, tburi, Svc.Bookmark.DEFAULT_INDEX, "Get Thunderbird!"); let bmk2_guid = store.GUIDForId(bmk2_id); // Create a server record for folder1 where we flip the order of // the children. let folder1_payload = store.createRecord(folder1_guid).cleartext; folder1_payload.children.reverse(); collection.wbos[folder1_guid] = new ServerWBO( folder1_guid, encryptPayload(folder1_payload)); // Create a bogus record that when synced down will provoke a // network error which in turn provokes an exception in _processIncoming. const BOGUS_GUID = "zzzzzzzzzzzz"; let bogus_record = collection.wbos[BOGUS_GUID] = new ServerWBO(BOGUS_GUID, "I'm a bogus record!"); bogus_record.get = function get() { throw "Sync this!"; }; // Make the 10 minutes old so it will only be synced in the toFetch phase. bogus_record.modified = Date.now() / 1000 - 60 * 10; engine.lastSync = Date.now() / 1000 - 60; engine.toFetch = [BOGUS_GUID]; let error; try { engine.sync(); } catch(ex) { error = ex; } do_check_true(!!error); // Verify that the bookmark order has been applied. let new_children = store.createRecord(folder1_guid).children; do_check_eq(new_children.length, 2); do_check_eq(new_children[0], folder1_payload.children[0]); do_check_eq(new_children[1], folder1_payload.children[1]); do_check_eq(Svc.Bookmark.getItemIndex(bmk1_id), 1); do_check_eq(Svc.Bookmark.getItemIndex(bmk2_id), 0); } finally { store.wipe(); server.stop(do_test_finished); Svc.Prefs.resetBranch(""); Records.clearCache(); syncTesting = new SyncTestingInfrastructure(makeEngine); } } function test_restorePromptsReupload() { _("Ensure that restoring from a backup will reupload all records."); do_test_pending(); Svc.Prefs.set("username", "foo"); Service.serverURL = "http://localhost:8080/"; Service.clusterURL = "http://localhost:8080/"; let collection = new ServerCollection({}, true); let engine = new BookmarksEngine(); let store = engine._store; let global = new ServerWBO('global', {engines: {bookmarks: {version: engine.version, syncID: engine.syncID}}}); let server = httpd_setup({ "/1.0/foo/storage/meta/global": global.handler(), "/1.0/foo/storage/bookmarks": collection.handler() }); Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup... try { let folder1_id = Svc.Bookmark.createFolder( Svc.Bookmark.toolbarFolder, "Folder 1", 0); let folder1_guid = store.GUIDForId(folder1_id); _("Folder 1: " + folder1_id + ", " + folder1_guid); let fxuri = Utils.makeURI("http://getfirefox.com/"); let tburi = Utils.makeURI("http://getthunderbird.com/"); _("Create a single record."); let bmk1_id = Svc.Bookmark.insertBookmark( folder1_id, fxuri, Svc.Bookmark.DEFAULT_INDEX, "Get Firefox!"); let bmk1_guid = store.GUIDForId(bmk1_id); _("Get Firefox!: " + bmk1_id + ", " + bmk1_guid); let dirSvc = Cc["@mozilla.org/file/directory_service;1"] .getService(Ci.nsIProperties); let backupFile = dirSvc.get("TmpD", Ci.nsILocalFile); _("Make a backup."); backupFile.append("t_b_e_" + Date.now() + ".json"); _("Backing up to file " + backupFile.path); backupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600); PlacesUtils.backupBookmarksToFile(backupFile); _("Create a different record and sync."); let bmk2_id = Svc.Bookmark.insertBookmark( folder1_id, tburi, Svc.Bookmark.DEFAULT_INDEX, "Get Thunderbird!"); let bmk2_guid = store.GUIDForId(bmk2_id); _("Get Thunderbird!: " + bmk2_id + ", " + bmk2_guid); Svc.Bookmark.removeItem(bmk1_id); let error; try { engine.sync(); } catch(ex) { error = ex; _("Got error: " + Utils.exceptionStr(ex)); } do_check_true(!error); _("Verify that there's only one bookmark on the server, and it's Thunderbird."); // Of course, there's also the Bookmarks Toolbar and Bookmarks Menu... let wbos = [id for ([id, wbo] in Iterator(collection.wbos)) if (["menu", "toolbar", "mobile", folder1_guid].indexOf(id) == -1)]; do_check_eq(wbos.length, 1); do_check_eq(wbos[0], bmk2_guid); _("Now restore from a backup."); PlacesUtils.restoreBookmarksFromJSONFile(backupFile); _("Ensure we have the bookmarks we expect locally."); let guids = store.getAllIDs(); _("GUIDs: " + JSON.stringify(guids)); let found = false; let count = 0; let newFX; for (let guid in guids) { count++; let id = store.idForGUID(guid, true); // Only one bookmark, so _all_ should be Firefox! if (Svc.Bookmark.getItemType(id) == Svc.Bookmark.TYPE_BOOKMARK) { let uri = Svc.Bookmark.getBookmarkURI(id); _("Found URI " + uri.spec + " for GUID " + guid); do_check_eq(uri.spec, fxuri.spec); newFX = guid; // Save the new GUID after restore. found = true; // Only runs if the above check passes. } } _("We found it: " + found); do_check_true(found); _("Have the correct number of IDs locally, too."); do_check_eq(count, ["menu", "toolbar", folder1_id, bmk1_id].length); _("Sync again. This'll wipe bookmarks from the server."); try { engine.sync(); } catch(ex) { error = ex; _("Got error: " + Utils.exceptionStr(ex)); } do_check_true(!error); _("Verify that there's only one bookmark on the server, and it's Firefox."); // Of course, there's also the Bookmarks Toolbar and Bookmarks Menu... wbos = [JSON.parse(JSON.parse(wbo.payload).ciphertext) for ([id, wbo] in Iterator(collection.wbos)) if (wbo.payload)]; _("WBOs: " + JSON.stringify(wbos)); let bookmarks = [wbo for each (wbo in wbos) if (wbo.type == "bookmark")]; do_check_eq(bookmarks.length, 1); do_check_eq(bookmarks[0].id, newFX); do_check_eq(bookmarks[0].bmkUri, fxuri.spec); do_check_eq(bookmarks[0].title, "Get Firefox!"); _("Our old friend Folder 1 is still in play."); let folders = [wbo for each (wbo in wbos) if ((wbo.type == "folder") && (wbo.id != "menu") && (wbo.id != "toolbar"))]; do_check_eq(folders.length, 1); do_check_eq(folders[0].title, "Folder 1"); } finally { store.wipe(); server.stop(do_test_finished); Svc.Prefs.resetBranch(""); Records.clearCache(); syncTesting = new SyncTestingInfrastructure(makeEngine); } } // Bug 632287. function test_mismatched_types() { _("Ensure that handling a record that changes type causes deletion " + "then re-adding."); function FakeRecord(constructor, r) { constructor.call(this, "bookmarks", r.id); for (let x in r) { this[x] = r[x]; } } let oldRecord = { "id": "l1nZZXfB8nC7", "type":"folder", "parentName":"Bookmarks Toolbar", "title":"Innerst i Sneglehode", "description":null, "parentid": "toolbar" }; let newRecord = { "id": "l1nZZXfB8nC7", "type":"livemark", "siteUri":"http://sneglehode.wordpress.com/", "feedUri":"http://sneglehode.wordpress.com/feed/", "parentName":"Bookmarks Toolbar", "title":"Innerst i Sneglehode", "description":null, "children": ["HCRq40Rnxhrd", "YeyWCV1RVsYw", "GCceVZMhvMbP", "sYi2hevdArlF", "vjbZlPlSyGY8", "UtjUhVyrpeG6", "rVq8WMG2wfZI", "Lx0tcy43ZKhZ", "oT74WwV8_j4P", "IztsItWVSo3-"], "parentid": "toolbar" }; do_test_pending(); Svc.Prefs.set("username", "foo"); Service.serverURL = "http://localhost:8080/"; Service.clusterURL = "http://localhost:8080/"; let collection = new ServerCollection({}, true); let engine = new BookmarksEngine(); let store = engine._store; let global = new ServerWBO('global', {engines: {bookmarks: {version: engine.version, syncID: engine.syncID}}}); _("GUID: " + store.GUIDForId(6, true)); let server = httpd_setup({ "/1.0/foo/storage/meta/global": global.handler(), "/1.0/foo/storage/bookmarks": collection.handler() }); try { let bms = store._bms; let oldR = new FakeRecord(BookmarkFolder, oldRecord); let newR = new FakeRecord(Livemark, newRecord); oldR._parent = Svc.Bookmark.toolbarFolder; newR._parent = Svc.Bookmark.toolbarFolder; store.applyIncoming(oldR); _("Applied old. It's a folder."); let oldID = store.idForGUID(oldR.id); _("Old ID: " + oldID); do_check_eq(bms.getItemType(oldID), bms.TYPE_FOLDER); do_check_false(store._ls.isLivemark(oldID)); store.applyIncoming(newR); let newID = store.idForGUID(newR.id); _("New ID: " + newID); _("Applied new. It's a livemark."); do_check_eq(bms.getItemType(newID), bms.TYPE_FOLDER); do_check_true(store._ls.isLivemark(newID)); } finally { store.wipe(); server.stop(do_test_finished); Svc.Prefs.resetBranch(""); Records.clearCache(); syncTesting = new SyncTestingInfrastructure(makeEngine); } } function run_test() { initTestLogging("Trace"); Log4Moz.repository.getLogger("Engine.Bookmarks").level = Log4Moz.Level.Trace; CollectionKeys.generateNewKeys(); test_processIncoming_error_orderChildren(); test_ID_caching(); test_mismatched_types(); test_restorePromptsReupload(); }