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