diff -r 000000000000 -r 6474c204b198 dom/contacts/tests/test_migration_chrome.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/contacts/tests/test_migration_chrome.js Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,336 @@ +/* global + sendAsyncMessage, + addMessageListener, + indexedDB + */ +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +let imports = {}; + +Cu.import("resource://gre/modules/ContactDB.jsm", imports); +Cu.import("resource://gre/modules/ContactService.jsm", imports); +Cu.import("resource://gre/modules/Promise.jsm", imports); +Cu.importGlobalProperties(["indexedDB"]); + +const { + STORE_NAME, + SAVED_GETALL_STORE_NAME, + REVISION_STORE, + DB_NAME, + ContactService, +} = imports; +// |const| will not work because +// it will make the Promise object immutable before assigning. +// Using Object.defineProperty() instead. +Object.defineProperty(this, "Promise", { + value: imports.Promise, writable: false, configurable: false +}); + +let DEBUG = false; +function debug(str) { + if (DEBUG){ + dump("-*- TestMigrationChromeScript: " + str + "\n"); + } +} + +const DATA = { + 12: { + SCHEMA: function createSchema12(db, transaction) { + let objectStore = db.createObjectStore(STORE_NAME, {keyPath: "id"}); + objectStore.createIndex("familyName", "properties.familyName", { multiEntry: true }); + objectStore.createIndex("givenName", "properties.givenName", { multiEntry: true }); + objectStore.createIndex("familyNameLowerCase", "search.familyName", { multiEntry: true }); + objectStore.createIndex("givenNameLowerCase", "search.givenName", { multiEntry: true }); + objectStore.createIndex("telLowerCase", "search.tel", { multiEntry: true }); + objectStore.createIndex("emailLowerCase", "search.email", { multiEntry: true }); + objectStore.createIndex("tel", "search.exactTel", { multiEntry: true }); + objectStore.createIndex("category", "properties.category", { multiEntry: true }); + objectStore.createIndex("email", "search.email", { multiEntry: true }); + objectStore.createIndex("telMatch", "search.parsedTel", {multiEntry: true}); + db.createObjectStore(SAVED_GETALL_STORE_NAME); + db.createObjectStore(REVISION_STORE).put(0, "revision"); + }, + BAD: [ + { + // this contact is bad because its "name" property is not an array and + // is the empty string (upgrade 16 to 17) + "properties": { + "name": "", + "email": [], + "url": [{ + "type": ["source"], + "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" + }], + "category": ["gmail"], + "adr": [], + "tel": [{ + "type": ["mobile"], + "value": "+7 123 456-78-90" + }], + "sex": "undefined", + "genderIdentity": "undefined" + }, + "search": { + "givenName": [], + "familyName": [], + "email": [], + "category": ["gmail"], + "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", + "4567890","567890","67890","7890","890","90","0","81234567890"], + "exactTel": ["+71234567890"], + "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] + }, + "updated": new Date("2013-07-27T16:47:40.974Z"), + "published": new Date("2013-07-27T16:47:40.974Z"), + "id": "bad-1" + }, + { + // This contact is bad because its "name" property is not an array + // (upgrade 14 to 15) + "properties": { + "name": "name-bad-2", + "email": [], + "url": [{ + "type": ["source"], + "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" + }], + "category": ["gmail"], + "adr": [], + "tel": [{ + "type": ["mobile"], + "value": "+7 123 456-78-90" + }], + "sex": "undefined", + "genderIdentity": "undefined" + }, + "search": { + "givenName": [], + "familyName": [], + "email": [], + "category": ["gmail"], + "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", + "4567890","567890","67890","7890","890","90","0","81234567890"], + "exactTel": ["+71234567890"], + "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] + }, + "updated": new Date("2013-07-27T16:47:40.974Z"), + "published": new Date("2013-07-27T16:47:40.974Z"), + "id": "bad-2" + }, + { + // This contact is bad because its bday property is a String (upgrade 15 + // to 16), and its anniversary property is an empty string (upgrade 16 + // to 17) + "properties": { + "name": ["name-bad-3"], + "email": [], + "url": [{ + "type": ["source"], + "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" + }], + "category": ["gmail"], + "adr": [], + "tel": [{ + "type": ["mobile"], + "value": "+7 123 456-78-90" + }], + "sex": "undefined", + "genderIdentity": "undefined", + "bday": "2013-07-27T16:47:40.974Z", + "anniversary": "" + }, + "search": { + "givenName": [], + "familyName": [], + "email": [], + "category": ["gmail"], + "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", + "4567890","567890","67890","7890","890","90","0","81234567890"], + "exactTel": ["+71234567890"], + "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] + }, + "updated": new Date("2013-07-27T16:47:40.974Z"), + "published": new Date("2013-07-27T16:47:40.974Z"), + "id": "bad-3" + }, + { + // This contact is bad because its tel property has a tel.type null + // value (upgrade 16 to 17), and email.type not array value (upgrade 14 + // to 15) + "properties": { + "name": ["name-bad-4"], + "email": [{ + "value": "toto@toto.com", + "type": "home" + }], + "url": [{ + "type": ["source"], + "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" + }], + "category": ["gmail"], + "adr": [], + "tel": [{ + "type": null, + "value": "+7 123 456-78-90" + }], + "sex": "undefined", + "genderIdentity": "undefined" + }, + "search": { + "givenName": [], + "familyName": [], + "email": [], + "category": ["gmail"], + "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", + "4567890","567890","67890","7890","890","90","0","81234567890"], + "exactTel": ["+71234567890"], + "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] + }, + "updated": new Date("2013-07-27T16:47:40.974Z"), + "published": new Date("2013-07-27T16:47:40.974Z"), + "id": "bad-4" + } + ], + GOOD: [ + { + "properties": { + "name": ["name-good-1"], + "email": [], + "url": [{ + "type": ["source"], + "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" + }], + "category": ["gmail"], + "adr": [], + "tel": [{ + "type": ["mobile"], + "value": "+7 123 456-78-90" + }], + "sex": "undefined", + "genderIdentity": "undefined" + }, + "search": { + "givenName": [], + "familyName": [], + "email": [], + "category": ["gmail"], + "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", + "4567890","567890","67890","7890","890","90","0","81234567890"], + "exactTel": ["+71234567890"], + "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] + }, + "updated": new Date("2013-07-27T16:47:40.974Z"), + "published": new Date("2013-07-27T16:47:40.974Z"), + "id": "good-1" + } + ] + } +}; + +function DataManager(version) { + if (!(version in DATA)) { + throw new Error("Version " + version + " can't be found in our test datas."); + } + + this.version = version; + this.data = DATA[version]; +} + +DataManager.prototype = { + open: function() { + debug("opening for version " + this.version); + var deferred = Promise.defer(); + + let req = indexedDB.open(DB_NAME, this.version); + req.onupgradeneeded = function() { + let db = req.result; + let transaction = req.transaction; + this.createSchema(db, transaction); + this.addContacts(db, transaction); + }.bind(this); + + req.onsuccess = function() { + debug("succeeded opening the db, let's close it now"); + req.result.close(); + deferred.resolve(this.contactsCount()); + }.bind(this); + + req.onerror = function() { + deferred.reject(this.error); + }; + + return deferred.promise; + }, + + createSchema: function(db, transaction) { + debug("createSchema for version " + this.version); + this.data.SCHEMA(db, transaction); + }, + + addContacts: function(db, transaction) { + debug("adding contacts for version " + this.version); + var os = transaction.objectStore(STORE_NAME); + + this.data.GOOD.forEach(function(contact) { + os.put(contact); + }); + this.data.BAD.forEach(function(contact) { + os.put(contact); + }); + }, + + contactsCount: function() { + return this.data.BAD.length + this.data.GOOD.length; + } +}; + +DataManager.delete = function() { + debug("Deleting the database"); + var deferred = Promise.defer(); + + /* forcibly close the db before deleting it */ + ContactService._db.close(); + + var req = indexedDB.deleteDatabase(DB_NAME); + req.onsuccess = function() { + debug("Successfully deleted!"); + deferred.resolve(); + }; + + req.onerror = function() { + debug("Not deleted, error is " + this.error.name); + deferred.reject(this.error); + }; + + req.onblocked = function() { + debug("Waiting for the current users"); + }; + + return deferred.promise; +}; + +addMessageListener("createDB", function(version) { + // Promises help handling gracefully exceptions + Promise.resolve().then(function() { + return new DataManager(version); + }).then(function(manager) { + return manager.open(); + }).then(function onSuccess(count) { + sendAsyncMessage("createDB.success", count); + }, function onError(err) { + sendAsyncMessage("createDB.error", "Failed to create the DB: " + + "(" + err.name + ") " + err.message); + }); +}); + +addMessageListener("deleteDB", function() { + Promise.resolve().then( + DataManager.delete + ).then(function onSuccess() { + sendAsyncMessage("deleteDB.success"); + }, function onError(err) { + sendAsyncMessage("deleteDB.error", "Failed to delete the DB:" + err.name); + }); +});