Bug 1058435 - Add removeAllDomainsSince to ContentPrefService2. r=adw

This commit is contained in:
Tomasz Kołodziejski 2014-09-19 14:24:00 -04:00
parent ef065ba355
commit 00cc687159
9 changed files with 439 additions and 95 deletions

View File

@ -67,7 +67,7 @@ interface nsIContentPref;
* See nsIContentPrefCallback2 below for more information about callbacks.
*/
[scriptable, uuid(f2507add-dc39-48e0-9147-e0270376148b)]
[scriptable, uuid(bed98666-d995-470f-bebd-62476d318576)]
interface nsIContentPrefService2 : nsISupports
{
/**
@ -299,6 +299,17 @@ interface nsIContentPrefService2 : nsISupports
void removeAllDomains(in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all non-global preferences created after and including |since|.
*
* @param since Timestamp in milliseconds.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeAllDomainsSince(in unsigned long long since,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all global preferences -- in other words, all preferences that have
* no domain.

View File

@ -286,7 +286,7 @@ ContentPrefService2.prototype = {
// Finally create or update the pref.
if (group) {
stmt = this._stmt(`
INSERT OR REPLACE INTO prefs (id, groupID, settingID, value)
INSERT OR REPLACE INTO prefs (id, groupID, settingID, value, timestamp)
VALUES(
(SELECT prefs.id
FROM prefs
@ -295,14 +295,15 @@ ContentPrefService2.prototype = {
WHERE groups.name = :group AND settings.name = :name),
(SELECT id FROM groups WHERE name = :group),
(SELECT id FROM settings WHERE name = :name),
:value
:value,
:now
)
`);
stmt.params.group = group;
}
else {
stmt = this._stmt(`
INSERT OR REPLACE INTO prefs (id, groupID, settingID, value)
INSERT OR REPLACE INTO prefs (id, groupID, settingID, value, timestamp)
VALUES(
(SELECT prefs.id
FROM prefs
@ -310,12 +311,14 @@ ContentPrefService2.prototype = {
WHERE prefs.groupID IS NULL AND settings.name = :name),
NULL,
(SELECT id FROM settings WHERE name = :name),
:value
:value,
:now
)
`);
}
stmt.params.name = name;
stmt.params.value = value;
stmt.params.now = Date.now() / 1000;
stmts.push(stmt);
this._execStmts(stmts, {
@ -379,18 +382,7 @@ ContentPrefService2.prototype = {
stmt.params.name = name;
stmts.push(stmt);
// Delete settings and groups that are no longer used. The NOTNULL term in
// the subquery of the second statment is needed because of SQLite's weird
// IN behavior vis-a-vis NULLs. See http://sqlite.org/lang_expr.html.
stmts.push(this._stmt(`
DELETE FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`));
stmts.push(this._stmt(`
DELETE FROM groups WHERE id NOT IN (
SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL
)
`));
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
let prefs = new ContentPrefStore();
@ -424,6 +416,23 @@ ContentPrefService2.prototype = {
});
},
// Deletes settings and groups that are no longer used.
_settingsAndGroupsCleanupStmts: function() {
// The NOTNULL term in the subquery of the second statment is needed because of
// SQLite's weird IN behavior vis-a-vis NULLs. See http://sqlite.org/lang_expr.html.
return [
this._stmt(`
DELETE FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`),
this._stmt(`
DELETE FROM groups WHERE id NOT IN (
SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL
)
`)
];
},
removeByDomain: function CPS2_removeByDomain(group, context, callback) {
checkGroupArg(group);
this._removeByDomain(group, false, context, callback);
@ -516,36 +525,40 @@ ContentPrefService2.prototype = {
});
},
removeAllDomains: function CPS2_removeAllDomains(context, callback) {
_removeAllDomainsSince: function CPS2__removeAllDomainsSince(since, context, callback) {
checkCallbackArg(callback, false);
since /= 1000;
// Invalidate the cached values so consumers accessing the cache between now
// and when the operation finishes don't get old data.
// Invalidate all the group cache because we don't know which groups will be removed.
this._cache.removeAllGroups();
let stmts = [];
// First get the matching prefs.
stmts.push(this._stmt(`
// Get prefs that are about to be removed to notify about their removal.
let stmt = this._stmt(`
SELECT groups.name AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
`));
WHERE timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
stmts.push(this._stmt(
"DELETE FROM prefs WHERE groupID NOTNULL"
));
stmts.push(this._stmt(
"DELETE FROM groups"
));
stmts.push(this._stmt(`
DELETE FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`));
// Do the actual remove.
stmt = this._stmt(`
DELETE FROM prefs WHERE groupID NOTNULL AND timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
// Cleanup no longer used values.
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
let prefs = new ContentPrefStore();
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
@ -554,6 +567,8 @@ ContentPrefService2.prototype = {
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
// This nukes all the groups in _pbStore since we don't have their timestamp
// information.
if (ok && context && context.usePrivateBrowsing) {
for (let [sgroup, sname, ] in this._pbStore) {
prefs.set(sgroup, sname, undefined);
@ -573,6 +588,14 @@ ContentPrefService2.prototype = {
});
},
removeAllDomainsSince: function CPS2_removeAllDomainsSince(since, context, callback) {
this._removeAllDomainsSince(since, context, callback);
},
removeAllDomains: function CPS2_removeAllDomains(context, callback) {
this._removeAllDomainsSince(0, context, callback);
},
removeByName: function CPS2_removeByName(name, context, callback) {
checkNameArg(name);
checkCallbackArg(callback, false);

View File

@ -998,20 +998,21 @@ ContentPrefService.prototype = {
//**************************************************************************//
// Database Creation & Access
_dbVersion: 3,
_dbVersion: 4,
_dbSchema: {
tables: {
groups: "id INTEGER PRIMARY KEY, \
name TEXT NOT NULL",
settings: "id INTEGER PRIMARY KEY, \
name TEXT NOT NULL",
prefs: "id INTEGER PRIMARY KEY, \
groupID INTEGER REFERENCES groups(id), \
settingID INTEGER NOT NULL REFERENCES settings(id), \
value BLOB"
value BLOB, \
timestamp INTEGER NOT NULL DEFAULT 0" // Storage in seconds, API in ms. 0 for migrated values.
},
indices: {
groups_idx: {
@ -1024,7 +1025,7 @@ ContentPrefService.prototype = {
},
prefs_idx: {
table: "prefs",
columns: ["groupID", "settingID"]
columns: ["timestamp", "groupID", "settingID"]
}
}
},
@ -1166,35 +1167,36 @@ ContentPrefService.prototype = {
},
_dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) {
if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) {
aDBConnection.beginTransaction();
try {
this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection);
aDBConnection.schemaVersion = aNewVersion;
aDBConnection.commitTransaction();
}
catch(ex) {
aDBConnection.rollbackTransaction();
throw ex;
aDBConnection.beginTransaction();
try {
/**
* If the schema version is 0, that means it was never set, which means
* the database was somehow created without the schema being applied, perhaps
* because the system ran out of disk space (although we check for this
* in _createDB) or because some other code created the database file without
* applying the schema. In any case, recover by simply reapplying the schema.
*/
if (aOldVersion == 0) {
this._dbCreateSchema(aDBConnection);
} else {
for (let i = aOldVersion; i < aNewVersion; i++) {
let migrationName = "_dbMigrate" + i + "To" + (i + 1);
if (typeof this[migrationName] != 'function') {
throw("no migrator function from version " + aOldVersion + " to version " + aNewVersion);
}
this[migrationName](aDBConnection);
}
}
aDBConnection.schemaVersion = aNewVersion;
aDBConnection.commitTransaction();
} catch (ex) {
aDBConnection.rollbackTransaction();
throw ex;
}
else
throw("no migrator function from version " + aOldVersion +
" to version " + aNewVersion);
},
/**
* If the schema version is 0, that means it was never set, which means
* the database was somehow created without the schema being applied, perhaps
* because the system ran out of disk space (although we check for this
* in _createDB) or because some other code created the database file without
* applying the schema. In any case, recover by simply reapplying the schema.
*/
_dbMigrate0To3: function ContentPrefService___dbMigrate0To3(aDBConnection) {
this._dbCreateSchema(aDBConnection);
},
_dbMigrate1To3: function ContentPrefService___dbMigrate1To3(aDBConnection) {
_dbMigrate1To2: function ContentPrefService___dbMigrate1To2(aDBConnection) {
aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld");
aDBConnection.createTable("groups", this._dbSchema.tables.groups);
aDBConnection.executeSimpleSQL(`
@ -1204,14 +1206,19 @@ ContentPrefService.prototype = {
aDBConnection.executeSimpleSQL("DROP TABLE groupers");
aDBConnection.executeSimpleSQL("DROP TABLE groupsOld");
this._dbCreateIndices(aDBConnection);
},
_dbMigrate2To3: function ContentPrefService__dbMigrate2To3(aDBConnection) {
this._dbCreateIndices(aDBConnection);
},
_dbMigrate3To4: function ContentPrefService__dbMigrate3To4(aDBConnection) {
aDBConnection.executeSimpleSQL("ALTER TABLE prefs ADD COLUMN timestamp INTEGER NOT NULL DEFAULT 0");
// To modify prefs_idx drop it and create again.
aDBConnection.executeSimpleSQL("DROP INDEX IF EXISTS prefs_idx");
this._dbCreateIndices(aDBConnection);
},
_parseGroupParam: function ContentPrefService__parseGroupParam(aGroup) {
if (aGroup == null)
return null;

View File

@ -15,7 +15,7 @@ var next;
do_get_profile();
})();
function runAsyncTests(tests) {
function runAsyncTests(tests, dontResetBefore = false) {
do_test_pending();
cps = Cc["@mozilla.org/content-pref/service;1"].
@ -31,7 +31,7 @@ function runAsyncTests(tests) {
asyncRunner = new s.AsyncRunner({
done: do_test_finished,
error: function (err) {
// xpcshell test functions like do_check_eq throw NS_ERROR_ABORT on
// xpcshell test functions like equal throw NS_ERROR_ABORT on
// failure. Ignore those and catch only uncaught exceptions.
if (err !== Cr.NS_ERROR_ABORT) {
if (err.stack) {
@ -76,21 +76,33 @@ function runAsyncTests(tests) {
});
// reset() ends up calling asyncRunner.next(), starting the tests.
reset();
if (dontResetBefore) {
next();
} else {
reset();
}
}
function makeCallback(callbacks) {
function makeCallback(callbacks, success = null) {
callbacks = callbacks || {};
["handleResult", "handleError"].forEach(function (meth) {
if (!callbacks[meth])
callbacks[meth] = function () {
do_throw(meth + " shouldn't be called.");
};
});
if (!callbacks.handleError) {
callbacks.handleError = function (error) {
do_throw("handleError call was not expected, error: " + error);
};
}
if (!callbacks.handleResult) {
callbacks.handleResult = function() {
do_throw("handleResult call was not expected");
};
}
if (!callbacks.handleCompletion)
callbacks.handleCompletion = function (reason) {
do_check_eq(reason, Ci.nsIContentPrefCallback2.COMPLETE_OK);
next();
equal(reason, Ci.nsIContentPrefCallback2.COMPLETE_OK);
if (success) {
success();
} else {
next();
}
};
return callbacks;
}
@ -103,7 +115,7 @@ function do_check_throws(fn) {
catch (err) {
threw = true;
}
do_check_true(threw);
ok(threw);
}
function sendMessage(msg, callback) {
@ -117,6 +129,60 @@ function reset() {
sendMessage("reset", next);
}
function setWithDate(group, name, val, timestamp, context) {
function updateDate() {
let db = sendMessage("db");
let stmt = db.createAsyncStatement(`
UPDATE prefs SET timestamp = :timestamp
WHERE
settingID = (SELECT id FROM settings WHERE name = :name)
AND groupID = (SELECT id FROM groups WHERE name = :group)
`);
stmt.params.timestamp = timestamp / 1000;
stmt.params.name = name;
stmt.params.group = group;
stmt.executeAsync({
handleCompletion: function (reason) {
next();
},
handleError: function (err) {
do_throw(err);
}
});
stmt.finalize();
}
cps.set(group, name, val, context, makeCallback(null, updateDate));
}
function getDate(group, name, context) {
let db = sendMessage("db");
let stmt = db.createAsyncStatement(`
SELECT timestamp FROM prefs
WHERE
settingID = (SELECT id FROM settings WHERE name = :name)
AND groupID = (SELECT id FROM groups WHERE name = :group)
`);
stmt.params.name = name;
stmt.params.group = group;
let res;
stmt.executeAsync({
handleResult: function (results) {
let row = results.getNextRow();
res = row.getResultByName("timestamp");
},
handleCompletion: function (reason) {
next(res * 1000);
},
handleError: function (err) {
do_throw(err);
}
});
stmt.finalize();
}
function set(group, name, val, context) {
cps.set(group, name, val, context, makeCallback());
}
@ -126,13 +192,13 @@ function setGlobal(name, val, context) {
}
function prefOK(actual, expected, strict) {
do_check_true(actual instanceof Ci.nsIContentPref);
do_check_eq(actual.domain, expected.domain);
do_check_eq(actual.name, expected.name);
ok(actual instanceof Ci.nsIContentPref);
equal(actual.domain, expected.domain);
equal(actual.name, expected.name);
if (strict)
do_check_true(actual.value === expected.value);
strictEqual(actual.value, expected.value);
else
do_check_eq(actual.value, expected.value);
equal(actual.value, expected.value);
}
function getOK(args, expectedVal, expectedGroup, strict) {
@ -194,7 +260,7 @@ function getCachedSubdomainsOK(args, expectedGroupValPairs) {
actualPrefs = actualPrefs.sort(function (a, b) {
return a.domain.localeCompare(b.domain);
});
do_check_eq(actualPrefs.length, len.value);
equal(actualPrefs.length, len.value);
let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) {
return { domain: group, name: args[1], value: val };
});
@ -217,19 +283,24 @@ function getCachedOKEx(methodName, args, expectedPref, strict) {
if (expectedPref)
prefOK(actualPref, expectedPref, strict);
else
do_check_true(actualPref === null);
strictEqual(actualPref, null);
}
function arraysOK(actual, expected, cmp) {
if (actual.length != expected.length) {
do_throw("Length is not equal: " + JSON.stringify(actual) + "==" + JSON.stringify(expected));
} else {
actual.forEach(function (actualElt, j) {
let expectedElt = expected[j];
cmp(actualElt, expectedElt);
});
}
}
function arraysOfArraysOK(actual, expected, cmp) {
cmp = cmp || function (a, b) do_check_eq(a, b);
do_check_eq(actual.length, expected.length);
actual.forEach(function (actualChildArr, i) {
let expectedChildArr = expected[i];
do_check_eq(actualChildArr.length, expectedChildArr.length);
actualChildArr.forEach(function (actualElt, j) {
let expectedElt = expectedChildArr[j];
cmp(actualElt, expectedElt);
});
cmp = cmp || equal;
arraysOK(actual, expected, function (act, exp) {
arraysOK(act, exp, cmp)
});
}
@ -320,11 +391,16 @@ function on(event, names, dontRemove) {
});
}
function schemaVersionIs(expectedVersion) {
let db = sendMessage("db");
equal(db.schemaVersion, expectedVersion);
}
function wait() {
do_execute_soon(next);
}
function observerArgsOK(actualArgs, expectedArgs) {
do_check_neq(actualArgs, undefined);
notEqual(actualArgs, undefined);
arraysOfArraysOK(actualArgs, expectedArgs);
}

View File

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Dump of version we migrate from
let schema_version3 = `
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE groups (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO "groups" VALUES(1,'foo.com');
INSERT INTO "groups" VALUES(2,'bar.com');
CREATE TABLE settings (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO "settings" VALUES(1,'zoom-setting');
INSERT INTO "settings" VALUES(2,'dir-setting');
CREATE TABLE prefs (id INTEGER PRIMARY KEY, groupID INTEGER REFERENCES groups(id), settingID INTEGER NOT NULL REFERENCES settings(id), value BLOB);
INSERT INTO "prefs" VALUES(1,1,1,0.5);
INSERT INTO "prefs" VALUES(2,1,2,'/download/dir');
INSERT INTO "prefs" VALUES(3,2,1,0.3);
INSERT INTO "prefs" VALUES(4,NULL,1,0.1);
CREATE INDEX groups_idx ON groups(name);
CREATE INDEX settings_idx ON settings(name);
CREATE INDEX prefs_idx ON prefs(groupID, settingID);
COMMIT;`;
function prepareVersion3Schema(callback) {
var dirService = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
var dbFile = dirService.get("ProfD", Ci.nsIFile);
dbFile.append("content-prefs.sqlite");
var dbService = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
ok(!dbFile.exists(), "Db should not exist yet.");
var dbConnection = dbService.openDatabase(dbFile);
equal(dbConnection.schemaVersion, 0);
dbConnection.executeSimpleSQL(schema_version3);
dbConnection.schemaVersion = 3;
dbConnection.close();
}
function run_test() {
prepareVersion3Schema();
runAsyncTests(tests, true);
}
// WARNING: Database will reset after every test. This limitation comes from
// the fact that we ContentPrefService constructor is run only once per test file
// and so migration will be run only once.
let tests = [
function testMigration() {
// Test migrated db content.
schemaVersionIs(4);
let dbExpectedState = [
[null, "zoom-setting", 0.1],
["bar.com", "zoom-setting", 0.3],
["foo.com", "zoom-setting", 0.5],
["foo.com", "dir-setting", "/download/dir"],
];
yield dbOK(dbExpectedState);
// Migrated fields should have timestamp set to 0.
yield cps.removeAllDomainsSince(1000, null, makeCallback());
yield dbOK(dbExpectedState);
yield cps.removeAllDomainsSince(0, null, makeCallback());
yield dbOK([[null, "zoom-setting", 0.1]]);
// Test that dates are present after migration (column is added).
const timestamp = 1234;
yield setWithDate("a.com", "pref-name", "val", timestamp);
let actualTimestamp = yield getDate("a.com", "pref-name");
equal(actualTimestamp, timestamp);
}
];

View File

@ -69,6 +69,25 @@ let tests = [
observerArgsOK(args.bar, []);
},
function observerForName_removeAllDomainsSince() {
yield setWithDate("a.com", "foo", 1, 100);
yield setWithDate("b.com", "foo", 2, 200);
yield setWithDate("c.com", "foo", 3, 300);
yield setWithDate("a.com", "bar", 1, 000);
yield setWithDate("b.com", "bar", 2, 100);
yield setWithDate("c.com", "bar", 3, 200);
yield setGlobal("foo", 2);
yield cps.removeAllDomainsSince(200, null, makeCallback());
let args = yield on("Removed", ["foo", "bar", null]);
observerArgsOK(args.foo, [["b.com", "foo"], ["c.com", "foo"]]);
observerArgsOK(args.bar, [["c.com", "bar"]]);
observerArgsOK(args.null, [["b.com", "foo"], ["c.com", "bar"], ["c.com", "foo"]]);
},
function observerForName_removeAllDomains() {
yield set("a.com", "foo", 1);
yield setGlobal("foo", 2);

View File

@ -0,0 +1,111 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
function run_test() {
runAsyncTests(tests);
}
let tests = [
function nonexistent() {
yield setGlobal("foo", 1);
yield cps.removeAllDomainsSince(0, null, makeCallback());
yield getGlobalOK(["foo"], 1);
},
function domainsAll() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
yield set("b.com", "bar", 6);
yield cps.removeAllDomainsSince(0, null, makeCallback());
yield dbOK([
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], undefined);
yield getOK(["b.com", "bar"], undefined);
},
function domainsWithDate() {
yield setWithDate("a.com", "foobar", 0, 0);
yield setWithDate("a.com", "foo", 1, 1000);
yield setWithDate("a.com", "bar", 2, 4000);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield setWithDate("b.com", "foo", 5, 2000);
yield setWithDate("b.com", "bar", 6, 3000);
yield setWithDate("b.com", "foobar", 7, 1000);
yield cps.removeAllDomainsSince(2000, null, makeCallback());
yield dbOK([
["a.com", "foobar", 0],
["a.com", "foo", 1],
[null, "foo", 3],
[null, "bar", 4],
["b.com", "foobar", 7],
]);
},
function privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield setGlobal("foo", 7, context);
yield cps.removeAllDomainsSince(0, context, makeCallback());
yield dbOK([
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo", context], undefined);
yield getOK(["a.com", "bar", context], undefined);
yield getGlobalOK(["foo", context], 7);
yield getGlobalOK(["bar", context], 4);
yield getOK(["b.com", "foo", context], undefined);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], undefined);
},
function erroneous() {
do_check_throws(function () cps.removeAllDomainsSince(null, "bogus"));
yield true;
},
function invalidateCache() {
yield setWithDate("a.com", "foobar", 0, 0);
yield setWithDate("a.com", "foo", 1, 1000);
yield setWithDate("a.com", "bar", 2, 4000);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield setWithDate("b.com", "foo", 5, 2000);
yield setWithDate("b.com", "bar", 6, 3000);
yield setWithDate("b.com", "foobar", 7, 1000);
cps.removeAllDomainsSince(0, null, makeCallback());
getCachedOK(["a.com", "foobar"], false);
getCachedOK(["a.com", "foo"], false);
getCachedOK(["a.com", "bar"], false);
getCachedGlobalOK(["foo"], true, 3);
getCachedGlobalOK(["bar"], true, 4);
getCachedOK(["b.com", "foo"], false);
getCachedOK(["b.com", "bar"], false);
getCachedOK(["b.com", "foobar"], false);
yield true;
},
];

View File

@ -191,5 +191,18 @@ let tests = [
{"domain": null, "name": "foo", "value": 4},
{"domain": "b.com", "name": "foo", "value": 5}
]);
}
},
function setSetsCurrentDate() {
// Because Date.now() is not guaranteed to be monotonically increasing
// we just do here rough sanity check with one minute tolerance.
const MINUTE = 60 * 1000;
let now = Date.now();
let start = now - MINUTE;
let end = now + MINUTE;
yield set("a.com", "foo", 1);
let timestamp = yield getDate("a.com", "foo");
ok(start <= timestamp, "Timestamp is not too early (" + start + "<=" + timestamp + ").");
ok(timestamp <= end, "Timestamp is not too late (" + timestamp + "<=" + end + ").");
},
];

View File

@ -14,3 +14,5 @@ support-files = AsyncRunner.jsm
[test_getCachedSubdomains.js]
[test_observers.js]
[test_extractDomain.js]
[test_migrationToSchema4.js]
[test_removeAllDomainsSince.js]