Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Side by Side Diff: chrome/test/data/webui/settings/prefs_tests.js

Issue 1333473002: Support lists in <cr-settings-pref> (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@PrefsTests
Patch Set: Now with 100% more correctness Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/test/data/webui/polymer_browser_test_base.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** @fileoverview Suite of tests for cr-settings-prefs. */ 5 /** @fileoverview Suite of tests for cr-settings-prefs. */
6 cr.define('cr_settings_prefs', function() { 6 cr.define('cr_settings_prefs', function() {
7 /** 7 /**
8 * Creates a deep copy of the object.
9 * @param {!Object} obj
10 * @return {!Object}
11 */
12 function deepCopy(obj) {
13 return JSON.parse(JSON.stringify(obj));
14 }
15
16 /**
8 * Mock of chrome.settingsPrivate API. 17 * Mock of chrome.settingsPrivate API.
9 * @constructor 18 * @constructor
10 * @extends {chrome.settingsPrivate} 19 * @extends {chrome.settingsPrivate}
11 */ 20 */
12 function MockSettingsApi() { 21 function MockSettingsApi() {
13 this.prefs = {}; 22 this.prefs = {};
14 this.listener_ = null; 23 this.listener_ = null;
24 this.rand_ = getPseudoRand();
15 25
16 // Hack alert: bind this instance's onPrefsChanged members to this. 26 // Hack alert: bind this instance's onPrefsChanged members to this.
17 this.onPrefsChanged = { 27 this.onPrefsChanged = {
18 addListener: this.onPrefsChanged.addListener.bind(this), 28 addListener: this.onPrefsChanged.addListener.bind(this),
19 removeListener: this.onPrefsChanged.removeListener.bind(this), 29 removeListener: this.onPrefsChanged.removeListener.bind(this),
20 }; 30 };
21 31
22 this.addBooleanPref_('top_level_pref', true); 32 this.addBooleanPref_('top_level_pref', true);
23 this.addBooleanPref_('browser.enable_flash', false); 33 this.addBooleanPref_('browser.enable_flash', false);
24 this.addBooleanPref_('browser.enable_html5', true); 34 this.addBooleanPref_('browser.enable_html5', true);
25 this.addNumberPref_('device.overclock', .6); 35 this.addNumberPref_('device.overclock', .6);
26 this.addStringPref_('homepage', 'example.com'); 36 this.addStringPref_('homepage', 'example.com');
37 this.addListPref_('languages', generateList(10, String, this.rand_));
38 this.addListPref_('content.sites', generateList(2, Object, this.rand_));
27 } 39 }
28 40
29 MockSettingsApi.prototype = { 41 MockSettingsApi.prototype = {
30 // chrome.settingsPrivate overrides. 42 // chrome.settingsPrivate overrides.
31 onPrefsChanged: { 43 onPrefsChanged: {
32 addListener: function(listener) { 44 addListener: function(listener) {
33 this.listener_ = listener; 45 this.listener_ = listener;
34 }, 46 },
35 47
36 removeListener: function(listener) { 48 removeListener: function(listener) {
37 expectNotEquals(null, this.listener_); 49 expectNotEquals(null, this.listener_);
38 this.listener_ = null; 50 this.listener_ = null;
39 }, 51 },
40 }, 52 },
41 53
42 getAllPrefs: function(callback) { 54 getAllPrefs: function(callback) {
43 // Send a copy of prefs to keep our internal state private. 55 // Send a copy of prefs to keep our internal state private.
44 var prefs = []; 56 var prefs = [];
45 for (var key in this.prefs) 57 for (var key in this.prefs)
46 prefs.push(this.createCopy_(this.prefs[key])); 58 prefs.push(deepCopy(this.prefs[key]));
47 59
48 setTimeout(callback.bind(null, prefs)); 60 setTimeout(callback.bind(null, prefs));
49 }, 61 },
50 62
51 setPref: function(key, value, pageId, callback) { 63 setPref: function(key, value, pageId, callback) {
52 var pref = this.prefs[key]; 64 var pref = this.prefs[key];
53 assertNotEquals(undefined, pref); 65 assertNotEquals(undefined, pref);
54 assertEquals(typeof value, typeof pref.value); 66 assertEquals(typeof value, typeof pref.value);
55 assertEquals(Array.isArray(value), Array.isArray(pref.value)); 67 assertEquals(Array.isArray(value), Array.isArray(pref.value));
56 68
57 if (this.failNextSetPref_) { 69 if (this.failNextSetPref_) {
58 setTimeout(callback.bind(null, false)); 70 setTimeout(callback.bind(null, false));
59 this.failNextSetPref_ = false; 71 this.failNextSetPref_ = false;
60 return; 72 return;
61 } 73 }
74 assertNotEquals(true, this.disallowSetPref_);
62 75
63 // TODO(michaelpg): support list and dict prefs. 76 var changed = JSON.stringify(pref.value) != JSON.stringify(value);
64 var changed = this.prefs[key].value != value; 77 pref.value = JSON.parse(JSON.stringify(value));
65 this.prefs[key].value = value; 78 setTimeout(callback.bind(null,true));
66 setTimeout(function() { 79 if (changed) {
67 callback(true); 80 setTimeout(function() {
68 // Like chrome.settingsPrivate, send a notification when prefs change. 81 // Like chrome.settingsPrivate, send a notification when prefs change.
69 if (changed) 82 this.sendPrefChanges([key], [deepCopy(value)]);
70 this.sendPrefChanges([key], [value]); 83 }.bind(this));
71 }.bind(this)); 84 }
72 }, 85 },
73 86
74 getPref: function(key, callback) { 87 getPref: function(key, callback) {
75 var pref = this.prefs[key]; 88 var pref = this.prefs[key];
76 assertNotEquals(undefined, pref); 89 assertNotEquals(undefined, pref);
77 90
78 setTimeout(callback.bind(null, this.createCopy_(pref))); 91 var copy = deepCopy(pref);
92 setTimeout(callback.bind(null, copy));
79 }, 93 },
80 94
81 // Functions used by tests. 95 // Functions used by tests.
82 96
83 /** Instructs the API to fail when setPref is next called. */ 97 /** Instructs the API to return a failure when setPref is next called. */
84 failNextSetPref: function() { 98 failNextSetPref: function() {
85 this.failNextSetPref_ = true; 99 this.failNextSetPref_ = true;
86 }, 100 },
87 101
102 /** Instructs the API to assert (fail the test) if setPref is called. */
103 disallowSetPref: function() {
104 this.disallowSetPref_ = true;
105 },
106
107 allowSetPref: function() {
108 this.disallowSetPref_ = false;
109 },
110
88 /** 111 /**
89 * Notifies the listener of pref changes. 112 * Notifies the listener of pref changes.
90 * @param {!Array<string>} keys 113 * @param {!Array<string>} keys
91 * @param {!Array<*>} values 114 * @param {!Array<*>} values
92 */ 115 */
93 sendPrefChanges: function(keys, values) { 116 sendPrefChanges: function(keys, values) {
94 assertNotEquals(null, this.listener_); 117 assertNotEquals(null, this.listener_);
95 118
96 var prefs = []; 119 var prefs = [];
97 for (var i = 0; i < keys.length; i++) { 120 for (var i = 0; i < keys.length; i++) {
98 var key = keys[i]; 121 var key = keys[i];
99 var pref = this.prefs[key]; 122 var pref = this.prefs[key];
123 assertNotEquals(undefined, pref);
100 pref.value = values[i]; 124 pref.value = values[i];
101 prefs.push(this.createCopy_(pref)); 125 prefs.push(deepCopy(pref));
102 } 126 }
103 127
104 setTimeout(this.listener_.bind(null, prefs)); 128 this.listener_(prefs);
129 // setTimeout(this.listener_.bind(null, prefs));
105 }, 130 },
106 131
107 // Private methods for use by the mock API. 132 // Private methods for use by the mock API.
108 133
109 /** 134 /**
110 * @param {!chrome.settingsPrivate.PrefType} type 135 * @param {!chrome.settingsPrivate.PrefType} type
111 * @param {string} key 136 * @param {string} key
112 * @param {*} value 137 * @param {*} value
113 */ 138 */
114 addPref_: function(type, key, value) { 139 addPref_: function(type, key, value) {
(...skipping 22 matching lines...) Expand all
137 162
138 /** 163 /**
139 * @param {string} key 164 * @param {string} key
140 * @param {string} value 165 * @param {string} value
141 */ 166 */
142 addStringPref_: function(key, value) { 167 addStringPref_: function(key, value) {
143 this.addPref_('STRING', key, value); 168 this.addPref_('STRING', key, value);
144 }, 169 },
145 170
146 /** 171 /**
147 * Creates a deep copy of the preference. 172 * @param {string} key
148 * @param {chrome.settingsPrivate.PrefObject} pref 173 * @param {!Array} value
149 * @return {chrome.settingsPrivate.PrefObject}
150 */ 174 */
151 createCopy_: function(pref) { 175 addListPref_: function(key, value) {
152 return JSON.parse(JSON.stringify(pref)); 176 this.addPref_(chrome.settingsPrivate.PrefType.LIST, key, value);
153 }, 177 },
154 }; 178 };
155 179
156 /** 180 /**
157 * Helper function to deal with asynchronicity by repeatedly queueing 181 * Helper function to deal with asynchronicity by repeatedly queueing
158 * tasks to check if the callback returns true. 182 * tasks to check if the callback returns true.
159 * @param {function(): boolean} callback 183 * @param {function(): boolean} callback
160 * @return {Promise} 184 * @return {Promise}
161 */ 185 */
162 function waitUntilTrue(callback) { 186 function waitUntilTrue(callback) {
163 return new Promise(function(resolve, reject) { 187 return new Promise(function(resolve, reject) {
164 var interval = setInterval(function() { 188 var interval = setInterval(function() {
165 if (callback.call()) { 189 if (callback.call()) {
166 clearInterval(interval); 190 clearInterval(interval);
167 resolve(); 191 resolve();
168 } 192 }
169 }); 193 });
170 }); 194 });
171 } 195 }
172 196
197 // Pseudo-random number generator using Fibonacci LFSR.
198 function getPseudoRand() {
199 // Start with any integer in [1, 65535].
200 var lfsr = 3465; // Chromium's melting point (F).
201 var bit;
202 return function() {
203 bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5) ) & 1;
204 lfsr = (lfsr >> 1) | (bit << 15);
205 return lfsr;
206 };
207 }
208
209 function generateList(length, valueType, rand) {
210 var list = [];
211 for (var i = 0; i < length; i++) {
212 if (valueType == Boolean) {
213 list.push(Boolean(rand() % 2));
214 } else if (valueType == Number) {
215 list.push(rand());
216 } else if (valueType == String) {
217 // Allow empty strings.
218 list.push(generateString(rand() % 20, rand));
219 } else if (valueType == Object) {
220 // Don't allow empty objects.
221 list.push(generateObject(rand() % 2 + 1, rand));
222 }
223 }
224
225 return list;
226 };
227
228 function generateString(length, rand) {
229 var s = '';
230 for (var i = 0; i < length; i++)
231 s += String.fromCharCode(65 + rand() % 26);
232 return s;
233 }
234
235 function generateObject(numProps, rand) {
236 var obj = {};
237 var types = [Boolean, Number, String, Array];
238 var typesIndex = 0;
239 for (var i = 0; i < numProps; i++) {
240 var prop;
241 do {
242 prop = generateString(rand() % 20 + 1, rand);
243 } while (obj[prop] != undefined);
244 switch(types[typesIndex++ % types.length]) {
245 case Boolean:
246 obj[prop] = Boolean(rand() % 2);
247 break;
248 case Number:
249 obj[prop] = rand();
250 break;
251 case String:
252 obj[prop] = generateString(rand() % 20, rand);
253 break;
254 case Array:
255 // Generate a list of booleans, numbers or strings.
256 obj[prop] = generateList(rand() % 10, types[rand() % 3], rand);
257 break;
258 }
259 }
260 return obj;
261 }
262 /*
263 var pref = {
264 type: 'LIST',
265 key: key,
266 value: list
267 };
268 */
269
173 function registerTests() { 270 function registerTests() {
174 suite('CrSettingsPrefs', function() { 271 suite('CrSettingsPrefs', function() {
175 /** 272 /**
176 * Prefs instance created before each test. 273 * Prefs instance created before each test.
177 * @type {CrSettingsPrefs} 274 * @type {CrSettingsPrefs}
178 */ 275 */
179 var prefs; 276 var prefs;
180 277
181 /** @type {MockSettingsApi} */ 278 /** @type {MockSettingsApi} */
182 var mockApi = null; 279 var mockApi = null;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 // We've already registered an error, so skip the pref. 319 // We've already registered an error, so skip the pref.
223 skipPref = true; 320 skipPref = true;
224 break; 321 break;
225 } 322 }
226 } 323 }
227 if (skipPref) 324 if (skipPref)
228 continue; 325 continue;
229 326
230 expectEquals(expectedPref.key, actualPref.key); 327 expectEquals(expectedPref.key, actualPref.key);
231 expectEquals(expectedPref.type, actualPref.type); 328 expectEquals(expectedPref.type, actualPref.type);
232 if (expectedPref.type != chrome.settingsPrivate.LIST) { 329 if (expectedPref.type != chrome.settingsPrivate.PrefType.LIST) {
233 expectEquals(expectedPref.value, actualPref.value); 330 expectEquals(expectedPref.value, actualPref.value);
234 } else { 331 } else {
235 expectEquals(JSON.stringify(expectedPref.value), 332 expectEquals(JSON.stringify(expectedPref.value),
236 JSON.stringify(actualPref.value)); 333 JSON.stringify(actualPref.value));
237 } 334 }
238 } 335 }
239 }); 336 });
240 337
241 test('forwards pref changes to API', function() { 338 test('forwards pref changes to API', function(done) {
339 var rand = getPseudoRand();
340 rand();rand();
341
242 // Test that cr-settings-prefs uses the setPref API. 342 // Test that cr-settings-prefs uses the setPref API.
243 prefs.set('prefs.browser.enable_flash.value', true); 343 prefs.set('prefs.browser.enable_flash.value', true);
244 expectTrue(mockApi.prefs['browser.enable_flash'].value); 344 expectTrue(mockApi.prefs['browser.enable_flash'].value);
245 prefs.set('prefs.device.overclock.value', 1.2); 345 prefs.set('prefs.device.overclock.value', 1.2);
246 expectEquals(1.2, mockApi.prefs['device.overclock'].value); 346 expectEquals(1.2, mockApi.prefs['device.overclock'].value);
247 prefs.set('prefs.homepage.value', 'chromium.org'); 347 prefs.set('prefs.homepage.value', 'chromium.org');
248 expectEquals('chromium.org', mockApi.prefs['homepage'].value); 348 expectEquals('chromium.org', mockApi.prefs['homepage'].value);
349 var list = generateList(10, String, rand);
350 prefs.set('prefs.languages.value', deepCopy(list));
351 expectEquals(JSON.stringify(list),
352 JSON.stringify(mockApi.prefs['languages'].value));
353 var newList = deepCopy(mockApi.prefs['content.sites'].value);
354 var dict = newList[1];
355 var prop = Object.keys(dict)[0];
356 // Set a property (and append _ to ensure the value is different)
357 dict[prop] = generateString(10, rand) + '_';
358 // var listOfDicts = generateList(3, Object, rand);
359 prefs.set('prefs.content.sites.value', deepCopy(newList));
360 expectEquals(JSON.stringify(newList),
361 JSON.stringify(mockApi.prefs['content.sites'].value));
362 PolymerTest.async(function() {
363 mockApi.disallowSetPref();
364 prefs.set('prefs.content.sites.value', deepCopy(newList));
365 mockApi.allowSetPref();
249 366
250 // Test that when setPref fails, the pref is reverted locally. 367 // Test that when setPref fails, the pref is reverted locally.
251 mockApi.failNextSetPref(); 368 mockApi.failNextSetPref();
252 prefs.set('prefs.browser.enable_flash.value', false); 369 prefs.set('prefs.browser.enable_flash.value', false);
253 mockApi.failNextSetPref(); 370 mockApi.failNextSetPref();
254 prefs.set('prefs.device.overclock.value', 2.0); 371 prefs.set('prefs.device.overclock.value', 2.0);
255 mockApi.failNextSetPref(); 372 mockApi.failNextSetPref();
256 prefs.set('prefs.homepage.value', 'invalid-url'); 373 prefs.set('prefs.homepage.value', 'invalid-url');
374 mockApi.failNextSetPref();
375 prefs.set('prefs.languages.value', generateList(10, String, rand));
376 mockApi.failNextSetPref();
377 prefs.set('prefs.content.sites.value', generateList(3, Object, rand));
378 }).async(function() {
257 379
258 // Multiple asynchronous round trips need to happen. 380 // Multiple asynchronous round trips need to happen.
259 return Promise.all([ 381 Promise.all([
260 waitUntilTrue(function() { 382 waitUntilTrue(function() {
261 return prefs.prefs.browser.enable_flash.value; 383 return prefs.prefs.browser.enable_flash.value;
262 }), 384 }),
263 waitUntilTrue(function() { 385 waitUntilTrue(function() {
264 return prefs.prefs.device.overclock.value == 1.2; 386 return prefs.prefs.device.overclock.value == 1.2;
265 }), 387 }),
266 waitUntilTrue(function() { 388 waitUntilTrue(function() {
267 return prefs.prefs.homepage.value == 'chromium.org'; 389 return prefs.prefs.homepage.value == 'chromium.org';
268 }), 390 }),
269 ]); 391 waitUntilTrue(function() {
392 return JSON.stringify(prefs.prefs.languages.value) ==
393 JSON.stringify(list);
394 }),
395 waitUntilTrue(function() {
396 return JSON.stringify(prefs.prefs.content.sites.value) ==
397 JSON.stringify(newList);
398 }),
399 ]).then(function() {
400 done();
401 });
402 });
270 }); 403 });
271 404
272 test('responds to API changes', function(done) { 405 test('responds to API changes', function(done) {
273 mockApi.sendPrefChanges(['top_level_pref'], [false]); 406 var rand = getPseudoRand();
407
408 var topLevelPref = false;
409 var contentSites = generateList(2, Object, rand);
410 contentSites = [{E: 'e', F: 'ff'}, {G: true, H: false}];
411
412 mockApi.disallowSetPref();
274 PolymerTest.async(function() { 413 PolymerTest.async(function() {
275 expectEquals(false, prefs.prefs.top_level_pref.value); 414 mockApi.sendPrefChanges(['top_level_pref', 'content.sites'],
415 [topLevelPref, deepCopy(contentSites)]);
416 }).async(function() {
417 expectEquals(topLevelPref, prefs.prefs.top_level_pref.value);
418 expectEquals(JSON.stringify(contentSites),
419 JSON.stringify(prefs.prefs.content.sites.value));
276 420
277 mockApi.sendPrefChanges(['top_level_pref'], [true]); 421 topLevelPref = true;
422 var oldContentSites = contentSites;
423 contentSites = deepCopy(contentSites);
424 var dict = contentSites[1];
425 var prop = Object.keys(contentSites[1])[0];
426 // Set a property (and append _ to ensure the value is different)
427 dict[prop] = generateString(10, rand) + '_';
428 mockApi.sendPrefChanges(['top_level_pref', 'content.sites'],
429 [topLevelPref, deepCopy(contentSites)]);
278 }).async(function() { 430 }).async(function() {
279 expectEquals(true, prefs.prefs.top_level_pref.value); 431 expectEquals(topLevelPref, prefs.prefs.top_level_pref.value);
280 done(); 432 expectEquals(JSON.stringify(contentSites),
281 }); 433 JSON.stringify(prefs.prefs.content.sites.value));
434 }).async(function() {
435 // Shouldn't respond to non-changes.
436 mockApi.disallowSetPref();
437 mockApi.sendPrefChanges(['top_level_pref', 'content.sites'],
438 [topLevelPref, deepCopy(contentSites)]);
439 }).async(function() {}
440 ).async(done);
441 });
442 });
443
444 suite('pseudo-random number generator', function() {
445 test('generates pseudo-random numbers', function() {
446 var numbers = {};
447 var rand1 = getPseudoRand();
448 var rand2 = getPseudoRand();
449 for (var i = 0; i < 1000; i++) {
450 var num1 = rand1();
451 var num2 = rand2();
452 // Generators should have the same seed and algorithm.
453 expectEquals(num1, num2);
454 // Generator shouldn't cycle in the first few numbers.
455 expectEquals(undefined, numbers[num1]);
456 numbers[num1] = true;
457 }
458 });
459 });
460
461 suite('list generator', function() {
462 var rand;
463
464 function checkList(length, type) {
465 var list = generateList(length, type, rand);
466 expectEquals(length, list.length);
467 for (var i = 0; i < length; i++)
468 expectEquals(typeof list[i], type.name.toLowerCase());
469 return list;
470 }
471
472 setup(function() {
473 rand = getPseudoRand();
474 });
475
476 test('flat lists', function() {
477 checkList(10, Boolean);
478 checkList(20, Number);
479 checkList(5, String);
480 });
481
482 test('object lists', function() {
483 var list = checkList(40, Object);
484 for (var i = 0; i < 40; i++)
485 expectGT(Object.keys(list[i]).length, 0);
282 }); 486 });
283 }); 487 });
284 } 488 }
285 489
286 return { 490 return {
287 registerTests: registerTests, 491 registerTests: registerTests,
288 }; 492 };
289 }); 493 });
OLDNEW
« no previous file with comments | « chrome/test/data/webui/polymer_browser_test_base.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698