1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/tests/unit/test_adaptive.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,376 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/** 1.11 + * Test for bug 395739 to make sure the feedback to the search results in those 1.12 + * entries getting better ranks. Additionally, exact matches should be ranked 1.13 + * higher. Because the interactions among adaptive rank and visit counts is not 1.14 + * well defined, this test holds one of the two values constant when modifying 1.15 + * the other. 1.16 + * 1.17 + * This also tests bug 395735 for the instrumentation feedback mechanism. 1.18 + * 1.19 + * Bug 411293 is tested to make sure the drop down strongly prefers previously 1.20 + * typed pages that have been selected and are moved to the top with adaptive 1.21 + * learning. 1.22 + */ 1.23 + 1.24 +function AutoCompleteInput(aSearches) { 1.25 + this.searches = aSearches; 1.26 +} 1.27 +AutoCompleteInput.prototype = { 1.28 + constructor: AutoCompleteInput, 1.29 + 1.30 + get minResultsForPopup() 0, 1.31 + get timeout() 10, 1.32 + get searchParam() "", 1.33 + get textValue() "", 1.34 + get disableAutoComplete() false, 1.35 + get completeDefaultIndex() false, 1.36 + 1.37 + get searchCount() this.searches.length, 1.38 + getSearchAt: function (aIndex) this.searches[aIndex], 1.39 + 1.40 + onSearchBegin: function () {}, 1.41 + onSearchComplete: function() {}, 1.42 + 1.43 + get popupOpen() false, 1.44 + popup: { 1.45 + set selectedIndex(aIndex) aIndex, 1.46 + invalidate: function () {}, 1.47 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup]) 1.48 + }, 1.49 + 1.50 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput]) 1.51 +} 1.52 + 1.53 +/** 1.54 + * Checks that autocomplete results are ordered correctly. 1.55 + */ 1.56 +function ensure_results(expected, searchTerm) 1.57 +{ 1.58 + let controller = Cc["@mozilla.org/autocomplete/controller;1"]. 1.59 + getService(Ci.nsIAutoCompleteController); 1.60 + 1.61 + // Make an AutoCompleteInput that uses our searches 1.62 + // and confirms results on search complete. 1.63 + let input = new AutoCompleteInput(["history"]); 1.64 + 1.65 + controller.input = input; 1.66 + 1.67 + input.onSearchComplete = function() { 1.68 + do_check_eq(controller.searchStatus, 1.69 + Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH); 1.70 + do_check_eq(controller.matchCount, expected.length); 1.71 + for (let i = 0; i < controller.matchCount; i++) { 1.72 + print("Testing for '" + expected[i].uri.spec + "' got '" + controller.getValueAt(i) + "'"); 1.73 + do_check_eq(controller.getValueAt(i), expected[i].uri.spec); 1.74 + do_check_eq(controller.getStyleAt(i), expected[i].style); 1.75 + } 1.76 + 1.77 + deferEnsureResults.resolve(); 1.78 + }; 1.79 + 1.80 + controller.startSearch(searchTerm); 1.81 +} 1.82 + 1.83 +/** 1.84 + * Asynchronous task that bumps up the rank for an uri. 1.85 + */ 1.86 +function task_setCountRank(aURI, aCount, aRank, aSearch, aBookmark) 1.87 +{ 1.88 + // Bump up the visit count for the uri. 1.89 + let visits = []; 1.90 + for (let i = 0; i < aCount; i++) { 1.91 + visits.push({ uri: aURI, visitDate: d1, transition: TRANSITION_TYPED }); 1.92 + } 1.93 + yield promiseAddVisits(visits); 1.94 + 1.95 + // Make a nsIAutoCompleteController and friends for instrumentation feedback. 1.96 + let thing = { 1.97 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput, 1.98 + Ci.nsIAutoCompletePopup, 1.99 + Ci.nsIAutoCompleteController]), 1.100 + get popup() thing, 1.101 + get controller() thing, 1.102 + popupOpen: true, 1.103 + selectedIndex: 0, 1.104 + getValueAt: function() aURI.spec, 1.105 + searchString: aSearch 1.106 + }; 1.107 + 1.108 + // Bump up the instrumentation feedback. 1.109 + for (let i = 0; i < aRank; i++) { 1.110 + Services.obs.notifyObservers(thing, "autocomplete-will-enter-text", null); 1.111 + } 1.112 + 1.113 + // If this is supposed to be a bookmark, add it. 1.114 + if (aBookmark) { 1.115 + PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, 1.116 + aURI, 1.117 + PlacesUtils.bookmarks.DEFAULT_INDEX, 1.118 + "test_book"); 1.119 + 1.120 + // And add the tag if we need to. 1.121 + if (aBookmark == "tag") { 1.122 + PlacesUtils.tagging.tagURI(aURI, ["test_tag"]); 1.123 + } 1.124 + } 1.125 +} 1.126 + 1.127 +/** 1.128 + * Decay the adaptive entries by sending the daily idle topic. 1.129 + */ 1.130 +function doAdaptiveDecay() 1.131 +{ 1.132 + PlacesUtils.history.runInBatchMode({ 1.133 + runBatched: function() { 1.134 + for (let i = 0; i < 10; i++) { 1.135 + PlacesUtils.history.QueryInterface(Ci.nsIObserver) 1.136 + .observe(null, "idle-daily", null); 1.137 + } 1.138 + } 1.139 + }, this); 1.140 +} 1.141 + 1.142 +let uri1 = uri("http://site.tld/1"); 1.143 +let uri2 = uri("http://site.tld/2"); 1.144 + 1.145 +// d1 is some date for the page visit 1.146 +let d1 = new Date(Date.now() - 1000 * 60 * 60) * 1000; 1.147 +// c1 is larger (should show up higher) than c2 1.148 +let c1 = 10; 1.149 +let c2 = 1; 1.150 +// s1 is a partial match of s2 1.151 +let s0 = ""; 1.152 +let s1 = "si"; 1.153 +let s2 = "site"; 1.154 + 1.155 +let observer = { 1.156 + results: null, 1.157 + search: null, 1.158 + runCount: -1, 1.159 + observe: function(aSubject, aTopic, aData) 1.160 + { 1.161 + if (--this.runCount > 0) 1.162 + return; 1.163 + ensure_results(this.results, this.search); 1.164 + } 1.165 +}; 1.166 +Services.obs.addObserver(observer, PlacesUtils.TOPIC_FEEDBACK_UPDATED, false); 1.167 + 1.168 +/** 1.169 + * Make the result object for a given URI that will be passed to ensure_results. 1.170 + */ 1.171 +function makeResult(aURI) { 1.172 + return { 1.173 + uri: aURI, 1.174 + style: "favicon", 1.175 + }; 1.176 +} 1.177 + 1.178 +let tests = [ 1.179 + // Test things without a search term. 1.180 + function() { 1.181 + print("Test 0 same count, diff rank, same term; no search"); 1.182 + observer.results = [ 1.183 + makeResult(uri1), 1.184 + makeResult(uri2), 1.185 + ]; 1.186 + observer.search = s0; 1.187 + observer.runCount = c1 + c2; 1.188 + yield task_setCountRank(uri1, c1, c1, s2); 1.189 + yield task_setCountRank(uri2, c1, c2, s2); 1.190 + }, 1.191 + function() { 1.192 + print("Test 1 same count, diff rank, same term; no search"); 1.193 + observer.results = [ 1.194 + makeResult(uri2), 1.195 + makeResult(uri1), 1.196 + ]; 1.197 + observer.search = s0; 1.198 + observer.runCount = c1 + c2; 1.199 + yield task_setCountRank(uri1, c1, c2, s2); 1.200 + yield task_setCountRank(uri2, c1, c1, s2); 1.201 + }, 1.202 + function() { 1.203 + print("Test 2 diff count, same rank, same term; no search"); 1.204 + observer.results = [ 1.205 + makeResult(uri1), 1.206 + makeResult(uri2), 1.207 + ]; 1.208 + observer.search = s0; 1.209 + observer.runCount = c1 + c1; 1.210 + yield task_setCountRank(uri1, c1, c1, s2); 1.211 + yield task_setCountRank(uri2, c2, c1, s2); 1.212 + }, 1.213 + function() { 1.214 + print("Test 3 diff count, same rank, same term; no search"); 1.215 + observer.results = [ 1.216 + makeResult(uri2), 1.217 + makeResult(uri1), 1.218 + ]; 1.219 + observer.search = s0; 1.220 + observer.runCount = c1 + c1; 1.221 + yield task_setCountRank(uri1, c2, c1, s2); 1.222 + yield task_setCountRank(uri2, c1, c1, s2); 1.223 + }, 1.224 + 1.225 + // Test things with a search term (exact match one, partial other). 1.226 + function() { 1.227 + print("Test 4 same count, same rank, diff term; one exact/one partial search"); 1.228 + observer.results = [ 1.229 + makeResult(uri1), 1.230 + makeResult(uri2), 1.231 + ]; 1.232 + observer.search = s1; 1.233 + observer.runCount = c1 + c1; 1.234 + yield task_setCountRank(uri1, c1, c1, s1); 1.235 + yield task_setCountRank(uri2, c1, c1, s2); 1.236 + }, 1.237 + function() { 1.238 + print("Test 5 same count, same rank, diff term; one exact/one partial search"); 1.239 + observer.results = [ 1.240 + makeResult(uri2), 1.241 + makeResult(uri1), 1.242 + ]; 1.243 + observer.search = s1; 1.244 + observer.runCount = c1 + c1; 1.245 + yield task_setCountRank(uri1, c1, c1, s2); 1.246 + yield task_setCountRank(uri2, c1, c1, s1); 1.247 + }, 1.248 + 1.249 + // Test things with a search term (exact match both). 1.250 + function() { 1.251 + print("Test 6 same count, diff rank, same term; both exact search"); 1.252 + observer.results = [ 1.253 + makeResult(uri1), 1.254 + makeResult(uri2), 1.255 + ]; 1.256 + observer.search = s1; 1.257 + observer.runCount = c1 + c2; 1.258 + yield task_setCountRank(uri1, c1, c1, s1); 1.259 + yield task_setCountRank(uri2, c1, c2, s1); 1.260 + }, 1.261 + function() { 1.262 + print("Test 7 same count, diff rank, same term; both exact search"); 1.263 + observer.results = [ 1.264 + makeResult(uri2), 1.265 + makeResult(uri1), 1.266 + ]; 1.267 + observer.search = s1; 1.268 + observer.runCount = c1 + c2; 1.269 + yield task_setCountRank(uri1, c1, c2, s1); 1.270 + yield task_setCountRank(uri2, c1, c1, s1); 1.271 + }, 1.272 + 1.273 + // Test things with a search term (partial match both). 1.274 + function() { 1.275 + print("Test 8 same count, diff rank, same term; both partial search"); 1.276 + observer.results = [ 1.277 + makeResult(uri1), 1.278 + makeResult(uri2), 1.279 + ]; 1.280 + observer.search = s1; 1.281 + observer.runCount = c1 + c2; 1.282 + yield task_setCountRank(uri1, c1, c1, s2); 1.283 + yield task_setCountRank(uri2, c1, c2, s2); 1.284 + }, 1.285 + function() { 1.286 + print("Test 9 same count, diff rank, same term; both partial search"); 1.287 + observer.results = [ 1.288 + makeResult(uri2), 1.289 + makeResult(uri1), 1.290 + ]; 1.291 + observer.search = s1; 1.292 + observer.runCount = c1 + c2; 1.293 + yield task_setCountRank(uri1, c1, c2, s2); 1.294 + yield task_setCountRank(uri2, c1, c1, s2); 1.295 + }, 1.296 + function() { 1.297 + print("Test 10 same count, same rank, same term, decay first; exact match"); 1.298 + observer.results = [ 1.299 + makeResult(uri2), 1.300 + makeResult(uri1), 1.301 + ]; 1.302 + observer.search = s1; 1.303 + observer.runCount = c1 + c1; 1.304 + yield task_setCountRank(uri1, c1, c1, s1); 1.305 + doAdaptiveDecay(); 1.306 + yield task_setCountRank(uri2, c1, c1, s1); 1.307 + }, 1.308 + function() { 1.309 + print("Test 11 same count, same rank, same term, decay second; exact match"); 1.310 + observer.results = [ 1.311 + makeResult(uri1), 1.312 + makeResult(uri2), 1.313 + ]; 1.314 + observer.search = s1; 1.315 + observer.runCount = c1 + c1; 1.316 + yield task_setCountRank(uri2, c1, c1, s1); 1.317 + doAdaptiveDecay(); 1.318 + yield task_setCountRank(uri1, c1, c1, s1); 1.319 + }, 1.320 + // Test that bookmarks or tags are hidden if the preferences are set right. 1.321 + function() { 1.322 + print("Test 12 same count, diff rank, same term; no search; history only"); 1.323 + Services.prefs.setIntPref("browser.urlbar.matchBehavior", 1.324 + Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY); 1.325 + observer.results = [ 1.326 + makeResult(uri1), 1.327 + makeResult(uri2), 1.328 + ]; 1.329 + observer.search = s0; 1.330 + observer.runCount = c1 + c2; 1.331 + yield task_setCountRank(uri1, c1, c1, s2, "bookmark"); 1.332 + yield task_setCountRank(uri2, c1, c2, s2); 1.333 + }, 1.334 + function() { 1.335 + print("Test 13 same count, diff rank, same term; no search; history only with tag"); 1.336 + Services.prefs.setIntPref("browser.urlbar.matchBehavior", 1.337 + Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY); 1.338 + observer.results = [ 1.339 + makeResult(uri1), 1.340 + makeResult(uri2), 1.341 + ]; 1.342 + observer.search = s0; 1.343 + observer.runCount = c1 + c2; 1.344 + yield task_setCountRank(uri1, c1, c1, s2, "tag"); 1.345 + yield task_setCountRank(uri2, c1, c2, s2); 1.346 + }, 1.347 +]; 1.348 + 1.349 +/** 1.350 + * This deferred object contains a promise that is resolved when the 1.351 + * ensure_results function has finished its execution. 1.352 + */ 1.353 +let deferEnsureResults; 1.354 + 1.355 +/** 1.356 + * Test adaptive autocomplete. 1.357 + */ 1.358 +function run_test() 1.359 +{ 1.360 + run_next_test(); 1.361 +} 1.362 + 1.363 +add_task(function test_adaptive() 1.364 +{ 1.365 + for (let [, test] in Iterator(tests)) { 1.366 + // Cleanup. 1.367 + PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId); 1.368 + PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.tagsFolderId); 1.369 + observer.runCount = -1; 1.370 + 1.371 + yield promiseClearHistory(); 1.372 + 1.373 + deferEnsureResults = Promise.defer(); 1.374 + yield test(); 1.375 + yield deferEnsureResults.promise; 1.376 + } 1.377 + 1.378 + Services.obs.removeObserver(observer, PlacesUtils.TOPIC_FEEDBACK_UPDATED); 1.379 +});