/* 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/. */ "use strict"; var fac; var numRecords, timeGroupingSize, now; const DEFAULT_EXPIRE_DAYS = 180; function padLeft(number, length) { let str = number + ""; while (str.length < length) { str = "0" + str; } return str; } function getFormExpiryDays() { if (Services.prefs.prefHasUserValue("browser.formfill.expire_days")) { return Services.prefs.getIntPref("browser.formfill.expire_days"); } return DEFAULT_EXPIRE_DAYS; } function run_test() { // ===== test init ===== let testfile = do_get_file("formhistory_autocomplete.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"); fac = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService( Ci.nsIFormAutoComplete ); timeGroupingSize = Services.prefs.getIntPref("browser.formfill.timeGroupingSize") * 1000 * 1000; run_next_test(); } add_test(function test0() { let maxTimeGroupings = Services.prefs.getIntPref( "browser.formfill.maxTimeGroupings" ); let bucketSize = Services.prefs.getIntPref("browser.formfill.bucketSize"); // ===== Tests with constant timesUsed and varying lastUsed date ===== // insert 2 records per bucket to check alphabetical sort within now = 1000 * Date.now(); numRecords = Math.ceil(maxTimeGroupings / bucketSize) * 2; let changes = []; for (let i = 0; i < numRecords; i += 2) { let useDate = now - (i / 2) * bucketSize * timeGroupingSize; changes.push({ op: "add", fieldname: "field1", value: "value" + padLeft(numRecords - 1 - i, 2), timesUsed: 1, firstUsed: useDate, lastUsed: useDate, }); changes.push({ op: "add", fieldname: "field1", value: "value" + padLeft(numRecords - 2 - i, 2), timesUsed: 1, firstUsed: useDate, lastUsed: useDate, }); } updateFormHistory(changes, run_next_test); }); add_test(function test1() { do_log_info("Check initial state is as expected"); countEntries(null, null, function () { countEntries("field1", null, function (count) { Assert.ok(count > 0); run_next_test(); }); }); }); add_test(function test2() { do_log_info("Check search contains all entries"); fac.autoCompleteSearchAsync("field1", "", null, null, false, { onSearchCompletion(aResults) { Assert.equal(numRecords, aResults.matchCount); run_next_test(); }, }); }); add_test(function test3() { do_log_info("Check search result ordering with empty search term"); let lastFound = numRecords; fac.autoCompleteSearchAsync("field1", "", null, null, false, { onSearchCompletion(aResults) { for (let i = 0; i < numRecords; i += 2) { Assert.equal( parseInt(aResults.getValueAt(i + 1).substr(5), 10), --lastFound ); Assert.equal( parseInt(aResults.getValueAt(i).substr(5), 10), --lastFound ); } run_next_test(); }, }); }); add_test(function test4() { do_log_info('Check search result ordering with "v"'); let lastFound = numRecords; fac.autoCompleteSearchAsync("field1", "v", null, null, false, { onSearchCompletion(aResults) { for (let i = 0; i < numRecords; i += 2) { Assert.equal( parseInt(aResults.getValueAt(i + 1).substr(5), 10), --lastFound ); Assert.equal( parseInt(aResults.getValueAt(i).substr(5), 10), --lastFound ); } run_next_test(); }, }); }); const timesUsedSamples = 20; add_test(function test5() { do_log_info("Begin tests with constant use dates and varying timesUsed"); let changes = []; for (let i = 0; i < timesUsedSamples; i++) { let timesUsed = timesUsedSamples - i; let change = { op: "add", fieldname: "field2", value: "value" + (timesUsedSamples - 1 - i), timesUsed: timesUsed * timeGroupingSize, firstUsed: now, lastUsed: now, }; changes.push(change); } updateFormHistory(changes, run_next_test); }); add_test(function test6() { do_log_info("Check search result ordering with empty search term"); let lastFound = timesUsedSamples; fac.autoCompleteSearchAsync("field2", "", null, null, false, { onSearchCompletion(aResults) { for (let i = 0; i < timesUsedSamples; i++) { Assert.equal( parseInt(aResults.getValueAt(i).substr(5), 10), --lastFound ); } run_next_test(); }, }); }); add_test(function test7() { do_log_info('Check search result ordering with "v"'); let lastFound = timesUsedSamples; fac.autoCompleteSearchAsync("field2", "v", null, null, false, { onSearchCompletion(aResults) { for (let i = 0; i < timesUsedSamples; i++) { Assert.equal( parseInt(aResults.getValueAt(i).substr(5), 10), --lastFound ); } run_next_test(); }, }); }); add_test(function test8() { do_log_info( 'Check that "senior citizen" entries get a bonus (browser.formfill.agedBonus)' ); let agedDate = 1000 * (Date.now() - getFormExpiryDays() * 24 * 60 * 60 * 1000); let changes = []; changes.push({ op: "add", fieldname: "field3", value: "old but not senior", timesUsed: 100, firstUsed: agedDate + 60 * 1000 * 1000, lastUsed: now, }); changes.push({ op: "add", fieldname: "field3", value: "senior citizen", timesUsed: 100, firstUsed: agedDate - 60 * 1000 * 1000, lastUsed: now, }); updateFormHistory(changes, run_next_test); }); add_test(function test9() { fac.autoCompleteSearchAsync("field3", "", null, null, false, { onSearchCompletion(aResults) { Assert.equal(aResults.getValueAt(0), "senior citizen"); Assert.equal(aResults.getValueAt(1), "old but not senior"); run_next_test(); }, }); }); add_test(function test10() { do_log_info("Check entries that are really old or in the future"); let changes = []; changes.push({ op: "add", fieldname: "field4", value: "date of 0", timesUsed: 1, firstUsed: 0, lastUsed: 0, }); changes.push({ op: "add", fieldname: "field4", value: "in the future 1", timesUsed: 1, firstUsed: 0, lastUsed: now * 2, }); changes.push({ op: "add", fieldname: "field4", value: "in the future 2", timesUsed: 1, firstUsed: now * 2, lastUsed: now * 2, }); updateFormHistory(changes, run_next_test); }); add_test(function test11() { fac.autoCompleteSearchAsync("field4", "", null, null, false, { onSearchCompletion(aResults) { Assert.equal(aResults.matchCount, 3); run_next_test(); }, }); }); var syncValues = ["sync1", "sync1a", "sync2", "sync3"]; add_test(function test12() { do_log_info("Check old synchronous api"); let changes = []; for (let value of syncValues) { changes.push({ op: "add", fieldname: "field5", value }); } updateFormHistory(changes, run_next_test); }); add_test(function test_token_limit_DB() { function test_token_limit_previousResult(previousResult) { do_log_info( "Check that the number of tokens used in a search is not capped to " + "MAX_SEARCH_TOKENS when using a previousResult" ); // This provide more accuracy since performance is less of an issue. // Search for a string where the first 10 tokens match the previous value but the 11th does not // when re-using a previous result. fac.autoCompleteSearchAsync( "field_token_cap", "a b c d e f g h i j .", null, previousResult, false, { onSearchCompletion(aResults) { Assert.equal( aResults.matchCount, 0, "All search tokens should be used with previous results" ); run_next_test(); }, } ); } do_log_info( "Check that the number of tokens used in a search is capped to MAX_SEARCH_TOKENS " + "for performance when querying the DB" ); let changes = []; changes.push({ op: "add", fieldname: "field_token_cap", // value with 15 unique tokens value: "a b c d e f g h i j k l m n o", timesUsed: 1, firstUsed: 0, lastUsed: 0, }); updateFormHistory(changes, () => { // Search for a string where the first 10 tokens match the value above but the 11th does not // (which would prevent the result from being returned if the 11th term was used). fac.autoCompleteSearchAsync( "field_token_cap", "a b c d e f g h i j .", null, null, false, { onSearchCompletion(aResults) { Assert.equal( aResults.matchCount, 1, "Only the first MAX_SEARCH_TOKENS tokens " + "should be used for DB queries" ); test_token_limit_previousResult(aResults); }, } ); }); }); add_test(async function can_search_escape_marker() { await promiseUpdate({ op: "add", fieldname: "field1", value: "/* Further reading */ test", timesUsed: 1, firstUsed: now, lastUsed: now, }); fac.autoCompleteSearchAsync( "field1", "/* Further reading */ t", null, null, false, { onSearchCompletion(aResults) { Assert.equal(1, aResults.matchCount); run_next_test(); }, } ); });