mockclock_test.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. // Copyright 2007 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.testing.MockClockTest');
  15. goog.setTestOnly('goog.testing.MockClockTest');
  16. goog.require('goog.Promise');
  17. goog.require('goog.Timer');
  18. goog.require('goog.events');
  19. goog.require('goog.functions');
  20. goog.require('goog.testing.MockClock');
  21. goog.require('goog.testing.PropertyReplacer');
  22. goog.require('goog.testing.jsunit');
  23. goog.require('goog.testing.recordFunction');
  24. var stubs = new goog.testing.PropertyReplacer();
  25. function tearDown() {
  26. stubs.reset();
  27. }
  28. function testMockClockWasInstalled() {
  29. var clock = new goog.testing.MockClock();
  30. var originalTimeout = window.setTimeout;
  31. clock.install();
  32. assertNotEquals(window.setTimeout, originalTimeout);
  33. setTimeout(function() {}, 100);
  34. assertEquals(1, clock.getTimeoutsMade());
  35. setInterval(function() {}, 200);
  36. assertEquals(2, clock.getTimeoutsMade());
  37. clock.uninstall();
  38. assertEquals(window.setTimeout, originalTimeout);
  39. assertNull(clock.replacer_);
  40. }
  41. function testSetTimeoutAndTick() {
  42. var clock = new goog.testing.MockClock(true);
  43. var m5 = false, m10 = false, m15 = false, m20 = false;
  44. setTimeout(function() { m5 = true; }, 5);
  45. setTimeout(function() { m10 = true; }, 10);
  46. setTimeout(function() { m15 = true; }, 15);
  47. setTimeout(function() { m20 = true; }, 20);
  48. assertEquals(4, clock.getTimeoutsMade());
  49. assertEquals(4, clock.tick(4));
  50. assertEquals(4, clock.getCurrentTime());
  51. assertEquals(0, clock.getCallbacksTriggered());
  52. assertFalse(m5);
  53. assertFalse(m10);
  54. assertFalse(m15);
  55. assertFalse(m20);
  56. assertEquals(5, clock.tick(1));
  57. assertEquals(5, clock.getCurrentTime());
  58. assertEquals(1, clock.getCallbacksTriggered());
  59. assertTrue('m5 should now be true', m5);
  60. assertFalse(m10);
  61. assertFalse(m15);
  62. assertFalse(m20);
  63. assertEquals(10, clock.tick(5));
  64. assertEquals(10, clock.getCurrentTime());
  65. assertEquals(2, clock.getCallbacksTriggered());
  66. assertTrue('m5 should be true', m5);
  67. assertTrue('m10 should now be true', m10);
  68. assertFalse(m15);
  69. assertFalse(m20);
  70. assertEquals(15, clock.tick(5));
  71. assertEquals(15, clock.getCurrentTime());
  72. assertEquals(3, clock.getCallbacksTriggered());
  73. assertTrue('m5 should be true', m5);
  74. assertTrue('m10 should be true', m10);
  75. assertTrue('m15 should now be true', m15);
  76. assertFalse(m20);
  77. assertEquals(20, clock.tick(5));
  78. assertEquals(20, clock.getCurrentTime());
  79. assertEquals(4, clock.getCallbacksTriggered());
  80. assertTrue('m5 should be true', m5);
  81. assertTrue('m10 should be true', m10);
  82. assertTrue('m15 should be true', m15);
  83. assertTrue('m20 should now be true', m20);
  84. clock.uninstall();
  85. }
  86. function testSetImmediateAndTick() {
  87. var clock = new goog.testing.MockClock(true);
  88. var tick0 = false;
  89. var tick1 = false;
  90. setImmediate(function() { tick0 = true; });
  91. setImmediate(function() { tick1 = true; });
  92. assertEquals(2, clock.getTimeoutsMade());
  93. assertEquals(0, clock.getCallbacksTriggered());
  94. clock.tick(0);
  95. assertEquals(2, clock.getCallbacksTriggered());
  96. assertTrue(tick0);
  97. assertTrue(tick1);
  98. clock.uninstall();
  99. }
  100. function testSetInterval() {
  101. var clock = new goog.testing.MockClock(true);
  102. var times = 0;
  103. setInterval(function() { times++; }, 100);
  104. clock.tick(500);
  105. assertEquals(5, clock.getCallbacksTriggered());
  106. assertEquals(5, times);
  107. clock.tick(100);
  108. assertEquals(6, clock.getCallbacksTriggered());
  109. assertEquals(6, times);
  110. clock.tick(100);
  111. assertEquals(7, clock.getCallbacksTriggered());
  112. assertEquals(7, times);
  113. clock.tick(50);
  114. assertEquals(7, clock.getCallbacksTriggered());
  115. assertEquals(7, times);
  116. clock.tick(50);
  117. assertEquals(8, clock.getCallbacksTriggered());
  118. assertEquals(8, times);
  119. clock.uninstall();
  120. }
  121. function testRequestAnimationFrame() {
  122. goog.global.requestAnimationFrame = function() {};
  123. var clock = new goog.testing.MockClock(true);
  124. var times = [];
  125. var recFunc = goog.testing.recordFunction(function(now) { times.push(now); });
  126. goog.global.requestAnimationFrame(recFunc);
  127. clock.tick(50);
  128. assertEquals(1, clock.getCallbacksTriggered());
  129. assertEquals(1, recFunc.getCallCount());
  130. assertEquals(20, times[0]);
  131. goog.global.requestAnimationFrame(recFunc);
  132. clock.tick(100);
  133. assertEquals(2, clock.getCallbacksTriggered());
  134. assertEquals(2, recFunc.getCallCount());
  135. assertEquals(70, times[1]);
  136. clock.uninstall();
  137. }
  138. function testClearTimeout() {
  139. var clock = new goog.testing.MockClock(true);
  140. var ran = false;
  141. var c = setTimeout(function() { ran = true; }, 100);
  142. clock.tick(50);
  143. assertFalse(ran);
  144. clearTimeout(c);
  145. clock.tick(100);
  146. assertFalse(ran);
  147. clock.uninstall();
  148. }
  149. function testClearInterval() {
  150. var clock = new goog.testing.MockClock(true);
  151. var times = 0;
  152. var c = setInterval(function() { times++; }, 100);
  153. clock.tick(500);
  154. assertEquals(5, times);
  155. clock.tick(100);
  156. assertEquals(6, times);
  157. clock.tick(100);
  158. clearInterval(c);
  159. assertEquals(7, times);
  160. clock.tick(50);
  161. assertEquals(7, times);
  162. clock.tick(50);
  163. assertEquals(7, times);
  164. clock.uninstall();
  165. }
  166. function testClearInterval2() {
  167. // Tests that we can clear the interval from inside the function
  168. var clock = new goog.testing.MockClock(true);
  169. var times = 0;
  170. var c = setInterval(function() {
  171. times++;
  172. if (times == 6) {
  173. clearInterval(c);
  174. }
  175. }, 100);
  176. clock.tick(500);
  177. assertEquals(5, times);
  178. clock.tick(100);
  179. assertEquals(6, times);
  180. clock.tick(100);
  181. assertEquals(6, times);
  182. clock.tick(50);
  183. assertEquals(6, times);
  184. clock.tick(50);
  185. assertEquals(6, times);
  186. clock.uninstall();
  187. }
  188. function testCancelRequestAnimationFrame() {
  189. goog.global.requestAnimationFrame = function() {};
  190. goog.global.cancelRequestAnimationFrame = function() {};
  191. var clock = new goog.testing.MockClock(true);
  192. var ran = false;
  193. var c = goog.global.requestAnimationFrame(function() { ran = true; });
  194. clock.tick(10);
  195. assertFalse(ran);
  196. goog.global.cancelRequestAnimationFrame(c);
  197. clock.tick(20);
  198. assertFalse(ran);
  199. clock.uninstall();
  200. }
  201. function testMockGoogNow() {
  202. assertNotEquals(0, goog.now());
  203. var clock = new goog.testing.MockClock(true);
  204. assertEquals(0, goog.now());
  205. clock.tick(50);
  206. assertEquals(50, goog.now());
  207. clock.uninstall();
  208. assertNotEquals(50, goog.now());
  209. }
  210. function testTimeoutDelay() {
  211. var clock = new goog.testing.MockClock(true);
  212. var m5 = false, m10 = false, m20 = false;
  213. setTimeout(function() { m5 = true; }, 5);
  214. setTimeout(function() { m10 = true; }, 10);
  215. setTimeout(function() { m20 = true; }, 20);
  216. // Fire 3ms early, so m5 fires at t=2
  217. clock.setTimeoutDelay(-3);
  218. clock.tick(1);
  219. assertFalse(m5);
  220. assertFalse(m10);
  221. clock.tick(1);
  222. assertTrue(m5);
  223. assertFalse(m10);
  224. // Fire 3ms late, so m10 fires at t=13
  225. clock.setTimeoutDelay(3);
  226. assertEquals(12, clock.tick(10));
  227. assertEquals(12, clock.getCurrentTime());
  228. assertFalse(m10);
  229. clock.tick(1);
  230. assertTrue(m10);
  231. assertFalse(m20);
  232. // Fire 10ms early, so m20 fires now, since it's after t=10
  233. clock.setTimeoutDelay(-10);
  234. assertFalse(m20);
  235. assertEquals(14, clock.tick(1));
  236. assertEquals(14, clock.getCurrentTime());
  237. assertTrue(m20);
  238. clock.uninstall();
  239. }
  240. function testTimerCallbackCanCreateIntermediateTimer() {
  241. var clock = new goog.testing.MockClock(true);
  242. var sequence = [];
  243. // Create 3 timers: 1, 2, and 3. Timer 1 should fire at T=1, timer 2 at
  244. // T=2, and timer 3 at T=3. The catch: Timer 2 is created by the
  245. // callback within timer 0.
  246. // Testing method: Create a simple string sequencing each timer and at
  247. // what time it fired.
  248. setTimeout(function() {
  249. sequence.push('timer1 at T=' + goog.now());
  250. setTimeout(function() { sequence.push('timer2 at T=' + goog.now()); }, 1);
  251. }, 1);
  252. setTimeout(function() { sequence.push('timer3 at T=' + goog.now()); }, 3);
  253. clock.tick(4);
  254. assertEquals(
  255. 'Each timer should fire in sequence at the correct time.',
  256. 'timer1 at T=1, timer2 at T=2, timer3 at T=3', sequence.join(', '));
  257. clock.uninstall();
  258. }
  259. function testCorrectArgumentsPassedToCallback() {
  260. var clock = new goog.testing.MockClock(true);
  261. var timeoutId;
  262. var timeoutExecuted = false;
  263. timeoutId = setTimeout(function(arg) {
  264. assertEquals('"this" must be goog.global', goog.global, this);
  265. assertEquals('The timeout ID must be the first parameter', timeoutId, arg);
  266. assertEquals('Exactly one argument must be passed', 1, arguments.length);
  267. timeoutExecuted = true;
  268. }, 1);
  269. clock.tick(4);
  270. assertTrue('The timeout was not executed', timeoutExecuted);
  271. clock.uninstall();
  272. }
  273. function testTickZero() {
  274. var clock = new goog.testing.MockClock(true);
  275. var calls = 0;
  276. setTimeout(function() {
  277. assertEquals('I need to be first', 0, calls);
  278. calls++;
  279. }, 0);
  280. setTimeout(function() {
  281. assertEquals('I need to be second', 1, calls);
  282. calls++;
  283. }, 0);
  284. clock.tick(0);
  285. assertEquals(2, calls);
  286. setTimeout(function() {
  287. assertEquals('I need to be third', 2, calls);
  288. calls++;
  289. }, 0);
  290. clock.tick(0);
  291. assertEquals(3, calls);
  292. assertEquals('Time should still be zero', 0, goog.now());
  293. clock.uninstall();
  294. }
  295. function testReset() {
  296. var clock = new goog.testing.MockClock(true);
  297. var id = setTimeout(function() {
  298. fail('Timeouts should be cleared after a reset');
  299. }, 0);
  300. clock.reset();
  301. clock.tick(999999);
  302. var calls = 0;
  303. setTimeout(function() { calls++; }, 10);
  304. clearTimeout(id);
  305. clock.tick(100);
  306. assertEquals(
  307. 'New timeout should still run after clearing from before reset', 1,
  308. calls);
  309. clock.uninstall();
  310. }
  311. function testNewClockWithOldTimeoutId() {
  312. var clock = new goog.testing.MockClock(true);
  313. var id = setTimeout(function() {
  314. fail('Timeouts should be cleared after uninstall');
  315. }, 0);
  316. clock.uninstall();
  317. clock = new goog.testing.MockClock(true);
  318. var calls = 0;
  319. setTimeout(function() { calls++; }, 10);
  320. clearTimeout(id);
  321. clock.tick(100);
  322. assertEquals(
  323. 'Timeout should still run after cancelling from old clock', 1, calls);
  324. clock.uninstall();
  325. }
  326. function testQueueInsertionHelper() {
  327. var queue = [];
  328. function queueToString() {
  329. var buffer = [];
  330. for (var i = 0; i < queue.length; i++) {
  331. buffer.push(queue[i].runAtMillis);
  332. }
  333. return buffer.join(',');
  334. }
  335. goog.testing.MockClock.insert_({runAtMillis: 2}, queue);
  336. assertEquals('Only item', '2', queueToString());
  337. goog.testing.MockClock.insert_({runAtMillis: 4}, queue);
  338. assertEquals('Biggest item', '4,2', queueToString());
  339. goog.testing.MockClock.insert_({runAtMillis: 5}, queue);
  340. assertEquals('An even bigger item', '5,4,2', queueToString());
  341. goog.testing.MockClock.insert_({runAtMillis: 1}, queue);
  342. assertEquals('Smallest item', '5,4,2,1', queueToString());
  343. goog.testing.MockClock.insert_({runAtMillis: 1, dup: true}, queue);
  344. assertEquals('Duplicate smallest item', '5,4,2,1,1', queueToString());
  345. assertTrue('Duplicate item comes at a smaller index', queue[3].dup);
  346. goog.testing.MockClock.insert_({runAtMillis: 3}, queue);
  347. goog.testing.MockClock.insert_({runAtMillis: 3, dup: true}, queue);
  348. assertEquals('Duplicate a middle item', '5,4,3,3,2,1,1', queueToString());
  349. assertTrue('Duplicate item comes at a smaller index', queue[2].dup);
  350. }
  351. function testIsTimeoutSet() {
  352. var clock = new goog.testing.MockClock(true);
  353. var timeoutKey = setTimeout(function() {}, 1);
  354. assertTrue(
  355. 'Timeout ' + timeoutKey + ' should be set',
  356. clock.isTimeoutSet(timeoutKey));
  357. var nextTimeoutKey = timeoutKey + 1;
  358. assertFalse(
  359. 'Timeout ' + nextTimeoutKey + ' should not be set',
  360. clock.isTimeoutSet(nextTimeoutKey));
  361. clearTimeout(timeoutKey);
  362. assertFalse(
  363. 'Timeout ' + timeoutKey + ' should no longer be set',
  364. clock.isTimeoutSet(timeoutKey));
  365. var newTimeoutKey = setTimeout(function() {}, 1);
  366. clock.tick(5);
  367. assertFalse(
  368. 'Timeout ' + timeoutKey + ' should not be set',
  369. clock.isTimeoutSet(timeoutKey));
  370. assertTrue(
  371. 'Timeout ' + newTimeoutKey + ' should be set',
  372. clock.isTimeoutSet(newTimeoutKey));
  373. clock.uninstall();
  374. }
  375. function testBalksOnTimeoutsGreaterThanMaxInt() {
  376. // Browsers have trouble with timeout greater than max int, so we
  377. // want Mock Clock to fail if this happens.
  378. var clock = new goog.testing.MockClock(true);
  379. // Functions on window don't seem to be able to throw exceptions in
  380. // IE6. Explicitly reading the property makes it work.
  381. var setTimeout = window.setTimeout;
  382. assertThrows('Timeouts > MAX_INT should fail', function() {
  383. setTimeout(goog.nullFunction, 2147483648);
  384. });
  385. assertThrows('Timeouts much greater than MAX_INT should fail', function() {
  386. setTimeout(goog.nullFunction, 2147483648 * 10);
  387. });
  388. clock.uninstall();
  389. }
  390. function testCorrectSetTimeoutIsRestored() {
  391. var safe = goog.functions.error('should not have been called');
  392. stubs.set(window, 'setTimeout', safe);
  393. var clock = new goog.testing.MockClock(true);
  394. assertNotEquals('setTimeout is replaced', safe, window.setTimeout);
  395. clock.uninstall();
  396. // NOTE: If this assertion proves to be flaky in IE, the string value of
  397. // the two functions have to be compared as described in
  398. // goog.testing.TestCase#finalize.
  399. assertEquals('setTimeout is restored', safe, window.setTimeout);
  400. }
  401. function testMozRequestAnimationFrame() {
  402. // Setting this function will indirectly tell the mock clock to mock it out.
  403. stubs.set(window, 'mozRequestAnimationFrame', goog.nullFunction);
  404. var clock = new goog.testing.MockClock(true);
  405. var mozBeforePaint = goog.testing.recordFunction();
  406. goog.events.listen(window, 'MozBeforePaint', mozBeforePaint);
  407. window.mozRequestAnimationFrame(null);
  408. assertEquals(0, mozBeforePaint.getCallCount());
  409. clock.tick(goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT);
  410. assertEquals(1, mozBeforePaint.getCallCount());
  411. clock.dispose();
  412. }
  413. function testClearBeforeSet() {
  414. var clock = new goog.testing.MockClock(true);
  415. var expectedId = goog.testing.MockClock.nextId;
  416. window.clearTimeout(expectedId);
  417. var fn = goog.testing.recordFunction();
  418. var actualId = window.setTimeout(fn, 0);
  419. assertEquals(
  420. 'In order for this test to work, we have to guess the ids in advance',
  421. expectedId, actualId);
  422. clock.tick(1);
  423. assertEquals(1, fn.getCallCount());
  424. clock.dispose();
  425. }
  426. function testNonFunctionArguments() {
  427. var clock = new goog.testing.MockClock(true);
  428. // Unlike normal setTimeout and friends, we only accept functions (not
  429. // strings, not undefined, etc). Make sure that if we get a non-function, we
  430. // fail early rather than on the next .tick() operation.
  431. assertThrows('setTimeout with a non-function value should fail', function() {
  432. window.setTimeout(undefined, 0);
  433. });
  434. clock.tick(1);
  435. assertThrows('setTimeout with a string should fail', function() {
  436. window.setTimeout('throw new Error("setTimeout string eval!");', 0);
  437. });
  438. clock.tick(1);
  439. clock.dispose();
  440. }
  441. function testUnspecifiedTimeout() {
  442. var clock = new goog.testing.MockClock(true);
  443. var m0a = false, m0b = false, m10 = false;
  444. setTimeout(function() { m0a = true; });
  445. setTimeout(function() { m10 = true; }, 10);
  446. assertEquals(2, clock.getTimeoutsMade());
  447. assertFalse(m0a);
  448. assertFalse(m0b);
  449. assertFalse(m10);
  450. assertEquals(0, clock.tick(0));
  451. assertEquals(0, clock.getCurrentTime());
  452. assertTrue(m0a);
  453. assertFalse(m0b);
  454. assertFalse(m10);
  455. setTimeout(function() { m0b = true; });
  456. assertEquals(3, clock.getTimeoutsMade());
  457. assertEquals(0, clock.tick(0));
  458. assertEquals(0, clock.getCurrentTime());
  459. assertTrue(m0a);
  460. assertTrue(m0b);
  461. assertFalse(m10);
  462. assertEquals(10, clock.tick(10));
  463. assertEquals(10, clock.getCurrentTime());
  464. assertTrue(m0a);
  465. assertTrue(m0b);
  466. assertTrue(m10);
  467. clock.uninstall();
  468. }
  469. function testUnspecifiedInterval() {
  470. var clock = new goog.testing.MockClock(true);
  471. var times = 0;
  472. var handle = setInterval(function() {
  473. if (++times >= 5) {
  474. clearInterval(handle);
  475. }
  476. });
  477. clock.tick(0);
  478. assertEquals(5, times);
  479. clock.uninstall();
  480. }
  481. function testTickPromise() {
  482. var clock = new goog.testing.MockClock(true);
  483. var p = goog.Promise.resolve('foo');
  484. assertEquals('foo', clock.tickPromise(p));
  485. var rejected = goog.Promise.reject(new Error('failed'));
  486. var e = assertThrows(function() { clock.tickPromise(rejected); });
  487. assertEquals('failed', e.message);
  488. var delayed = goog.Timer.promise(500, 'delayed');
  489. e = assertThrows(function() { clock.tickPromise(delayed); });
  490. assertEquals(
  491. 'Promise was expected to be resolved after mock clock tick.', e.message);
  492. assertEquals('delayed', clock.tickPromise(delayed, 500));
  493. clock.dispose();
  494. }