michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: function run_test() { michael@0: //**************************************************************************// michael@0: // Database Creation, Schema Migration, and Backup michael@0: michael@0: // Note: in these tests we use createInstance instead of getService michael@0: // so we can instantiate the service multiple times and make it run michael@0: // its database initialization code each time. michael@0: michael@0: // Create a new database. michael@0: { michael@0: ContentPrefTest.deleteDatabase(); michael@0: michael@0: // Get the service and make sure it has a ready database connection. michael@0: let cps = Cc["@mozilla.org/content-pref/service;1"]. michael@0: createInstance(Ci.nsIContentPrefService); michael@0: do_check_true(cps.DBConnection.connectionReady); michael@0: cps.DBConnection.close(); michael@0: } michael@0: michael@0: // Open an existing database. michael@0: { michael@0: let dbFile = ContentPrefTest.deleteDatabase(); michael@0: michael@0: let cps = Cc["@mozilla.org/content-pref/service;1"]. michael@0: createInstance(Ci.nsIContentPrefService); michael@0: cps.DBConnection.close(); michael@0: do_check_true(dbFile.exists()); michael@0: michael@0: // Get the service and make sure it has a ready database connection. michael@0: cps = Cc["@mozilla.org/content-pref/service;1"]. michael@0: createInstance(Ci.nsIContentPrefService); michael@0: do_check_true(cps.DBConnection.connectionReady); michael@0: cps.DBConnection.close(); michael@0: } michael@0: michael@0: // Open an empty database. michael@0: { michael@0: let dbFile = ContentPrefTest.deleteDatabase(); michael@0: michael@0: // Create an empty database. michael@0: let dbService = Cc["@mozilla.org/storage/service;1"]. michael@0: getService(Ci.mozIStorageService); michael@0: let dbConnection = dbService.openDatabase(dbFile); michael@0: do_check_eq(dbConnection.schemaVersion, 0); michael@0: dbConnection.close(); michael@0: do_check_true(dbFile.exists()); michael@0: michael@0: // Get the service and make sure it has created the schema. michael@0: let cps = Cc["@mozilla.org/content-pref/service;1"]. michael@0: createInstance(Ci.nsIContentPrefService); michael@0: do_check_neq(cps.DBConnection.schemaVersion, 0); michael@0: cps.DBConnection.close(); michael@0: } michael@0: michael@0: // Open a corrupted database. michael@0: { michael@0: let dbFile = ContentPrefTest.deleteDatabase(); michael@0: let backupDBFile = ContentPrefTest.deleteBackupDatabase(); michael@0: michael@0: // Create a corrupted database. michael@0: let foStream = Cc["@mozilla.org/network/file-output-stream;1"]. michael@0: createInstance(Ci.nsIFileOutputStream); michael@0: foStream.init(dbFile, 0x02 | 0x08 | 0x20, 0666, 0); michael@0: let garbageData = "garbage that makes SQLite think the file is corrupted"; michael@0: foStream.write(garbageData, garbageData.length); michael@0: foStream.close(); michael@0: michael@0: // Get the service and make sure it backs up and recreates the database. michael@0: let cps = Cc["@mozilla.org/content-pref/service;1"]. michael@0: createInstance(Ci.nsIContentPrefService); michael@0: do_check_true(backupDBFile.exists()); michael@0: do_check_true(cps.DBConnection.connectionReady); michael@0: michael@0: cps.DBConnection.close(); michael@0: } michael@0: michael@0: // Open a database with a corrupted schema. michael@0: { michael@0: let dbFile = ContentPrefTest.deleteDatabase(); michael@0: let backupDBFile = ContentPrefTest.deleteBackupDatabase(); michael@0: michael@0: // Create an empty database and set the schema version to a number michael@0: // that will trigger a schema migration that will fail. michael@0: let dbService = Cc["@mozilla.org/storage/service;1"]. michael@0: getService(Ci.mozIStorageService); michael@0: let dbConnection = dbService.openDatabase(dbFile); michael@0: dbConnection.schemaVersion = -1; michael@0: dbConnection.close(); michael@0: do_check_true(dbFile.exists()); michael@0: michael@0: // Get the service and make sure it backs up and recreates the database. michael@0: let cps = Cc["@mozilla.org/content-pref/service;1"]. michael@0: createInstance(Ci.nsIContentPrefService); michael@0: do_check_true(backupDBFile.exists()); michael@0: do_check_true(cps.DBConnection.connectionReady); michael@0: michael@0: cps.DBConnection.close(); michael@0: } michael@0: michael@0: michael@0: // Now get the content pref service for real for use by the rest of the tests. michael@0: let cps = new ContentPrefInstance(null); michael@0: michael@0: var uri = ContentPrefTest.getURI("http://www.example.com/"); michael@0: michael@0: // Make sure disk synchronization checking is turned off by default. michael@0: var statement = cps.DBConnection.createStatement("PRAGMA synchronous"); michael@0: statement.executeStep(); michael@0: do_check_eq(0, statement.getInt32(0)); michael@0: michael@0: //**************************************************************************// michael@0: // Nonexistent Pref michael@0: michael@0: do_check_eq(cps.getPref(uri, "test.nonexistent.getPref"), undefined); michael@0: do_check_eq(cps.setPref(uri, "test.nonexistent.setPref", 5), undefined); michael@0: do_check_false(cps.hasPref(uri, "test.nonexistent.hasPref")); michael@0: do_check_eq(cps.removePref(uri, "test.nonexistent.removePref"), undefined); michael@0: michael@0: michael@0: //**************************************************************************// michael@0: // Existing Pref michael@0: michael@0: cps.setPref(uri, "test.existing", 5); michael@0: michael@0: // getPref should return the pref value michael@0: do_check_eq(cps.getPref(uri, "test.existing"), 5); michael@0: michael@0: // setPref should return undefined and change the value of the pref michael@0: do_check_eq(cps.setPref(uri, "test.existing", 6), undefined); michael@0: do_check_eq(cps.getPref(uri, "test.existing"), 6); michael@0: michael@0: // hasPref should return true michael@0: do_check_true(cps.hasPref(uri, "test.existing")); michael@0: michael@0: // removePref should return undefined and remove the pref michael@0: do_check_eq(cps.removePref(uri, "test.existing"), undefined); michael@0: do_check_false(cps.hasPref(uri, "test.existing")); michael@0: michael@0: michael@0: //**************************************************************************// michael@0: // Round-Trip Data Integrity michael@0: michael@0: // Make sure pref values remain the same from setPref to getPref. michael@0: michael@0: cps.setPref(uri, "test.data-integrity.integer", 5); michael@0: do_check_eq(cps.getPref(uri, "test.data-integrity.integer"), 5); michael@0: michael@0: cps.setPref(uri, "test.data-integrity.float", 5.5); michael@0: do_check_eq(cps.getPref(uri, "test.data-integrity.float"), 5.5); michael@0: michael@0: cps.setPref(uri, "test.data-integrity.boolean", true); michael@0: do_check_eq(cps.getPref(uri, "test.data-integrity.boolean"), true); michael@0: michael@0: cps.setPref(uri, "test.data-integrity.string", "test"); michael@0: do_check_eq(cps.getPref(uri, "test.data-integrity.string"), "test"); michael@0: michael@0: cps.setPref(uri, "test.data-integrity.null", null); michael@0: do_check_eq(cps.getPref(uri, "test.data-integrity.null"), null); michael@0: michael@0: // XXX Test arbitrary binary data. michael@0: michael@0: // Make sure hasPref and removePref work on all data types. michael@0: michael@0: do_check_true(cps.hasPref(uri, "test.data-integrity.integer")); michael@0: do_check_true(cps.hasPref(uri, "test.data-integrity.float")); michael@0: do_check_true(cps.hasPref(uri, "test.data-integrity.boolean")); michael@0: do_check_true(cps.hasPref(uri, "test.data-integrity.string")); michael@0: do_check_true(cps.hasPref(uri, "test.data-integrity.null")); michael@0: michael@0: do_check_eq(cps.removePref(uri, "test.data-integrity.integer"), undefined); michael@0: do_check_eq(cps.removePref(uri, "test.data-integrity.float"), undefined); michael@0: do_check_eq(cps.removePref(uri, "test.data-integrity.boolean"), undefined); michael@0: do_check_eq(cps.removePref(uri, "test.data-integrity.string"), undefined); michael@0: do_check_eq(cps.removePref(uri, "test.data-integrity.null"), undefined); michael@0: michael@0: do_check_false(cps.hasPref(uri, "test.data-integrity.integer")); michael@0: do_check_false(cps.hasPref(uri, "test.data-integrity.float")); michael@0: do_check_false(cps.hasPref(uri, "test.data-integrity.boolean")); michael@0: do_check_false(cps.hasPref(uri, "test.data-integrity.string")); michael@0: do_check_false(cps.hasPref(uri, "test.data-integrity.null")); michael@0: michael@0: michael@0: //**************************************************************************// michael@0: // getPrefs michael@0: michael@0: cps.setPref(uri, "test.getPrefs.a", 1); michael@0: cps.setPref(uri, "test.getPrefs.b", 2); michael@0: cps.setPref(uri, "test.getPrefs.c", 3); michael@0: michael@0: var prefs = cps.getPrefs(uri); michael@0: do_check_true(prefs.hasKey("test.getPrefs.a")); michael@0: do_check_eq(prefs.get("test.getPrefs.a"), 1); michael@0: do_check_true(prefs.hasKey("test.getPrefs.b")); michael@0: do_check_eq(prefs.get("test.getPrefs.b"), 2); michael@0: do_check_true(prefs.hasKey("test.getPrefs.c")); michael@0: do_check_eq(prefs.get("test.getPrefs.c"), 3); michael@0: michael@0: michael@0: //**************************************************************************// michael@0: // Site-Specificity michael@0: michael@0: // These are all different sites, and setting a pref for one of them michael@0: // shouldn't set it for the others. michael@0: var uri1 = ContentPrefTest.getURI("http://www.domain1.com/"); michael@0: var uri2 = ContentPrefTest.getURI("http://foo.domain1.com/"); michael@0: var uri3 = ContentPrefTest.getURI("http://domain1.com/"); michael@0: var uri4 = ContentPrefTest.getURI("http://www.domain2.com/"); michael@0: michael@0: cps.setPref(uri1, "test.site-specificity.uri1", 5); michael@0: do_check_false(cps.hasPref(uri2, "test.site-specificity.uri1")); michael@0: do_check_false(cps.hasPref(uri3, "test.site-specificity.uri1")); michael@0: do_check_false(cps.hasPref(uri4, "test.site-specificity.uri1")); michael@0: michael@0: cps.setPref(uri2, "test.site-specificity.uri2", 5); michael@0: do_check_false(cps.hasPref(uri1, "test.site-specificity.uri2")); michael@0: do_check_false(cps.hasPref(uri3, "test.site-specificity.uri2")); michael@0: do_check_false(cps.hasPref(uri4, "test.site-specificity.uri2")); michael@0: michael@0: cps.setPref(uri3, "test.site-specificity.uri3", 5); michael@0: do_check_false(cps.hasPref(uri1, "test.site-specificity.uri3")); michael@0: do_check_false(cps.hasPref(uri2, "test.site-specificity.uri3")); michael@0: do_check_false(cps.hasPref(uri4, "test.site-specificity.uri3")); michael@0: michael@0: cps.setPref(uri4, "test.site-specificity.uri4", 5); michael@0: do_check_false(cps.hasPref(uri1, "test.site-specificity.uri4")); michael@0: do_check_false(cps.hasPref(uri2, "test.site-specificity.uri4")); michael@0: do_check_false(cps.hasPref(uri3, "test.site-specificity.uri4")); michael@0: michael@0: michael@0: //**************************************************************************// michael@0: // Observers michael@0: michael@0: var specificObserver = { michael@0: interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports], michael@0: michael@0: QueryInterface: function ContentPrefTest_QueryInterface(iid) { michael@0: if (!this.interfaces.some( function(v) { return iid.equals(v) } )) michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: return this; michael@0: }, michael@0: michael@0: numTimesSetCalled: 0, michael@0: onContentPrefSet: function specificObserver_onContentPrefSet(group, name, value) { michael@0: ++this.numTimesSetCalled; michael@0: do_check_eq(group, "www.example.com"); michael@0: do_check_eq(name, "test.observer.1"); michael@0: do_check_eq(value, "test value"); michael@0: }, michael@0: michael@0: numTimesRemovedCalled: 0, michael@0: onContentPrefRemoved: function specificObserver_onContentPrefRemoved(group, name) { michael@0: ++this.numTimesRemovedCalled; michael@0: do_check_eq(group, "www.example.com"); michael@0: do_check_eq(name, "test.observer.1"); michael@0: } michael@0: michael@0: }; michael@0: michael@0: var genericObserver = { michael@0: interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports], michael@0: michael@0: QueryInterface: function ContentPrefTest_QueryInterface(iid) { michael@0: if (!this.interfaces.some( function(v) { return iid.equals(v) } )) michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: return this; michael@0: }, michael@0: michael@0: numTimesSetCalled: 0, michael@0: onContentPrefSet: function genericObserver_onContentPrefSet(group, name, value) { michael@0: ++this.numTimesSetCalled; michael@0: do_check_eq(group, "www.example.com"); michael@0: if (name != "test.observer.1" && name != "test.observer.2") michael@0: do_throw("genericObserver.onContentPrefSet: " + michael@0: "name not in (test.observer.1, test.observer.2)"); michael@0: do_check_eq(value, "test value"); michael@0: }, michael@0: michael@0: numTimesRemovedCalled: 0, michael@0: onContentPrefRemoved: function genericObserver_onContentPrefRemoved(group, name) { michael@0: ++this.numTimesRemovedCalled; michael@0: do_check_eq(group, "www.example.com"); michael@0: if (name != "test.observer.1" && name != "test.observer.2") michael@0: do_throw("genericObserver.onContentPrefSet: " + michael@0: "name not in (test.observer.1, test.observer.2)"); michael@0: } michael@0: michael@0: }; michael@0: michael@0: // Make sure we can add observers, observers get notified about changes, michael@0: // specific observers only get notified about changes to the specific setting, michael@0: // and generic observers get notified about changes to all settings. michael@0: cps.addObserver("test.observer.1", specificObserver); michael@0: cps.addObserver(null, genericObserver); michael@0: cps.setPref(uri, "test.observer.1", "test value"); michael@0: cps.setPref(uri, "test.observer.2", "test value"); michael@0: cps.removePref(uri, "test.observer.1"); michael@0: cps.removePref(uri, "test.observer.2"); michael@0: do_check_eq(specificObserver.numTimesSetCalled, 1); michael@0: do_check_eq(genericObserver.numTimesSetCalled, 2); michael@0: do_check_eq(specificObserver.numTimesRemovedCalled, 1); michael@0: do_check_eq(genericObserver.numTimesRemovedCalled, 2); michael@0: michael@0: // Make sure we can remove observers and they don't get notified michael@0: // about changes anymore. michael@0: cps.removeObserver("test.observer.1", specificObserver); michael@0: cps.removeObserver(null, genericObserver); michael@0: cps.setPref(uri, "test.observer.1", "test value"); michael@0: cps.removePref(uri, "test.observer.1", "test value"); michael@0: do_check_eq(specificObserver.numTimesSetCalled, 1); michael@0: do_check_eq(genericObserver.numTimesSetCalled, 2); michael@0: do_check_eq(specificObserver.numTimesRemovedCalled, 1); michael@0: do_check_eq(genericObserver.numTimesRemovedCalled, 2); michael@0: michael@0: michael@0: //**************************************************************************// michael@0: // Get/Remove Prefs By Name michael@0: michael@0: { michael@0: var anObserver = { michael@0: interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports], michael@0: michael@0: QueryInterface: function ContentPrefTest_QueryInterface(iid) { michael@0: if (!this.interfaces.some( function(v) { return iid.equals(v) } )) michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: return this; michael@0: }, michael@0: michael@0: onContentPrefSet: function anObserver_onContentPrefSet(group, name, value) { michael@0: }, michael@0: michael@0: expectedDomains: [], michael@0: numTimesRemovedCalled: 0, michael@0: onContentPrefRemoved: function anObserver_onContentPrefRemoved(group, name) { michael@0: ++this.numTimesRemovedCalled; michael@0: michael@0: // remove the domain from the list of expected domains michael@0: var index = this.expectedDomains.indexOf(group); michael@0: do_check_true(index >= 0); michael@0: this.expectedDomains.splice(index, 1); michael@0: } michael@0: }; michael@0: michael@0: var uri1 = ContentPrefTest.getURI("http://www.domain1.com/"); michael@0: var uri2 = ContentPrefTest.getURI("http://foo.domain1.com/"); michael@0: var uri3 = ContentPrefTest.getURI("http://domain1.com/"); michael@0: var uri4 = ContentPrefTest.getURI("http://www.domain2.com/"); michael@0: michael@0: cps.setPref(uri1, "test.byname.1", 1); michael@0: cps.setPref(uri1, "test.byname.2", 2); michael@0: cps.setPref(uri2, "test.byname.1", 4); michael@0: cps.setPref(uri3, "test.byname.3", 8); michael@0: cps.setPref(uri4, "test.byname.1", 16); michael@0: cps.setPref(null, "test.byname.1", 32); michael@0: cps.setPref(null, "test.byname.2", false); michael@0: michael@0: function enumerateAndCheck(testName, expectedSum, expectedDomains) { michael@0: var prefsByName = cps.getPrefsByName(testName); michael@0: var enumerator = prefsByName.enumerator; michael@0: var sum = 0; michael@0: while (enumerator.hasMoreElements()) { michael@0: var property = enumerator.getNext().QueryInterface(Components.interfaces.nsIProperty); michael@0: sum += parseInt(property.value); michael@0: michael@0: // remove the domain from the list of expected domains michael@0: var index = expectedDomains.indexOf(property.name); michael@0: do_check_true(index >= 0); michael@0: expectedDomains.splice(index, 1); michael@0: } michael@0: do_check_eq(sum, expectedSum); michael@0: // check all domains have been removed from the array michael@0: do_check_eq(expectedDomains.length, 0); michael@0: } michael@0: michael@0: enumerateAndCheck("test.byname.1", 53, michael@0: ["foo.domain1.com", null, "www.domain1.com", "www.domain2.com"]); michael@0: enumerateAndCheck("test.byname.2", 2, ["www.domain1.com", null]); michael@0: enumerateAndCheck("test.byname.3", 8, ["domain1.com"]); michael@0: michael@0: cps.addObserver("test.byname.1", anObserver); michael@0: anObserver.expectedDomains = ["foo.domain1.com", null, "www.domain1.com", "www.domain2.com"]; michael@0: michael@0: cps.removePrefsByName("test.byname.1"); michael@0: do_check_false(cps.hasPref(uri1, "test.byname.1")); michael@0: do_check_false(cps.hasPref(uri2, "test.byname.1")); michael@0: do_check_false(cps.hasPref(uri3, "test.byname.1")); michael@0: do_check_false(cps.hasPref(uri4, "test.byname.1")); michael@0: do_check_false(cps.hasPref(null, "test.byname.1")); michael@0: do_check_true(cps.hasPref(uri1, "test.byname.2")); michael@0: do_check_true(cps.hasPref(uri3, "test.byname.3")); michael@0: michael@0: do_check_eq(anObserver.numTimesRemovedCalled, 4); michael@0: do_check_eq(anObserver.expectedDomains.length, 0); michael@0: michael@0: cps.removeObserver("test.byname.1", anObserver); michael@0: michael@0: // Clean up after ourselves michael@0: cps.removePref(uri1, "test.byname.2"); michael@0: cps.removePref(uri3, "test.byname.3"); michael@0: cps.removePref(null, "test.byname.2"); michael@0: } michael@0: michael@0: michael@0: //**************************************************************************// michael@0: // Clear Private Data Pref Removal michael@0: michael@0: { michael@0: let uri1 = ContentPrefTest.getURI("http://www.domain1.com/"); michael@0: let uri2 = ContentPrefTest.getURI("http://www.domain2.com/"); michael@0: let uri3 = ContentPrefTest.getURI("http://www.domain3.com/"); michael@0: michael@0: let dbConnection = cps.DBConnection; michael@0: michael@0: let prefCount = dbConnection.createStatement("SELECT COUNT(*) AS count FROM prefs"); michael@0: michael@0: let groupCount = dbConnection.createStatement("SELECT COUNT(*) AS count FROM groups"); michael@0: michael@0: // Add some prefs for multiple domains. michael@0: cps.setPref(uri1, "test.removeAllGroups", 1); michael@0: cps.setPref(uri2, "test.removeAllGroups", 2); michael@0: cps.setPref(uri3, "test.removeAllGroups", 3); michael@0: michael@0: // Add a global pref. michael@0: cps.setPref(null, "test.removeAllGroups", 1); michael@0: michael@0: // Make sure there are some prefs and groups in the database. michael@0: prefCount.executeStep(); michael@0: do_check_true(prefCount.row.count > 0); michael@0: prefCount.reset(); michael@0: groupCount.executeStep(); michael@0: do_check_true(groupCount.row.count > 0); michael@0: groupCount.reset(); michael@0: michael@0: // Remove all prefs and groups from the database using the same routine michael@0: // the Clear Private Data dialog uses. michael@0: cps.removeGroupedPrefs(); michael@0: michael@0: // Make sure there are no longer any groups in the database and the only pref michael@0: // is the global one. michael@0: prefCount.executeStep(); michael@0: do_check_true(prefCount.row.count == 1); michael@0: prefCount.reset(); michael@0: groupCount.executeStep(); michael@0: do_check_true(groupCount.row.count == 0); michael@0: groupCount.reset(); michael@0: let globalPref = dbConnection.createStatement("SELECT groupID FROM prefs"); michael@0: globalPref.executeStep(); michael@0: do_check_true(globalPref.row.groupID == null); michael@0: globalPref.reset(); michael@0: } michael@0: }