Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* 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/. */
var testnum = 0;
var dbConnection; // used for deleted table tests
async function countDeletedEntries(expected) {
let stmt = "SELECT COUNT(*) AS numEntries FROM moz_deleted_formhistory";
try {
let requiredRow = await dbConnection.executeCached(stmt);
Assert.equal(expected, requiredRow[0].getResultByName("numEntries"));
} catch (error) {
do_throw("Error occurred counting deleted entries: " + error);
}
}
async function checkTimeDeleted(guid, checkFunction) {
let stmt =
"SELECT timeDeleted FROM moz_deleted_formhistory WHERE guid = :guid";
let params = { guid };
try {
let requiredRow = await dbConnection.executeCached(stmt, params);
checkFunction(requiredRow[0].getResultByName("timeDeleted"));
} catch (error) {
do_throw("Error occurred getting deleted entries: " + error);
}
}
function promiseUpdateEntry(op, name, value) {
let change = { op };
if (name !== null) {
change.fieldname = name;
}
if (value !== null) {
change.value = value;
}
return promiseUpdate(change);
}
add_task(async function () {
let oldSupportsDeletedTable = FormHistory._supportsDeletedTable;
FormHistory._supportsDeletedTable = true;
try {
// ===== test init =====
let testfile = do_get_file("formhistory_apitest.sqlite");
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
// Cleanup from any previous tests or failures.
let destFile = profileDir.clone();
destFile.append("formhistory.sqlite");
if (destFile.exists()) {
destFile.remove(false);
}
testfile.copyTo(profileDir, "formhistory.sqlite");
function checkExists(num) {
Assert.ok(num > 0);
}
function checkNotExists(num) {
Assert.ok(num == 0);
}
// ===== 1 =====
// Check initial state is as expected
testnum++;
await promiseCountEntries("name-A", null, checkExists);
await promiseCountEntries("name-B", null, checkExists);
await promiseCountEntries("name-C", null, checkExists);
await promiseCountEntries("name-D", null, checkExists);
await promiseCountEntries("name-A", "value-A", checkExists);
await promiseCountEntries("name-B", "value-B1", checkExists);
await promiseCountEntries("name-B", "value-B2", checkExists);
await promiseCountEntries("name-C", "value-C", checkExists);
await promiseCountEntries("name-D", "value-D", checkExists);
// time-A/B/C/D checked below.
// Delete anything from the deleted table
let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
dbFile.append("formhistory.sqlite");
dbConnection = await Sqlite.openConnection({
path: dbFile.path,
sharedMemoryCache: false,
});
let stmt = "DELETE FROM moz_deleted_formhistory";
try {
await dbConnection.executeCached(stmt);
} catch (error) {
do_throw("Error occurred counting deleted all entries: " + error);
}
// ===== 2 =====
// Test looking for nonexistent / bogus data.
testnum++;
await promiseCountEntries("blah", null, checkNotExists);
await promiseCountEntries("", null, checkNotExists);
await promiseCountEntries("name-A", "blah", checkNotExists);
await promiseCountEntries("name-A", "", checkNotExists);
await promiseCountEntries("name-A", null, checkExists);
await promiseCountEntries("blah", "value-A", checkNotExists);
await promiseCountEntries("", "value-A", checkNotExists);
await promiseCountEntries(null, "value-A", checkExists);
// Cannot use promiseCountEntries when name and value are null
// because it treats null values as not set
// and here a search should be done explicity for null.
let count = await FormHistory.count({ fieldname: null, value: null });
checkNotExists(count);
// ===== 3 =====
// Test removeEntriesForName with a single matching value
testnum++;
await promiseUpdateEntry("remove", "name-A", null);
await promiseCountEntries("name-A", "value-A", checkNotExists);
await promiseCountEntries("name-B", "value-B1", checkExists);
await promiseCountEntries("name-B", "value-B2", checkExists);
await promiseCountEntries("name-C", "value-C", checkExists);
await promiseCountEntries("name-D", "value-D", checkExists);
await countDeletedEntries(1);
// ===== 4 =====
// Test removeEntriesForName with multiple matching values
testnum++;
await promiseUpdateEntry("remove", "name-B", null);
await promiseCountEntries("name-A", "value-A", checkNotExists);
await promiseCountEntries("name-B", "value-B1", checkNotExists);
await promiseCountEntries("name-B", "value-B2", checkNotExists);
await promiseCountEntries("name-C", "value-C", checkExists);
await promiseCountEntries("name-D", "value-D", checkExists);
await countDeletedEntries(3);
// ===== 5 =====
// Test removing by time range (single entry, not surrounding entries)
testnum++;
await promiseCountEntries("time-A", null, checkExists); // firstUsed=1000, lastUsed=1000
await promiseCountEntries("time-B", null, checkExists); // firstUsed=1000, lastUsed=1099
await promiseCountEntries("time-C", null, checkExists); // firstUsed=1099, lastUsed=1099
await promiseCountEntries("time-D", null, checkExists); // firstUsed=2001, lastUsed=2001
await promiseUpdate({
op: "remove",
firstUsedStart: 1050,
firstUsedEnd: 2000,
});
await promiseCountEntries("time-A", null, checkExists);
await promiseCountEntries("time-B", null, checkExists);
await promiseCountEntries("time-C", null, checkNotExists);
await promiseCountEntries("time-D", null, checkExists);
await countDeletedEntries(4);
// ===== 6 =====
// Test removing by time range (multiple entries)
testnum++;
await promiseUpdate({
op: "remove",
firstUsedStart: 1000,
firstUsedEnd: 2000,
});
await promiseCountEntries("time-A", null, checkNotExists);
await promiseCountEntries("time-B", null, checkNotExists);
await promiseCountEntries("time-C", null, checkNotExists);
await promiseCountEntries("time-D", null, checkExists);
await countDeletedEntries(6);
// ===== 7 =====
// test removeAllEntries
testnum++;
await promiseUpdateEntry("remove", null, null);
await promiseCountEntries("name-C", null, checkNotExists);
await promiseCountEntries("name-D", null, checkNotExists);
await promiseCountEntries("name-C", "value-C", checkNotExists);
await promiseCountEntries("name-D", "value-D", checkNotExists);
await promiseCountEntries(null, null, checkNotExists);
await countDeletedEntries(6);
// ===== 8 =====
// Add a single entry back
testnum++;
await promiseUpdateEntry("add", "newname-A", "newvalue-A");
await promiseCountEntries("newname-A", "newvalue-A", checkExists);
// ===== 9 =====
// Remove the single entry
testnum++;
await promiseUpdateEntry("remove", "newname-A", "newvalue-A");
await promiseCountEntries("newname-A", "newvalue-A", checkNotExists);
// ===== 10 =====
// Add a single entry
testnum++;
await promiseUpdateEntry("add", "field1", "value1");
await promiseCountEntries("field1", "value1", checkExists);
let processFirstResult = function processResults(results) {
// Only handle the first result
if (results.length) {
let result = results[0];
return [
result.timesUsed,
result.firstUsed,
result.lastUsed,
result.guid,
];
}
return undefined;
};
let results = await FormHistory.search(
["timesUsed", "firstUsed", "lastUsed"],
{ fieldname: "field1", value: "value1" }
);
let [timesUsed, firstUsed, lastUsed] = processFirstResult(results);
Assert.equal(1, timesUsed);
Assert.ok(firstUsed > 0);
Assert.ok(lastUsed > 0);
await promiseCountEntries(null, null, num => Assert.equal(num, 1));
// ===== 11 =====
// Add another single entry
testnum++;
await promiseUpdateEntry("add", "field1", "value1b");
await promiseCountEntries("field1", "value1", checkExists);
await promiseCountEntries("field1", "value1b", checkExists);
await promiseCountEntries(null, null, num => Assert.equal(num, 2));
// ===== 12 =====
// Update a single entry
testnum++;
results = await FormHistory.search(["guid"], {
fieldname: "field1",
value: "value1",
});
let guid = processFirstResult(results)[3];
await promiseUpdate({ op: "update", guid, value: "modifiedValue" });
await promiseCountEntries("field1", "modifiedValue", checkExists);
await promiseCountEntries("field1", "value1", checkNotExists);
await promiseCountEntries("field1", "value1b", checkExists);
await promiseCountEntries(null, null, num => Assert.equal(num, 2));
// ===== 13 =====
// Add a single entry with times
testnum++;
await promiseUpdate({
op: "add",
fieldname: "field2",
value: "value2",
timesUsed: 20,
firstUsed: 100,
lastUsed: 500,
});
results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
fieldname: "field2",
value: "value2",
});
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
Assert.equal(20, timesUsed);
Assert.equal(100, firstUsed);
Assert.equal(500, lastUsed);
await promiseCountEntries(null, null, num => Assert.equal(num, 3));
// ===== 14 =====
// Bump an entry, which updates its lastUsed field
testnum++;
await promiseUpdate({
op: "bump",
fieldname: "field2",
value: "value2",
timesUsed: 20,
firstUsed: 100,
lastUsed: 500,
});
results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
fieldname: "field2",
value: "value2",
});
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
Assert.equal(21, timesUsed);
Assert.equal(100, firstUsed);
Assert.ok(lastUsed > 500);
await promiseCountEntries(null, null, num => Assert.equal(num, 3));
// ===== 15 =====
// Bump an entry that does not exist
testnum++;
await promiseUpdate({
op: "bump",
fieldname: "field3",
value: "value3",
timesUsed: 10,
firstUsed: 50,
lastUsed: 400,
});
results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
fieldname: "field3",
value: "value3",
});
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
Assert.equal(10, timesUsed);
Assert.equal(50, firstUsed);
Assert.equal(400, lastUsed);
await promiseCountEntries(null, null, num => Assert.equal(num, 4));
// ===== 16 =====
// Bump an entry with a guid
testnum++;
results = await FormHistory.search(["guid"], {
fieldname: "field3",
value: "value3",
});
guid = processFirstResult(results)[3];
await promiseUpdate({
op: "bump",
guid,
timesUsed: 20,
firstUsed: 55,
lastUsed: 400,
});
results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
fieldname: "field3",
value: "value3",
});
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
Assert.equal(11, timesUsed);
Assert.equal(50, firstUsed);
Assert.ok(lastUsed > 400);
await promiseCountEntries(null, null, num => Assert.equal(num, 4));
// ===== 17 =====
// Remove an entry
testnum++;
await countDeletedEntries(7);
results = await FormHistory.search(["guid"], {
fieldname: "field1",
value: "value1b",
});
guid = processFirstResult(results)[3];
await promiseUpdate({ op: "remove", guid });
await promiseCountEntries("field1", "modifiedValue", checkExists);
await promiseCountEntries("field1", "value1b", checkNotExists);
await promiseCountEntries(null, null, num => Assert.equal(num, 3));
await countDeletedEntries(8);
await checkTimeDeleted(guid, timeDeleted => Assert.ok(timeDeleted > 10000));
// ===== 18 =====
// Add yet another single entry
testnum++;
await promiseUpdate({
op: "add",
fieldname: "field4",
value: "value4",
timesUsed: 5,
firstUsed: 230,
lastUsed: 600,
});
await promiseCountEntries(null, null, num => Assert.equal(num, 4));
// ===== 19 =====
// Remove an entry by time
testnum++;
await promiseUpdate({
op: "remove",
firstUsedStart: 60,
firstUsedEnd: 250,
});
await promiseCountEntries("field1", "modifiedValue", checkExists);
await promiseCountEntries("field2", "value2", checkNotExists);
await promiseCountEntries("field3", "value3", checkExists);
await promiseCountEntries("field4", "value4", checkNotExists);
await promiseCountEntries(null, null, num => Assert.equal(num, 2));
await countDeletedEntries(10);
// ===== 20 =====
// Bump multiple existing entries at once
testnum++;
await promiseUpdate([
{
op: "add",
fieldname: "field5",
value: "value5",
timesUsed: 5,
firstUsed: 230,
lastUsed: 600,
},
{
op: "add",
fieldname: "field6",
value: "value6",
timesUsed: 12,
firstUsed: 430,
lastUsed: 700,
},
]);
await promiseCountEntries(null, null, num => Assert.equal(num, 4));
await promiseUpdate([
{ op: "bump", fieldname: "field5", value: "value5" },
{ op: "bump", fieldname: "field6", value: "value6" },
]);
results = await FormHistory.search(
["fieldname", "timesUsed", "firstUsed", "lastUsed"],
{}
);
Assert.equal(6, results[2].timesUsed);
Assert.equal(13, results[3].timesUsed);
Assert.equal(230, results[2].firstUsed);
Assert.equal(430, results[3].firstUsed);
Assert.ok(results[2].lastUsed > 600);
Assert.ok(results[3].lastUsed > 700);
await promiseCountEntries(null, null, num => Assert.equal(num, 4));
// ===== 21 =====
// Check update fails if form history is disabled and the operation is not a
// pure removal.
testnum++;
Services.prefs.setBoolPref("browser.formfill.enable", false);
// Cannot use arrow functions, see bug 1237961.
await Assert.rejects(
promiseUpdate({ op: "bump", fieldname: "field5", value: "value5" }),
/Form history is disabled, only remove operations are allowed/,
"bumping when form history is disabled should fail"
);
await Assert.rejects(
promiseUpdate({ op: "add", fieldname: "field5", value: "value5" }),
/Form history is disabled, only remove operations are allowed/,
"Adding when form history is disabled should fail"
);
await Assert.rejects(
promiseUpdate([
{ op: "update", fieldname: "field5", value: "value5" },
{ op: "remove", fieldname: "field5", value: "value5" },
]),
/Form history is disabled, only remove operations are allowed/,
"mixed operations when form history is disabled should fail"
);
await Assert.rejects(
promiseUpdate([
null,
undefined,
"",
1,
{},
{ op: "remove", fieldname: "field5", value: "value5" },
]),
/Form history is disabled, only remove operations are allowed/,
"Invalid entries when form history is disabled should fail"
);
// Remove should work though.
await promiseUpdate([
{ op: "remove", fieldname: "field5", value: null },
{ op: "remove", fieldname: null, value: null },
]);
Services.prefs.clearUserPref("browser.formfill.enable");
} catch (e) {
throw new Error(`FAILED in test #${testnum} -- ${e}`);
} finally {
FormHistory._supportsDeletedTable = oldSupportsDeletedTable;
await dbConnection.close(do_test_finished);
}
});
function run_test() {
return run_next_test();
}