1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * Test fixture for utility.js.
8 * @extends {testing.Test}
10 function GoogleNowUtilityUnitTest () {
11 testing.Test.call(this);
14 GoogleNowUtilityUnitTest.prototype = {
15 __proto__: testing.Test.prototype,
19 'common_test_util.js',
20 'utility_test_util.js',
25 TEST_F('GoogleNowUtilityUnitTest', 'SendErrorReport1', function() {
26 // Test sending report for an error with a message that can be sent to server.
28 // Setup and expectations.
29 var testStack = 'Error: TEST ERROR MESSAGE\n ' +
30 'at buildErrorWithMessageForServer ' +
31 '(chrome-extension://ext_id/utility.js:29:15)\n' +
32 ' at <anonymous>:2:16\n ' +
33 'at Object.InjectedScript._evaluateOn (<anonymous>:580:39)\n ' +
34 'at Object.InjectedScript._evaluateAndWrap (<anonymous>:539:52)\n ' +
35 'at Object.InjectedScript.evaluate (<anonymous>:458:21)';
38 canSendMessageToServer: true,
40 name: 'TEST ERROR NAME',
41 message: 'TEST ERROR MESSAGE'
44 var testIdentityToken = 'test identity token';
46 this.makeAndRegisterMockGlobals(['buildServerRequest']);
47 this.makeMockLocalFunctions(['sendRequest', 'setRequestHeader']);
48 this.makeAndRegisterMockApis(['chrome.identity.getAuthToken']);
51 send: this.mockLocalFunctions.functions().sendRequest,
52 setRequestHeader: this.mockLocalFunctions.functions().setRequestHeader
55 var expectedRequestObject = {
56 message: 'TEST ERROR NAME: TEST ERROR MESSAGE',
57 file: '//ext_id/utility.js',
59 trace: 'Error: TEST ERROR MESSAGE\n ' +
60 'at buildErrorWithMessageForServer (chrome-extension://ext_id/util' +
62 'at <anonymous>:2:16\n ' +
63 'at Object.InjectedScript._evaluateOn (<anonymous>:580:39)\n ' +
64 'at Object.InjectedScript._evaluateAndWrap (<anonymous>:539:52)\n' +
65 ' at Object.InjectedScript.evaluate (<anonymous>:458:21)'
68 this.mockGlobals.expects(once()).
69 buildServerRequest('POST', 'jserrors', 'application/json').
70 will(returnValue(mockRequest));
72 var chromeIdentityGetAuthTokenSavedArgs = new SaveMockArguments();
73 this.mockApis.expects(once()).
74 chrome_identity_getAuthToken(
75 chromeIdentityGetAuthTokenSavedArgs.match(
76 eqJSON({interactive: false})),
77 chromeIdentityGetAuthTokenSavedArgs.match(ANYTHING)).
79 chromeIdentityGetAuthTokenSavedArgs,
83 this.mockLocalFunctions.expects(once()).setRequestHeader(
84 'Authorization', 'Bearer test identity token');
85 this.mockLocalFunctions.expects(once()).sendRequest(
86 JSON.stringify(expectedRequestObject));
88 // Invoking the tested function.
89 sendErrorReport(testError);
92 TEST_F('GoogleNowUtilityUnitTest', 'SendErrorReport2', function() {
93 // Test sending report for an error with a message that should not be sent to
94 // server, with an error generated in an anonymous function.
96 // Setup and expectations.
97 var testStack = 'TypeError: Property \'processPendingDismissals\' of ' +
98 'object [object Object] is not a function\n ' +
99 'at chrome-extension://ext_id/background.js:444:11\n ' +
100 'at chrome-extension://ext_id/utility.js:509:7';
107 var testIdentityToken = 'test identity token';
109 this.makeAndRegisterMockGlobals(['buildServerRequest']);
110 this.makeMockLocalFunctions(['sendRequest', 'setRequestHeader']);
111 this.makeAndRegisterMockApis(['chrome.identity.getAuthToken']);
114 send: this.mockLocalFunctions.functions().sendRequest,
115 setRequestHeader: this.mockLocalFunctions.functions().setRequestHeader
118 var expectedRequestObject = {
119 message: 'TypeError',
120 file: '//ext_id/background.js',
122 trace: '(message removed)\n ' +
123 'at chrome-extension://ext_id/background.js:444:11\n ' +
124 'at chrome-extension://ext_id/utility.js:509:7'
127 this.mockGlobals.expects(once()).
128 buildServerRequest('POST', 'jserrors', 'application/json').
129 will(returnValue(mockRequest));
131 var chromeIdentityGetAuthTokenSavedArgs = new SaveMockArguments();
132 this.mockApis.expects(once()).
133 chrome_identity_getAuthToken(
134 chromeIdentityGetAuthTokenSavedArgs.match(
135 eqJSON({interactive: false})),
136 chromeIdentityGetAuthTokenSavedArgs.match(ANYTHING)).
138 chromeIdentityGetAuthTokenSavedArgs,
142 this.mockLocalFunctions.expects(once()).setRequestHeader(
143 'Authorization', 'Bearer test identity token');
144 this.mockLocalFunctions.expects(once()).sendRequest(
145 JSON.stringify(expectedRequestObject));
147 // Invoking the tested function.
148 sendErrorReport(testError);
151 TEST_F('GoogleNowUtilityUnitTest', 'WrapperCheckInWrappedCallback', function() {
152 // Test generating an error when calling wrapper.checkInWrappedCallback from a
153 // non-instrumented code.
155 // Setup and expectations.
157 testField: 'TEST VALUE'
160 this.makeAndRegisterMockGlobals([
161 'buildErrorWithMessageForServer',
165 this.mockGlobals.expects(once()).
166 buildErrorWithMessageForServer('Not in instrumented callback').
167 will(returnValue(testError));
168 this.mockGlobals.expects(once()).
169 reportError(eqJSON(testError));
171 // Invoking the tested function.
172 wrapper.checkInWrappedCallback();
175 TEST_F('GoogleNowUtilityUnitTest', 'WrapperWrapCallbackEvent', function() {
176 // Tests wrapping event handler and that the handler code counts as an
177 // instrumented callback.
181 testField: 'TEST VALUE'
184 this.makeAndRegisterMockGlobals([
185 'buildErrorWithMessageForServer',
188 var onSuspendHandlerContainer = getMockHandlerContainer('runtime.onSuspend');
190 this.makeMockLocalFunctions(['callback']);
192 // Step 1. Wrap a callback.
193 var wrappedCallback =
194 wrapper.wrapCallback(this.mockLocalFunctions.functions().callback, true);
195 Mock4JS.verifyAllMocks();
197 // Step 2. Invoke wrapped callback.
199 this.mockLocalFunctions.expects(once()).
200 callback('test string', 239).
201 will(callFunction(function() {
202 wrapper.checkInWrappedCallback(); // it should succeed
205 // Invoking tested function.
206 wrappedCallback('test string', 239);
207 Mock4JS.verifyAllMocks();
209 // Step 3. Check that after the callback we are again in non-instrumented
212 this.mockGlobals.expects(once()).
213 buildErrorWithMessageForServer('Not in instrumented callback').
214 will(returnValue(testError));
215 this.mockGlobals.expects(once()).
216 reportError(eqJSON(testError));
219 wrapper.checkInWrappedCallback();
221 // Step 4. Check that there won't be errors whe the page unloads.
222 assertEquals(1, onSuspendHandlerContainer.length);
223 onSuspendHandlerContainer[0]();
226 TEST_F('GoogleNowUtilityUnitTest', 'WrapperWrapCallbackPlugin', function() {
227 // Tests calling plugin's prologue and epilogue.
230 this.makeMockLocalFunctions([
237 // Step 1. Register plugin factory.
238 wrapper.registerWrapperPluginFactory(
239 this.mockLocalFunctions.functions().pluginFactory);
240 Mock4JS.verifyAllMocks();
242 // Step 2. Wrap a callback.
244 this.mockLocalFunctions.expects(once()).
247 prologue: this.mockLocalFunctions.functions().prologue,
248 epilogue: this.mockLocalFunctions.functions().epilogue
251 // Invoking tested function.
252 var wrappedCallback =
253 wrapper.wrapCallback(this.mockLocalFunctions.functions().callback, true);
254 Mock4JS.verifyAllMocks();
256 // Step 3. Call the wrapped callback.
258 this.mockLocalFunctions.expects(once()).prologue();
259 this.mockLocalFunctions.expects(once()).callback();
260 this.mockLocalFunctions.expects(once()).epilogue();
262 // Invoking wrapped callback.
266 TEST_F('GoogleNowUtilityUnitTest', 'WrapperWrapCallbackCatchError', function() {
267 // Tests catching and sending errors by a wrapped callback.
270 this.makeAndRegisterMockGlobals([
273 this.makeMockLocalFunctions(['callback']);
275 // Step 1. Wrap a callback.
276 var wrappedCallback =
277 wrapper.wrapCallback(this.mockLocalFunctions.functions().callback, true);
278 Mock4JS.verifyAllMocks();
280 // Step 2. Invoke wrapped callback.
282 this.mockLocalFunctions.expects(once()).
284 will(callFunction(function() {
287 this.mockGlobals.expects(once()).
289 eqToString('TypeError: Cannot set property \'x\' of undefined'));
291 // Invoking tested function.
295 TEST_F('GoogleNowUtilityUnitTest',
296 'WrapperInstrumentChromeApiFunction',
298 // Tests wrapper.instrumentChromeApiFunction().
301 this.makeMockLocalFunctions([
311 addListener: this.mockLocalFunctions.functions().apiFunction1
314 // Step 1. Instrument the listener.
315 wrapper.instrumentChromeApiFunction('testApi.addListener', 1);
316 Mock4JS.verifyAllMocks();
318 // Step 2. Invoke the instrumented API call.
320 var function1SavedArgs = new SaveMockArguments();
321 this.mockLocalFunctions.expects(once()).
323 function1SavedArgs.match(eq(239)),
324 function1SavedArgs.match(ANYTHING));
327 instrumented.testApi.addListener(
328 239, this.mockLocalFunctions.functions().callback1);
329 Mock4JS.verifyAllMocks();
331 // Step 3. Invoke the callback that was passed by the instrumented function
332 // to the original one.
334 this.mockLocalFunctions.expects(once()).callback1(237);
337 function1SavedArgs.arguments[1](237);
338 Mock4JS.verifyAllMocks();
340 // Step 4. Register plugin factory.
341 wrapper.registerWrapperPluginFactory(
342 this.mockLocalFunctions.functions().pluginFactory);
343 Mock4JS.verifyAllMocks();
345 // Step 5. Bind the API to another function.
346 chrome.testApi.addListener = this.mockLocalFunctions.functions().apiFunction2;
348 // Step 6. Invoke the API with another callback.
350 this.mockLocalFunctions.expects(once()).
353 prologue: this.mockLocalFunctions.functions().prologue,
354 epilogue: this.mockLocalFunctions.functions().epilogue
356 var function2SavedArgs = new SaveMockArguments();
357 this.mockLocalFunctions.expects(once()).
359 function2SavedArgs.match(eq(239)),
360 function2SavedArgs.match(ANYTHING));
363 instrumented.testApi.addListener(
364 239, this.mockLocalFunctions.functions().callback2);
365 Mock4JS.verifyAllMocks();
367 // Step 7. Invoke the callback that was passed by the instrumented function
368 // to the original one.
370 this.mockLocalFunctions.expects(once()).prologue();
371 this.mockLocalFunctions.expects(once()).callback2(237);
372 this.mockLocalFunctions.expects(once()).epilogue();
375 function2SavedArgs.arguments[1](237);
378 TEST_F('GoogleNowUtilityUnitTest', 'WrapperOnSuspendListenerFail', function() {
379 // Tests that upon unloading event page, we get an error if there are pending
380 // required callbacks.
384 testField: 'TEST VALUE'
386 this.makeAndRegisterMockGlobals([
387 'buildErrorWithMessageForServer',
390 this.makeMockLocalFunctions(['listener', 'callback']);
391 var onSuspendHandlerContainer = getMockHandlerContainer('runtime.onSuspend');
393 // Step 1. Wrap event listener.
394 var wrappedListener =
395 wrapper.wrapCallback(this.mockLocalFunctions.functions().listener, true);
396 Mock4JS.verifyAllMocks();
398 // Step 2. Invoke event listener, which will wrap a required callback.
399 // Setup and expectations.
401 var testFixture = this;
402 this.mockLocalFunctions.expects(once()).
404 will(callFunction(function() {
405 wrappedCallback = wrapper.wrapCallback(
406 testFixture.mockLocalFunctions.functions().callback);
411 Mock4JS.verifyAllMocks();
413 // Step 3. Fire runtime.onSuspend event.
415 this.mockGlobals.expects(once()).
416 buildErrorWithMessageForServer(stringContains(
417 'ASSERT: Pending callbacks when unloading event page')).
418 will(returnValue(testError));
419 this.mockGlobals.expects(once()).
420 reportError(eqJSON(testError));
423 assertEquals(1, onSuspendHandlerContainer.length);
424 onSuspendHandlerContainer[0]();
427 TEST_F('GoogleNowUtilityUnitTest',
428 'WrapperOnSuspendListenerSuccess',
430 // Tests that upon unloading event page, we don't get an error if there are no
431 // pending required callbacks.
434 this.makeMockLocalFunctions(['listener', 'callback']);
435 var onSuspendHandlerContainer = getMockHandlerContainer('runtime.onSuspend');
437 // Step 1. Wrap event listener.
438 var wrappedListener =
439 wrapper.wrapCallback(this.mockLocalFunctions.functions().listener, true);
440 Mock4JS.verifyAllMocks();
442 // Step 2. Invoke event listener, which will wrap a required callback.
443 // Setup and expectations.
445 var testFixture = this;
446 this.mockLocalFunctions.expects(once()).
448 will(callFunction(function() {
449 wrappedCallback = wrapper.wrapCallback(
450 testFixture.mockLocalFunctions.functions().callback);
455 Mock4JS.verifyAllMocks();
457 // Step 3. Call wrapped callback.
459 this.mockLocalFunctions.expects(once()).callback();
464 // Step 4. Fire runtime.onSuspend event.
465 assertEquals(1, onSuspendHandlerContainer.length);
466 onSuspendHandlerContainer[0]();
469 var taskNameA = 'TASK A';
470 var taskNameB = 'TASK B';
471 var taskNameC = 'TASK C';
473 function areTasksConflicting(newTaskName, scheduledTaskName) {
474 // Task B is conflicting with Task A. This means that if Task B is added when
475 // Task A is running, Task B will be ignored (but not vice versa). No other
476 // pair is conflicting.
477 return newTaskName == taskNameB && scheduledTaskName == taskNameA;
480 function setUpTaskManagerTest(fixture) {
481 fixture.makeAndRegisterMockApis([
482 'wrapper.checkInWrappedCallback',
483 'wrapper.registerWrapperPluginFactory',
484 'wrapper.debugGetStateString'
486 fixture.makeMockLocalFunctions(['task1', 'task2', 'task3']);
487 fixture.makeAndRegisterMockGlobals(['reportError']);
489 fixture.mockApis.stubs().wrapper_checkInWrappedCallback();
490 fixture.mockApis.stubs().wrapper_debugGetStateString().
491 will(returnValue('testWrapperDebugState'));
493 var registerWrapperPluginFactorySavedArgs = new SaveMockArguments();
494 fixture.mockApis.expects(once()).wrapper_registerWrapperPluginFactory(
495 registerWrapperPluginFactorySavedArgs.match(ANYTHING));
496 var tasks = buildTaskManager(areTasksConflicting);
497 Mock4JS.verifyAllMocks();
501 pluginFactory: registerWrapperPluginFactorySavedArgs.arguments[0]
505 TEST_F('GoogleNowUtilityUnitTest', 'TaskManager2Sequential', function() {
506 // Tests that 2 tasks get successfully executed consecutively, even if the
507 // second one conflicts with the first.
510 var test = setUpTaskManagerTest(this);
512 // Step 1. Add 1st task that doesn't create pending callbacks.
514 this.mockLocalFunctions.expects(once()).task1();
516 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
517 Mock4JS.verifyAllMocks();
519 // Step 2. Add 2nd task.
521 this.mockLocalFunctions.expects(once()).task2();
523 test.tasks.add(taskNameB, this.mockLocalFunctions.functions().task2);
526 TEST_F('GoogleNowUtilityUnitTest', 'TaskManagerConflicting', function() {
527 // Tests that adding a task while a conflicting task is being executed causes
528 // the second one to be ignored.
531 var test = setUpTaskManagerTest(this);
532 var task1PluginInstance;
534 // Step 1. Add 1st task that creates a pending callback.
536 this.mockLocalFunctions.expects(once()).task1().
537 will(callFunction(function() {
538 task1PluginInstance = test.pluginFactory();
541 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
542 Mock4JS.verifyAllMocks();
544 // Step 2. Add 2nd task. Since it conflicts with currently running task1
545 // (see areTasksConflicting), it should be ignored.
546 test.tasks.add(taskNameB, this.mockLocalFunctions.functions().task2);
547 Mock4JS.verifyAllMocks();
549 // Step 3. Enter the callback of task1.
550 task1PluginInstance.prologue();
551 Mock4JS.verifyAllMocks();
553 // Step 4. Leave the callback of task1.
554 task1PluginInstance.epilogue();
557 TEST_F('GoogleNowUtilityUnitTest', 'TaskManagerNestedTaskEnqueue', function() {
558 // Tests that adding a task while a non-conflicting task is being executed
559 // causes the second one to be executed after the first one completes.
562 var test = setUpTaskManagerTest(this);
563 var task1PluginInstance;
565 // Step 1. Add 1st task that creates a pending callback.
567 this.mockLocalFunctions.expects(once()).task1().
568 will(callFunction(function() {
569 task1PluginInstance = test.pluginFactory();
572 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
573 Mock4JS.verifyAllMocks();
575 // Step 2. Add 2nd task. Since it doesn't conflict with currently running
576 // task1 (see areTasksConflicting), it should not be ignored.
577 test.tasks.add(taskNameC, this.mockLocalFunctions.functions().task2);
578 Mock4JS.verifyAllMocks();
580 // Step 3. Enter the callback of task1.
581 task1PluginInstance.prologue();
582 Mock4JS.verifyAllMocks();
584 // Step 4. Leave the callback of task1.
586 this.mockLocalFunctions.expects(once()).task2();
588 task1PluginInstance.epilogue();
591 TEST_F('GoogleNowUtilityUnitTest', 'TaskManagerBranching', function() {
592 // Tests that task manager correctly detects completion of tasks that create
593 // branching chains of callbacks (in this test, task1 creates pending
594 // callbacks 1 and 2, and callback 1 creates pending callback 3).
597 var test = setUpTaskManagerTest(this);
598 var task1PluginInstance1, task1PluginInstance2, task1PluginInstance3;
600 // Step 1. Add 1st task that creates a 2 pending callbacks.
602 this.mockLocalFunctions.expects(once()).task1().
603 will(callFunction(function() {
604 task1PluginInstance1 = test.pluginFactory();
605 task1PluginInstance2 = test.pluginFactory();
608 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
609 Mock4JS.verifyAllMocks();
611 // Step 2. Add 2nd task, which is not conflicting (see areTasksConflicting)
613 test.tasks.add(taskNameC, this.mockLocalFunctions.functions().task2);
614 Mock4JS.verifyAllMocks();
616 // Step 3. Enter callback 1, create pending callback 3, exit callback 1.
617 // Enter/exit callback 2. Enter callback 3.
618 task1PluginInstance1.prologue();
619 task1PluginInstance3 = test.pluginFactory();
620 task1PluginInstance1.epilogue();
621 task1PluginInstance2.prologue();
622 task1PluginInstance2.epilogue();
623 task1PluginInstance3.prologue();
624 Mock4JS.verifyAllMocks();
626 // Step 4. Leave 3rd callback of task1. Now task1 is complete, and task2
629 this.mockLocalFunctions.expects(once()).task2();
631 task1PluginInstance3.epilogue();
634 TEST_F('GoogleNowUtilityUnitTest', 'TaskManagerSuspendError', function() {
635 // Tests that task manager's onSuspend method reports an error if there are
639 var test = setUpTaskManagerTest(this);
640 var onSuspendHandlerContainer = getMockHandlerContainer('runtime.onSuspend');
642 // Step 1. Add a task that creates a pending callback.
644 this.mockLocalFunctions.expects(once()).task1().
645 will(callFunction(function() {
646 test.pluginFactory();
649 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
650 Mock4JS.verifyAllMocks();
652 // Step 2. Invoke onSuspend event of the task manager.
653 // Setup and expectations. The 2 callbacks in onSuspendHandlerContainer are
654 // from the wrapper and the task manager.
655 assertEquals(2, onSuspendHandlerContainer.length);
656 this.mockGlobals.expects(once()).reportError(eqToString(
657 'Error: ASSERT: Incomplete task when unloading event page,' +
658 ' queue = [{"name":"TASK A"}], testWrapperDebugState'));
660 onSuspendHandlerContainer[1]();
663 TEST_F('GoogleNowUtilityUnitTest', 'TaskManagerSuspendSuccess', function() {
664 // Tests that task manager's onSuspend method does not report an error if all
668 var test = setUpTaskManagerTest(this);
669 var onSuspendHandlerContainer = getMockHandlerContainer('runtime.onSuspend');
670 var task1PluginInstance;
672 // Step 1. Add a task that creates a pending callback.
674 this.mockLocalFunctions.expects(once()).task1().
675 will(callFunction(function() {
676 task1PluginInstance = test.pluginFactory();
679 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
680 Mock4JS.verifyAllMocks();
682 // Step 2. Invoke task's callback and the onSuspend event of the task manager.
683 // The 2 callbacks in onSuspendHandlerContainer are from the wrapper and the
685 task1PluginInstance.prologue();
686 task1PluginInstance.epilogue();
687 onSuspendHandlerContainer[1]();
690 TEST_F('GoogleNowUtilityUnitTest', 'TaskManager3Tasks', function() {
691 // Tests that 3 tasks can be executed too. In particular, that if the second
692 // task is a single-step task which execution was originally blocked by task1,
693 // unblocking it causes immediate synchronous execution of both tasks 2 and 3.
696 var test = setUpTaskManagerTest(this);
697 var task1PluginInstance;
699 // Step 1. Add 1st task that creates a pending callback.
701 this.mockLocalFunctions.expects(once()).task1().
702 will(callFunction(function() {
703 task1PluginInstance = test.pluginFactory();
706 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
707 Mock4JS.verifyAllMocks();
709 // Step 2. Add 2nd and 3rd tasks, both non-conflicting (see
710 // areTasksConflicting) with task1.
711 test.tasks.add(taskNameC, this.mockLocalFunctions.functions().task2);
712 test.tasks.add(taskNameC, this.mockLocalFunctions.functions().task3);
713 Mock4JS.verifyAllMocks();
715 // Step 3. Enter the callback of task1.
716 task1PluginInstance.prologue();
717 Mock4JS.verifyAllMocks();
719 // Step 4. Leave the callback of task1.
721 this.mockLocalFunctions.expects(once()).task2();
722 this.mockLocalFunctions.expects(once()).task3();
724 task1PluginInstance.epilogue();
727 TEST_F('GoogleNowUtilityUnitTest', 'TaskManagerNestedNonTask', function() {
728 // Tests callbacks requested while a task is running, but not from a callback
729 // belonging to a task, are not counted as a part of the task.
732 var test = setUpTaskManagerTest(this);
733 var task1PluginInstance;
735 // Step 1. Add 1st task that creates a pending callback.
737 this.mockLocalFunctions.expects(once()).task1().
738 will(callFunction(function() {
739 task1PluginInstance = test.pluginFactory();
742 test.tasks.add(taskNameA, this.mockLocalFunctions.functions().task1);
743 Mock4JS.verifyAllMocks();
745 // Step 2. Create a pending callback from code that is not a part of the task.
746 test.pluginFactory();
747 Mock4JS.verifyAllMocks();
749 // Step 3. Enter the callback of task1. After this, task1 should be
750 // finished despite the pending non-task callback.
751 task1PluginInstance.prologue();
752 task1PluginInstance.epilogue();
753 Mock4JS.verifyAllMocks();
755 // Step 4. Check that task1 is finished by submitting task2, which should
756 // be executed immediately.
757 this.mockLocalFunctions.expects(once()).task2();
758 test.tasks.add(taskNameC, this.mockLocalFunctions.functions().task2);
761 var testAttemptAlarmName = 'attempt-scheduler-testAttempts';
762 var testAttemptStorageKey = 'current-delay-testAttempts';
763 var testInitialDelaySeconds = 239;
764 var testMaximumDelaySeconds = 2239;
765 // Value to be returned by mocked Math.random(). We want the value returned by
766 // Math.random() to be predictable to be able to check results against expected
767 // values. A fixed seed would be okay, but fixed seeding isn't possible in JS at
769 var testRandomValue = 0.31415926;
772 * Creates a default storage current delay object.
774 function createTestAttemptStorageEntryRequest() {
775 var storageObject = {};
776 storageObject[testAttemptStorageKey] = undefined;
777 return storageObject;
781 * Creates a test storage object that attempt manager uses to store current
784 function createTestAttemptStorageEntry(delaySeconds) {
785 var storageObject = {};
786 storageObject[testAttemptStorageKey] = delaySeconds;
787 return storageObject;
790 function setupAttemptManagerTest(fixture) {
791 Math.random = function() { return testRandomValue; }
793 fixture.makeMockLocalFunctions([
797 fixture.makeAndRegisterMockApis([
798 'chrome.alarms.clear',
799 'chrome.alarms.create',
800 'chrome.storage.local.remove',
801 'chrome.storage.local.set',
802 'fillFromChromeLocalStorage',
803 'instrumented.alarms.get'
806 var testAttempts = buildAttemptManager(
808 fixture.mockLocalFunctions.functions().attempt,
809 testInitialDelaySeconds,
810 testMaximumDelaySeconds);
811 Mock4JS.verifyAllMocks();
814 attempts: testAttempts
818 TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerStartStop', function() {
819 // Tests starting and stopping an attempt manager.
822 var test = setupAttemptManagerTest(this);
824 // Step 1. Check that attempt manager is not running.
826 var alarmsGetSavedArgs = new SaveMockArguments();
827 this.mockApis.expects(once()).
828 instrumented_alarms_get(
829 alarmsGetSavedArgs.match(eq(testAttemptAlarmName)),
830 alarmsGetSavedArgs.match(ANYTHING)).
831 will(invokeCallback(alarmsGetSavedArgs, 1, undefined));
832 this.mockLocalFunctions.expects(once()).isRunningCallback(false);
834 test.attempts.isRunning(
835 this.mockLocalFunctions.functions().isRunningCallback);
836 Mock4JS.verifyAllMocks();
838 // Step 2. Start attempt manager with no parameters.
840 var expectedRetryDelaySeconds =
841 testInitialDelaySeconds * (1 + testRandomValue * 0.2);
842 this.mockApis.expects(once()).chrome_alarms_create(
843 testAttemptAlarmName,
845 delayInMinutes: expectedRetryDelaySeconds / 60,
846 periodInMinutes: testMaximumDelaySeconds / 60
848 this.mockApis.expects(once()).chrome_storage_local_set(
849 eqJSON(createTestAttemptStorageEntry(expectedRetryDelaySeconds)));
851 test.attempts.start();
852 Mock4JS.verifyAllMocks();
854 // Step 3. Check that attempt manager is running.
856 alarmsGetSavedArgs = new SaveMockArguments();
857 this.mockApis.expects(once()).
858 instrumented_alarms_get(
859 alarmsGetSavedArgs.match(eq(testAttemptAlarmName)),
860 alarmsGetSavedArgs.match(ANYTHING)).
861 will(invokeCallback(alarmsGetSavedArgs, 1, {testField: 'TEST VALUE'}));
862 this.mockLocalFunctions.expects(once()).isRunningCallback(true);
864 test.attempts.isRunning(
865 this.mockLocalFunctions.functions().isRunningCallback);
866 Mock4JS.verifyAllMocks();
868 // Step 4. Stop task manager.
870 this.mockApis.expects(once()).chrome_alarms_clear(testAttemptAlarmName);
871 this.mockApis.expects(once()).chrome_storage_local_remove(
872 testAttemptStorageKey);
874 test.attempts.stop();
878 'GoogleNowUtilityUnitTest',
879 'AttemptManagerStartWithDelayParam',
881 // Tests starting an attempt manager with a delay parameter.
884 var test = setupAttemptManagerTest(this);
885 var testFirstDelaySeconds = 1039;
887 // Starting attempt manager with a parameter specifying first delay.
889 this.mockApis.expects(once()).chrome_alarms_create(
890 testAttemptAlarmName,
892 delayInMinutes: testFirstDelaySeconds / 60,
893 periodInMinutes: testMaximumDelaySeconds / 60
895 this.mockApis.expects(once()).chrome_storage_local_remove(
896 testAttemptStorageKey);
898 test.attempts.start(testFirstDelaySeconds);
901 TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerExponGrowth', function() {
902 // Tests that retry time grows exponentially. We don't need to check the case
903 // of growing more than once, since the object doesn't have state, and the
904 // test checks all its inputs and outputs of the tested code.
907 var test = setupAttemptManagerTest(this);
908 var testStoredRetryDelay = 433;
910 // Call scheduleRetry, which schedules a retry.
911 // Current retry time is less than 1/2 of the maximum delay.
913 var expectedRetryDelaySeconds =
914 testStoredRetryDelay * 2 * (1 + testRandomValue * 0.2);
915 expectChromeLocalStorageGet(
917 createTestAttemptStorageEntryRequest(),
918 createTestAttemptStorageEntry(testStoredRetryDelay),
920 this.mockApis.expects(once()).chrome_alarms_create(
921 testAttemptAlarmName,
923 delayInMinutes: expectedRetryDelaySeconds / 60,
924 periodInMinutes: testMaximumDelaySeconds / 60}));
925 this.mockApis.expects(once()).chrome_storage_local_set(
926 eqJSON(createTestAttemptStorageEntry(expectedRetryDelaySeconds)));
928 var thenCalled = false;
929 var catchCalled = false;
930 test.attempts.scheduleRetry().then(function(request) {
932 }).catch(function(request) {
935 assertTrue(thenCalled);
936 assertFalse(catchCalled);
939 TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerGrowthLimit', function() {
940 // Tests that retry time stops growing at the maximum value.
943 var test = setupAttemptManagerTest(this);
944 var testStoredRetryDelay = 1500;
946 // Call scheduleRetry, which schedules a retry.
947 // Current retry time is greater than 1/2 of the maximum delay.
949 var expectedRetryDelaySeconds =
950 testMaximumDelaySeconds * (1 + testRandomValue * 0.2);
951 expectChromeLocalStorageGet(
953 createTestAttemptStorageEntryRequest(),
954 createTestAttemptStorageEntry(testStoredRetryDelay),
956 this.mockApis.expects(once()).chrome_alarms_create(
957 testAttemptAlarmName,
959 delayInMinutes: expectedRetryDelaySeconds / 60,
960 periodInMinutes: testMaximumDelaySeconds / 60
962 this.mockApis.expects(once()).chrome_storage_local_set(
963 eqJSON(createTestAttemptStorageEntry(expectedRetryDelaySeconds)));
965 var thenCalled = false;
966 var catchCalled = false;
967 test.attempts.scheduleRetry().then(function(request) {
969 }).catch(function(request) {
972 assertTrue(thenCalled);
973 assertFalse(catchCalled);
976 TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerAlarm', function() {
977 // Tests that firing the alarm invokes the attempt.
980 var test = setupAttemptManagerTest(this);
981 var onAlarmHandlerContainer = getMockHandlerContainer('alarms.onAlarm');
982 assertEquals(1, onAlarmHandlerContainer.length);
984 // Fire the alarm and check that this invokes the attempt callback.
986 var alarmsGetSavedArgs = new SaveMockArguments();
987 this.mockApis.expects(once()).
988 instrumented_alarms_get(
989 alarmsGetSavedArgs.match(eq(testAttemptAlarmName)),
990 alarmsGetSavedArgs.match(ANYTHING)).
991 will(invokeCallback(alarmsGetSavedArgs, 1, {testField: 'TEST VALUE'}));
992 this.mockLocalFunctions.expects(once()).attempt();
994 onAlarmHandlerContainer[0]({name: testAttemptAlarmName});