Commit 37135c7b authored by robliao@chromium.org's avatar robliao@chromium.org

Attempt Manager Refactor and Opt-In Pipeline Refactor

Refactored the attempt manager to use a more obvious naming scheme as well as
participate in promises for retries.

This also entailed reexamination of all attempt managers in Chrome Now.
Opt-in checking as a result became a first class state change citizen and is
no longer part of the card checking pipeline, fixing a few opt-in aggression
issues. With this change, we don't care how often a push message comes through
that may indicate that Google Now is enabled.

BUG=360649,362306

Review URL: https://codereview.chromium.org/248473003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268302 0039d316-1c4b-4281-b951-d872f2087c98
parent 8b367f7f
...@@ -26,6 +26,7 @@ mockChromeEvent(instrumented, 'notifications.onShowSettings'); ...@@ -26,6 +26,7 @@ mockChromeEvent(instrumented, 'notifications.onShowSettings');
mockChromeEvent(instrumented, 'pushMessaging.onMessage'); mockChromeEvent(instrumented, 'pushMessaging.onMessage');
mockChromeEvent(instrumented, 'runtime.onInstalled'); mockChromeEvent(instrumented, 'runtime.onInstalled');
mockChromeEvent(instrumented, 'runtime.onStartup'); mockChromeEvent(instrumented, 'runtime.onStartup');
mockChromeEvent(instrumented, 'storage.onChanged');
NOTIFICATION_CARDS_URL = 'https://test/'; NOTIFICATION_CARDS_URL = 'https://test/';
navigator = {language: 'en-US'}; navigator = {language: 'en-US'};
...@@ -879,21 +879,19 @@ function buildAttemptManager( ...@@ -879,21 +879,19 @@ function buildAttemptManager(
} }
/** /**
* Schedules next attempt. * Schedules the alarm with a random factor to reduce the chance that all
* @param {number=} opt_previousDelaySeconds Previous delay in a sequence of * clients will fire their timers at the same time.
* retry attempts, if specified. Not specified for scheduling first retry * @param {number} durationSeconds Number of seconds before firing the alarm.
* in the exponential sequence.
*/ */
function scheduleNextAttempt(opt_previousDelaySeconds) { function scheduleAlarm(durationSeconds) {
var base = opt_previousDelaySeconds ? opt_previousDelaySeconds * 2 : var randomizedRetryDuration =
initialDelaySeconds; Math.min(durationSeconds * (1 + 0.2 * Math.random()),
var newRetryDelaySeconds = maximumDelaySeconds);
Math.min(base * (1 + 0.2 * Math.random()), maximumDelaySeconds);
createAlarm(newRetryDelaySeconds); createAlarm(randomizedRetryDuration);
var items = {}; var items = {};
items[currentDelayStorageKey] = newRetryDelaySeconds; items[currentDelayStorageKey] = randomizedRetryDuration;
chrome.storage.local.set(items); chrome.storage.local.set(items);
} }
...@@ -908,7 +906,7 @@ function buildAttemptManager( ...@@ -908,7 +906,7 @@ function buildAttemptManager(
createAlarm(opt_firstDelaySeconds); createAlarm(opt_firstDelaySeconds);
chrome.storage.local.remove(currentDelayStorageKey); chrome.storage.local.remove(currentDelayStorageKey);
} else { } else {
scheduleNextAttempt(); scheduleAlarm(initialDelaySeconds);
} }
} }
...@@ -921,21 +919,24 @@ function buildAttemptManager( ...@@ -921,21 +919,24 @@ function buildAttemptManager(
} }
/** /**
* Plans for the next attempt. * Schedules an exponential backoff retry.
* @param {function()} callback Completion callback. It will be invoked after * @return {Promise} A promise to schedule the retry.
* the planning is done.
*/ */
function planForNext(callback) { function scheduleRetry() {
var request = {}; var request = {};
request[currentDelayStorageKey] = undefined; request[currentDelayStorageKey] = undefined;
fillFromChromeLocalStorage(request, PromiseRejection.ALLOW) return fillFromChromeLocalStorage(request, PromiseRejection.ALLOW)
.catch(function() { .catch(function() {
request[currentDelayStorageKey] = maximumDelaySeconds; request[currentDelayStorageKey] = maximumDelaySeconds;
return Promise.resolve(request); return Promise.resolve(request);
}).then(function(items) { })
console.log('planForNext-get-storage ' + JSON.stringify(items)); .then(function(items) {
scheduleNextAttempt(items[currentDelayStorageKey]); console.log('scheduleRetry-get-storage ' + JSON.stringify(items));
callback(); var retrySeconds = initialDelaySeconds;
if (items[currentDelayStorageKey]) {
retrySeconds = items[currentDelayStorageKey] * 2;
}
scheduleAlarm(retrySeconds);
}); });
} }
...@@ -949,7 +950,7 @@ function buildAttemptManager( ...@@ -949,7 +950,7 @@ function buildAttemptManager(
return { return {
start: start, start: start,
planForNext: planForNext, scheduleRetry: scheduleRetry,
stop: stop, stop: stop,
isRunning: isRunning isRunning: isRunning
}; };
......
...@@ -792,7 +792,6 @@ function setupAttemptManagerTest(fixture) { ...@@ -792,7 +792,6 @@ function setupAttemptManagerTest(fixture) {
fixture.makeMockLocalFunctions([ fixture.makeMockLocalFunctions([
'attempt', 'attempt',
'planForNextCallback',
'isRunningCallback' 'isRunningCallback'
]); ]);
fixture.makeAndRegisterMockApis([ fixture.makeAndRegisterMockApis([
...@@ -908,8 +907,8 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerExponGrowth', function() { ...@@ -908,8 +907,8 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerExponGrowth', function() {
var test = setupAttemptManagerTest(this); var test = setupAttemptManagerTest(this);
var testStoredRetryDelay = 433; var testStoredRetryDelay = 433;
// Call planForNext, which prepares next attempt. Current retry time // Call scheduleRetry, which schedules a retry.
// is less than 1/2 of the maximum delay. // Current retry time is less than 1/2 of the maximum delay.
// Expectations. // Expectations.
var expectedRetryDelaySeconds = var expectedRetryDelaySeconds =
testStoredRetryDelay * 2 * (1 + testRandomValue * 0.2); testStoredRetryDelay * 2 * (1 + testRandomValue * 0.2);
...@@ -925,10 +924,16 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerExponGrowth', function() { ...@@ -925,10 +924,16 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerExponGrowth', function() {
periodInMinutes: testMaximumDelaySeconds / 60})); periodInMinutes: testMaximumDelaySeconds / 60}));
this.mockApis.expects(once()).chrome_storage_local_set( this.mockApis.expects(once()).chrome_storage_local_set(
eqJSON(createTestAttemptStorageEntry(expectedRetryDelaySeconds))); eqJSON(createTestAttemptStorageEntry(expectedRetryDelaySeconds)));
this.mockLocalFunctions.expects(once()).planForNextCallback();
// Invocation. // Invocation.
test.attempts.planForNext( var thenCalled = false;
this.mockLocalFunctions.functions().planForNextCallback); var catchCalled = false;
test.attempts.scheduleRetry().then(function(request) {
thenCalled = true;
}).catch(function(request) {
catchCalled = true;
});
assertTrue(thenCalled);
assertFalse(catchCalled);
}); });
TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerGrowthLimit', function() { TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerGrowthLimit', function() {
...@@ -938,8 +943,8 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerGrowthLimit', function() { ...@@ -938,8 +943,8 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerGrowthLimit', function() {
var test = setupAttemptManagerTest(this); var test = setupAttemptManagerTest(this);
var testStoredRetryDelay = 1500; var testStoredRetryDelay = 1500;
// Call planForNext, which prepares next attempt. Current retry time // Call scheduleRetry, which schedules a retry.
// is greater than 1/2 of the maximum delay. // Current retry time is greater than 1/2 of the maximum delay.
// Expectations. // Expectations.
var expectedRetryDelaySeconds = testMaximumDelaySeconds; var expectedRetryDelaySeconds = testMaximumDelaySeconds;
expectChromeLocalStorageGet( expectChromeLocalStorageGet(
...@@ -955,10 +960,16 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerGrowthLimit', function() { ...@@ -955,10 +960,16 @@ TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerGrowthLimit', function() {
})); }));
this.mockApis.expects(once()).chrome_storage_local_set( this.mockApis.expects(once()).chrome_storage_local_set(
eqJSON(createTestAttemptStorageEntry(expectedRetryDelaySeconds))); eqJSON(createTestAttemptStorageEntry(expectedRetryDelaySeconds)));
this.mockLocalFunctions.expects(once()).planForNextCallback();
// Invocation. // Invocation.
test.attempts.planForNext( var thenCalled = false;
this.mockLocalFunctions.functions().planForNextCallback); var catchCalled = false;
test.attempts.scheduleRetry().then(function(request) {
thenCalled = true;
}).catch(function(request) {
catchCalled = true;
});
assertTrue(thenCalled);
assertFalse(catchCalled);
}); });
TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerAlarm', function() { TEST_F('GoogleNowUtilityUnitTest', 'AttemptManagerAlarm', function() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment