Index: chrome/test/data/webui/settings/prefs_tests.js |
diff --git a/chrome/test/data/webui/settings/prefs_tests.js b/chrome/test/data/webui/settings/prefs_tests.js |
index 95e8f487c94b30d9c6c726e009466fd1ee62cd3c..12c8deecfd3e16bbe6a7ee52978ee3321068a9f5 100644 |
--- a/chrome/test/data/webui/settings/prefs_tests.js |
+++ b/chrome/test/data/webui/settings/prefs_tests.js |
@@ -5,6 +5,15 @@ |
/** @fileoverview Suite of tests for cr-settings-prefs. */ |
cr.define('cr_settings_prefs', function() { |
/** |
+ * Creates a deep copy of the object. |
+ * @param {!Object} obj |
+ * @return {!Object} |
+ */ |
+ function deepCopy(obj) { |
+ return JSON.parse(JSON.stringify(obj)); |
+ } |
+ |
+ /** |
* Mock of chrome.settingsPrivate API. |
* @constructor |
* @extends {chrome.settingsPrivate} |
@@ -12,6 +21,7 @@ cr.define('cr_settings_prefs', function() { |
function MockSettingsApi() { |
this.prefs = {}; |
this.listener_ = null; |
+ this.rand_ = getPseudoRand(); |
// Hack alert: bind this instance's onPrefsChanged members to this. |
this.onPrefsChanged = { |
@@ -24,6 +34,8 @@ cr.define('cr_settings_prefs', function() { |
this.addBooleanPref_('browser.enable_html5', true); |
this.addNumberPref_('device.overclock', .6); |
this.addStringPref_('homepage', 'example.com'); |
+ this.addListPref_('languages', generateList(10, String, this.rand_)); |
+ this.addListPref_('content.sites', generateList(2, Object, this.rand_)); |
} |
MockSettingsApi.prototype = { |
@@ -43,7 +55,7 @@ cr.define('cr_settings_prefs', function() { |
// Send a copy of prefs to keep our internal state private. |
var prefs = []; |
for (var key in this.prefs) |
- prefs.push(this.createCopy_(this.prefs[key])); |
+ prefs.push(deepCopy(this.prefs[key])); |
setTimeout(callback.bind(null, prefs)); |
}, |
@@ -59,32 +71,43 @@ cr.define('cr_settings_prefs', function() { |
this.failNextSetPref_ = false; |
return; |
} |
+ assertNotEquals(true, this.disallowSetPref_); |
- // TODO(michaelpg): support list and dict prefs. |
- var changed = this.prefs[key].value != value; |
- this.prefs[key].value = value; |
- setTimeout(function() { |
- callback(true); |
+ var changed = JSON.stringify(pref.value) != JSON.stringify(value); |
+ pref.value = JSON.parse(JSON.stringify(value)); |
+ setTimeout(callback.bind(null,true)); |
+ if (changed) { |
+ setTimeout(function() { |
// Like chrome.settingsPrivate, send a notification when prefs change. |
- if (changed) |
- this.sendPrefChanges([key], [value]); |
- }.bind(this)); |
+ this.sendPrefChanges([key], [deepCopy(value)]); |
+ }.bind(this)); |
+ } |
}, |
getPref: function(key, callback) { |
var pref = this.prefs[key]; |
assertNotEquals(undefined, pref); |
- setTimeout(callback.bind(null, this.createCopy_(pref))); |
+ var copy = deepCopy(pref); |
+ setTimeout(callback.bind(null, copy)); |
}, |
// Functions used by tests. |
- /** Instructs the API to fail when setPref is next called. */ |
+ /** Instructs the API to return a failure when setPref is next called. */ |
failNextSetPref: function() { |
this.failNextSetPref_ = true; |
}, |
+ /** Instructs the API to assert (fail the test) if setPref is called. */ |
+ disallowSetPref: function() { |
+ this.disallowSetPref_ = true; |
+ }, |
+ |
+ allowSetPref: function() { |
+ this.disallowSetPref_ = false; |
+ }, |
+ |
/** |
* Notifies the listener of pref changes. |
* @param {!Array<string>} keys |
@@ -97,11 +120,13 @@ cr.define('cr_settings_prefs', function() { |
for (var i = 0; i < keys.length; i++) { |
var key = keys[i]; |
var pref = this.prefs[key]; |
+ assertNotEquals(undefined, pref); |
pref.value = values[i]; |
- prefs.push(this.createCopy_(pref)); |
+ prefs.push(deepCopy(pref)); |
} |
- setTimeout(this.listener_.bind(null, prefs)); |
+ this.listener_(prefs); |
+// setTimeout(this.listener_.bind(null, prefs)); |
}, |
// Private methods for use by the mock API. |
@@ -144,12 +169,11 @@ cr.define('cr_settings_prefs', function() { |
}, |
/** |
- * Creates a deep copy of the preference. |
- * @param {chrome.settingsPrivate.PrefObject} pref |
- * @return {chrome.settingsPrivate.PrefObject} |
+ * @param {string} key |
+ * @param {!Array} value |
*/ |
- createCopy_: function(pref) { |
- return JSON.parse(JSON.stringify(pref)); |
+ addListPref_: function(key, value) { |
+ this.addPref_(chrome.settingsPrivate.PrefType.LIST, key, value); |
}, |
}; |
@@ -170,6 +194,79 @@ cr.define('cr_settings_prefs', function() { |
}); |
} |
+ // Pseudo-random number generator using Fibonacci LFSR. |
+ function getPseudoRand() { |
+ // Start with any integer in [1, 65535]. |
+ var lfsr = 3465; // Chromium's melting point (F). |
+ var bit; |
+ return function() { |
+ bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5) ) & 1; |
+ lfsr = (lfsr >> 1) | (bit << 15); |
+ return lfsr; |
+ }; |
+ } |
+ |
+ function generateList(length, valueType, rand) { |
+ var list = []; |
+ for (var i = 0; i < length; i++) { |
+ if (valueType == Boolean) { |
+ list.push(Boolean(rand() % 2)); |
+ } else if (valueType == Number) { |
+ list.push(rand()); |
+ } else if (valueType == String) { |
+ // Allow empty strings. |
+ list.push(generateString(rand() % 20, rand)); |
+ } else if (valueType == Object) { |
+ // Don't allow empty objects. |
+ list.push(generateObject(rand() % 2 + 1, rand)); |
+ } |
+ } |
+ |
+ return list; |
+ }; |
+ |
+ function generateString(length, rand) { |
+ var s = ''; |
+ for (var i = 0; i < length; i++) |
+ s += String.fromCharCode(65 + rand() % 26); |
+ return s; |
+ } |
+ |
+ function generateObject(numProps, rand) { |
+ var obj = {}; |
+ var types = [Boolean, Number, String, Array]; |
+ var typesIndex = 0; |
+ for (var i = 0; i < numProps; i++) { |
+ var prop; |
+ do { |
+ prop = generateString(rand() % 20 + 1, rand); |
+ } while (obj[prop] != undefined); |
+ switch(types[typesIndex++ % types.length]) { |
+ case Boolean: |
+ obj[prop] = Boolean(rand() % 2); |
+ break; |
+ case Number: |
+ obj[prop] = rand(); |
+ break; |
+ case String: |
+ obj[prop] = generateString(rand() % 20, rand); |
+ break; |
+ case Array: |
+ // Generate a list of booleans, numbers or strings. |
+ obj[prop] = generateList(rand() % 10, types[rand() % 3], rand); |
+ break; |
+ } |
+ } |
+ return obj; |
+ } |
+/* |
+ var pref = { |
+ type: 'LIST', |
+ key: key, |
+ value: list |
+ }; |
+ */ |
+ |
function registerTests() { |
suite('CrSettingsPrefs', function() { |
/** |
@@ -229,7 +326,7 @@ cr.define('cr_settings_prefs', function() { |
expectEquals(expectedPref.key, actualPref.key); |
expectEquals(expectedPref.type, actualPref.type); |
- if (expectedPref.type != chrome.settingsPrivate.LIST) { |
+ if (expectedPref.type != chrome.settingsPrivate.PrefType.LIST) { |
expectEquals(expectedPref.value, actualPref.value); |
} else { |
expectEquals(JSON.stringify(expectedPref.value), |
@@ -238,7 +335,10 @@ cr.define('cr_settings_prefs', function() { |
} |
}); |
- test('forwards pref changes to API', function() { |
+ test('forwards pref changes to API', function(done) { |
+ var rand = getPseudoRand(); |
+ rand();rand(); |
+ |
// Test that cr-settings-prefs uses the setPref API. |
prefs.set('prefs.browser.enable_flash.value', true); |
expectTrue(mockApi.prefs['browser.enable_flash'].value); |
@@ -246,17 +346,39 @@ cr.define('cr_settings_prefs', function() { |
expectEquals(1.2, mockApi.prefs['device.overclock'].value); |
prefs.set('prefs.homepage.value', 'chromium.org'); |
expectEquals('chromium.org', mockApi.prefs['homepage'].value); |
- |
- // Test that when setPref fails, the pref is reverted locally. |
- mockApi.failNextSetPref(); |
- prefs.set('prefs.browser.enable_flash.value', false); |
- mockApi.failNextSetPref(); |
- prefs.set('prefs.device.overclock.value', 2.0); |
- mockApi.failNextSetPref(); |
- prefs.set('prefs.homepage.value', 'invalid-url'); |
+ var list = generateList(10, String, rand); |
+ prefs.set('prefs.languages.value', deepCopy(list)); |
+ expectEquals(JSON.stringify(list), |
+ JSON.stringify(mockApi.prefs['languages'].value)); |
+ var newList = deepCopy(mockApi.prefs['content.sites'].value); |
+ var dict = newList[1]; |
+ var prop = Object.keys(dict)[0]; |
+ // Set a property (and append _ to ensure the value is different) |
+ dict[prop] = generateString(10, rand) + '_'; |
+// var listOfDicts = generateList(3, Object, rand); |
+ prefs.set('prefs.content.sites.value', deepCopy(newList)); |
+ expectEquals(JSON.stringify(newList), |
+ JSON.stringify(mockApi.prefs['content.sites'].value)); |
+ PolymerTest.async(function() { |
+ mockApi.disallowSetPref(); |
+ prefs.set('prefs.content.sites.value', deepCopy(newList)); |
+ mockApi.allowSetPref(); |
+ |
+ // Test that when setPref fails, the pref is reverted locally. |
+ mockApi.failNextSetPref(); |
+ prefs.set('prefs.browser.enable_flash.value', false); |
+ mockApi.failNextSetPref(); |
+ prefs.set('prefs.device.overclock.value', 2.0); |
+ mockApi.failNextSetPref(); |
+ prefs.set('prefs.homepage.value', 'invalid-url'); |
+ mockApi.failNextSetPref(); |
+ prefs.set('prefs.languages.value', generateList(10, String, rand)); |
+ mockApi.failNextSetPref(); |
+ prefs.set('prefs.content.sites.value', generateList(3, Object, rand)); |
+ }).async(function() { |
// Multiple asynchronous round trips need to happen. |
- return Promise.all([ |
+ Promise.all([ |
waitUntilTrue(function() { |
return prefs.prefs.browser.enable_flash.value; |
}), |
@@ -266,19 +388,101 @@ cr.define('cr_settings_prefs', function() { |
waitUntilTrue(function() { |
return prefs.prefs.homepage.value == 'chromium.org'; |
}), |
- ]); |
+ waitUntilTrue(function() { |
+ return JSON.stringify(prefs.prefs.languages.value) == |
+ JSON.stringify(list); |
+ }), |
+ waitUntilTrue(function() { |
+ return JSON.stringify(prefs.prefs.content.sites.value) == |
+ JSON.stringify(newList); |
+ }), |
+ ]).then(function() { |
+ done(); |
+ }); |
+ }); |
}); |
test('responds to API changes', function(done) { |
- mockApi.sendPrefChanges(['top_level_pref'], [false]); |
- PolymerTest.async(function() { |
- expectEquals(false, prefs.prefs.top_level_pref.value); |
+ var rand = getPseudoRand(); |
- mockApi.sendPrefChanges(['top_level_pref'], [true]); |
+ var topLevelPref = false; |
+ var contentSites = generateList(2, Object, rand); |
+ contentSites = [{E: 'e', F: 'ff'}, {G: true, H: false}]; |
+ |
+ mockApi.disallowSetPref(); |
+ PolymerTest.async(function() { |
+ mockApi.sendPrefChanges(['top_level_pref', 'content.sites'], |
+ [topLevelPref, deepCopy(contentSites)]); |
}).async(function() { |
- expectEquals(true, prefs.prefs.top_level_pref.value); |
- done(); |
- }); |
+ expectEquals(topLevelPref, prefs.prefs.top_level_pref.value); |
+ expectEquals(JSON.stringify(contentSites), |
+ JSON.stringify(prefs.prefs.content.sites.value)); |
+ |
+ topLevelPref = true; |
+ var oldContentSites = contentSites; |
+ contentSites = deepCopy(contentSites); |
+ var dict = contentSites[1]; |
+ var prop = Object.keys(contentSites[1])[0]; |
+ // Set a property (and append _ to ensure the value is different) |
+ dict[prop] = generateString(10, rand) + '_'; |
+ mockApi.sendPrefChanges(['top_level_pref', 'content.sites'], |
+ [topLevelPref, deepCopy(contentSites)]); |
+ }).async(function() { |
+ expectEquals(topLevelPref, prefs.prefs.top_level_pref.value); |
+ expectEquals(JSON.stringify(contentSites), |
+ JSON.stringify(prefs.prefs.content.sites.value)); |
+ }).async(function() { |
+ // Shouldn't respond to non-changes. |
+ mockApi.disallowSetPref(); |
+ mockApi.sendPrefChanges(['top_level_pref', 'content.sites'], |
+ [topLevelPref, deepCopy(contentSites)]); |
+ }).async(function() {} |
+ ).async(done); |
+ }); |
+ }); |
+ |
+ suite('pseudo-random number generator', function() { |
+ test('generates pseudo-random numbers', function() { |
+ var numbers = {}; |
+ var rand1 = getPseudoRand(); |
+ var rand2 = getPseudoRand(); |
+ for (var i = 0; i < 1000; i++) { |
+ var num1 = rand1(); |
+ var num2 = rand2(); |
+ // Generators should have the same seed and algorithm. |
+ expectEquals(num1, num2); |
+ // Generator shouldn't cycle in the first few numbers. |
+ expectEquals(undefined, numbers[num1]); |
+ numbers[num1] = true; |
+ } |
+ }); |
+ }); |
+ |
+ suite('list generator', function() { |
+ var rand; |
+ |
+ function checkList(length, type) { |
+ var list = generateList(length, type, rand); |
+ expectEquals(length, list.length); |
+ for (var i = 0; i < length; i++) |
+ expectEquals(typeof list[i], type.name.toLowerCase()); |
+ return list; |
+ } |
+ |
+ setup(function() { |
+ rand = getPseudoRand(); |
+ }); |
+ |
+ test('flat lists', function() { |
+ checkList(10, Boolean); |
+ checkList(20, Number); |
+ checkList(5, String); |
+ }); |
+ |
+ test('object lists', function() { |
+ var list = checkList(40, Object); |
+ for (var i = 0; i < 40; i++) |
+ expectGT(Object.keys(list[i]).length, 0); |
}); |
}); |
} |