cachingmatcher_test.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // Copyright 2013 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.ui.ac.CachingMatcherTest');
  15. goog.setTestOnly('goog.ui.ac.CachingMatcherTest');
  16. goog.require('goog.testing.MockControl');
  17. goog.require('goog.testing.jsunit');
  18. goog.require('goog.testing.mockmatchers');
  19. goog.require('goog.ui.ac.CachingMatcher');
  20. ignoreArgument = goog.testing.mockmatchers.ignoreArgument;
  21. /**
  22. * Fake version of Throttle which only fires when we call permitOne().
  23. * @constructor
  24. * @suppress {missingProvide}
  25. */
  26. goog.async.Throttle = function(fn, time, self) {
  27. this.fn = fn;
  28. this.time = time;
  29. this.self = self;
  30. this.numFires = 0;
  31. };
  32. /** @suppress {missingProvide} */
  33. goog.async.Throttle.prototype.fire = function() {
  34. this.numFires++;
  35. };
  36. /** @suppress {missingProvide} */
  37. goog.async.Throttle.prototype.permitOne = function() {
  38. if (this.numFires == 0) {
  39. return;
  40. }
  41. this.fn.call(this.self);
  42. this.numFires = 0;
  43. };
  44. // Actual tests.
  45. var mockControl;
  46. var mockMatcher;
  47. var mockHandler;
  48. var matcher;
  49. function setUp() {
  50. mockControl = new goog.testing.MockControl();
  51. mockMatcher = {
  52. requestMatchingRows: mockControl.createFunctionMock('requestMatchingRows')
  53. };
  54. mockHandler = mockControl.createFunctionMock('matchHandler');
  55. matcher = new goog.ui.ac.CachingMatcher(mockMatcher);
  56. }
  57. function tearDown() {
  58. mockControl.$tearDown();
  59. }
  60. function testLocalThenRemoteMatch() {
  61. // We immediately get the local match.
  62. mockHandler('foo', []);
  63. mockControl.$replayAll();
  64. matcher.requestMatchingRows('foo', 12, mockHandler);
  65. mockControl.$verifyAll();
  66. mockControl.$resetAll();
  67. // Now we run the remote match.
  68. mockHandler('foo', ['foo1', 'foo2'], ignoreArgument);
  69. mockMatcher.requestMatchingRows('foo', 100, ignoreArgument)
  70. .$does(function(token, maxResults, matchHandler) {
  71. matchHandler('foo', ['foo1', 'foo2', 'bar3']);
  72. });
  73. mockControl.$replayAll();
  74. matcher.throttledTriggerBaseMatch_.permitOne();
  75. mockControl.$verifyAll();
  76. mockControl.$resetAll();
  77. }
  78. function testCacheSize() {
  79. matcher.setMaxCacheSize(4);
  80. // First we populate, but not overflow the cache.
  81. mockHandler('foo', []);
  82. mockHandler('foo', ['foo111', 'foo222'], ignoreArgument);
  83. mockMatcher.requestMatchingRows('foo', 100, ignoreArgument)
  84. .$does(function(token, maxResults, matchHandler) {
  85. matchHandler('foo', ['foo111', 'foo222', 'bar333']);
  86. });
  87. mockControl.$replayAll();
  88. matcher.requestMatchingRows('foo', 12, mockHandler);
  89. matcher.throttledTriggerBaseMatch_.permitOne();
  90. mockControl.$verifyAll();
  91. mockControl.$resetAll();
  92. // Now we verify the cache is populated.
  93. mockHandler('foo1', ['foo111']);
  94. mockControl.$replayAll();
  95. matcher.requestMatchingRows('foo1', 12, mockHandler);
  96. mockControl.$verifyAll();
  97. mockControl.$resetAll();
  98. // Now we overflow the cache. Check that the remote results show the first
  99. // time we get them back, even though they overflow the cache.
  100. mockHandler('foo11', ['foo111']);
  101. mockHandler(
  102. 'foo11', ['foo111', 'foo112', 'foo113', 'foo114'], ignoreArgument);
  103. mockMatcher.requestMatchingRows('foo11', 100, ignoreArgument)
  104. .$does(function(token, maxResults, matchHandler) {
  105. matchHandler('foo11', ['foo111', 'foo112', 'foo113', 'foo114']);
  106. });
  107. mockControl.$replayAll();
  108. matcher.requestMatchingRows('foo11', 12, mockHandler);
  109. matcher.throttledTriggerBaseMatch_.permitOne();
  110. mockControl.$verifyAll();
  111. mockControl.$resetAll();
  112. // Now check that the cache is empty.
  113. mockHandler('foo11', []);
  114. mockControl.$replayAll();
  115. matcher.requestMatchingRows('foo11', 12, mockHandler);
  116. mockControl.$verifyAll();
  117. mockControl.$resetAll();
  118. }
  119. function testClearCache() {
  120. // First we populate the cache.
  121. mockHandler('foo', []);
  122. mockHandler('foo', ['foo111', 'foo222'], ignoreArgument);
  123. mockMatcher.requestMatchingRows('foo', 100, ignoreArgument)
  124. .$does(function(token, maxResults, matchHandler) {
  125. matchHandler('foo', ['foo111', 'foo222', 'bar333']);
  126. });
  127. mockControl.$replayAll();
  128. matcher.requestMatchingRows('foo', 12, mockHandler);
  129. matcher.throttledTriggerBaseMatch_.permitOne();
  130. mockControl.$verifyAll();
  131. mockControl.$resetAll();
  132. // Now we verify the cache is populated.
  133. mockHandler('foo1', ['foo111']);
  134. mockControl.$replayAll();
  135. matcher.requestMatchingRows('foo1', 12, mockHandler);
  136. mockControl.$verifyAll();
  137. mockControl.$resetAll();
  138. // Now we clear the cache.
  139. matcher.clearCache();
  140. // Now check that the cache is empty.
  141. mockHandler('foo11', []);
  142. mockControl.$replayAll();
  143. matcher.requestMatchingRows('foo11', 12, mockHandler);
  144. mockControl.$verifyAll();
  145. mockControl.$resetAll();
  146. }
  147. function testSimilarMatchingDoesntReorderResults() {
  148. // Populate the cache. We get two prefix matches.
  149. mockHandler('ba', []);
  150. mockHandler('ba', ['bar', 'baz', 'bam'], ignoreArgument);
  151. mockMatcher.requestMatchingRows('ba', 100, ignoreArgument)
  152. .$does(function(token, maxResults, matchHandler) {
  153. matchHandler('ba', ['bar', 'baz', 'bam']);
  154. });
  155. mockControl.$replayAll();
  156. matcher.requestMatchingRows('ba', 12, mockHandler);
  157. matcher.throttledTriggerBaseMatch_.permitOne();
  158. mockControl.$verifyAll();
  159. mockControl.$resetAll();
  160. // The user types another character. The local match gives us two similar
  161. // matches, but no prefix matches. The remote match returns a prefix match,
  162. // which would normally be ranked above the similar matches, but gets ranked
  163. // below the similar matches because the user hasn't typed any more
  164. // characters.
  165. mockHandler('bad', ['bar', 'baz', 'bam']);
  166. mockHandler(
  167. 'bad', ['bar', 'baz', 'bam', 'bad', 'badder', 'baddest'], ignoreArgument);
  168. mockMatcher.requestMatchingRows('bad', 100, ignoreArgument)
  169. .$does(function(token, maxResults, matchHandler) {
  170. matchHandler('bad', ['bad', 'badder', 'baddest']);
  171. });
  172. mockControl.$replayAll();
  173. matcher.requestMatchingRows('bad', 12, mockHandler);
  174. matcher.throttledTriggerBaseMatch_.permitOne();
  175. mockControl.$verifyAll();
  176. mockControl.$resetAll();
  177. // The user types yet another character, which allows the prefix matches to
  178. // jump to the top of the list of suggestions.
  179. mockHandler('badd', ['badder', 'baddest']);
  180. mockControl.$replayAll();
  181. matcher.requestMatchingRows('badd', 12, mockHandler);
  182. mockControl.$verifyAll();
  183. mockControl.$resetAll();
  184. }
  185. function testSetThrottleTime() {
  186. assertEquals(150, matcher.throttledTriggerBaseMatch_.time);
  187. matcher.setThrottleTime(234);
  188. assertEquals(234, matcher.throttledTriggerBaseMatch_.time);
  189. }
  190. function testSetBaseMatcherMaxMatches() {
  191. mockHandler('foo', []); // Local match
  192. mockMatcher.requestMatchingRows('foo', 789, ignoreArgument);
  193. mockControl.$replayAll();
  194. matcher.setBaseMatcherMaxMatches();
  195. matcher.requestMatchingRows('foo', 12, mockHandler);
  196. }
  197. function testSetLocalMatcher() {
  198. // Use a local matcher which just sorts all the rows alphabetically.
  199. function sillyMatcher(token, maxMatches, rows) {
  200. rows = rows.concat([]);
  201. rows.sort();
  202. return rows;
  203. }
  204. mockHandler('foo', []);
  205. mockHandler('foo', ['a', 'b', 'c'], ignoreArgument);
  206. mockMatcher.requestMatchingRows('foo', 100, ignoreArgument)
  207. .$does(function(token, maxResults, matchHandler) {
  208. matchHandler('foo', ['b', 'a', 'c']);
  209. });
  210. mockControl.$replayAll();
  211. matcher.setLocalMatcher(sillyMatcher);
  212. matcher.requestMatchingRows('foo', 12, mockHandler);
  213. matcher.throttledTriggerBaseMatch_.permitOne();
  214. mockControl.$verifyAll();
  215. mockControl.$resetAll();
  216. }