|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 const file = require("sdk/io/file"); |
|
6 const prefs = require("sdk/preferences/service"); |
|
7 |
|
8 const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; |
|
9 const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod"; |
|
10 |
|
11 let {Cc,Ci} = require("chrome"); |
|
12 |
|
13 const { Loader } = require("sdk/test/loader"); |
|
14 const { id } = require("sdk/self"); |
|
15 |
|
16 let storeFile = Cc["@mozilla.org/file/directory_service;1"]. |
|
17 getService(Ci.nsIProperties). |
|
18 get("ProfD", Ci.nsIFile); |
|
19 storeFile.append("jetpack"); |
|
20 storeFile.append(id); |
|
21 storeFile.append("simple-storage"); |
|
22 file.mkpath(storeFile.path); |
|
23 storeFile.append("store.json"); |
|
24 let storeFilename = storeFile.path; |
|
25 |
|
26 function manager(loader) loader.sandbox("sdk/simple-storage").manager; |
|
27 |
|
28 exports.testSetGet = function (assert, done) { |
|
29 // Load the module once, set a value. |
|
30 let loader = Loader(module); |
|
31 let ss = loader.require("sdk/simple-storage"); |
|
32 manager(loader).jsonStore.onWrite = function (storage) { |
|
33 assert.ok(file.exists(storeFilename), "Store file should exist"); |
|
34 |
|
35 // Load the module again and make sure the value stuck. |
|
36 loader = Loader(module); |
|
37 ss = loader.require("sdk/simple-storage"); |
|
38 assert.equal(ss.storage.foo, val, "Value should persist"); |
|
39 manager(loader).jsonStore.onWrite = function (storage) { |
|
40 assert.fail("Nothing should be written since `storage` was not changed."); |
|
41 }; |
|
42 loader.unload(); |
|
43 file.remove(storeFilename); |
|
44 done(); |
|
45 }; |
|
46 let val = "foo"; |
|
47 ss.storage.foo = val; |
|
48 assert.equal(ss.storage.foo, val, "Value read should be value set"); |
|
49 loader.unload(); |
|
50 }; |
|
51 |
|
52 exports.testSetGetRootArray = function (assert, done) { |
|
53 setGetRoot(assert, done, [1, 2, 3], function (arr1, arr2) { |
|
54 if (arr1.length !== arr2.length) |
|
55 return false; |
|
56 for (let i = 0; i < arr1.length; i++) { |
|
57 if (arr1[i] !== arr2[i]) |
|
58 return false; |
|
59 } |
|
60 return true; |
|
61 }); |
|
62 }; |
|
63 |
|
64 exports.testSetGetRootBool = function (assert, done) { |
|
65 setGetRoot(assert, done, true); |
|
66 }; |
|
67 |
|
68 exports.testSetGetRootFunction = function (assert, done) { |
|
69 setGetRootError(assert, done, function () {}, |
|
70 "Setting storage to a function should fail"); |
|
71 }; |
|
72 |
|
73 exports.testSetGetRootNull = function (assert, done) { |
|
74 setGetRoot(assert, done, null); |
|
75 }; |
|
76 |
|
77 exports.testSetGetRootNumber = function (assert, done) { |
|
78 setGetRoot(assert, done, 3.14); |
|
79 }; |
|
80 |
|
81 exports.testSetGetRootObject = function (assert, done) { |
|
82 setGetRoot(assert, done, { foo: 1, bar: 2 }, function (obj1, obj2) { |
|
83 for (let prop in obj1) { |
|
84 if (!(prop in obj2) || obj2[prop] !== obj1[prop]) |
|
85 return false; |
|
86 } |
|
87 for (let prop in obj2) { |
|
88 if (!(prop in obj1) || obj1[prop] !== obj2[prop]) |
|
89 return false; |
|
90 } |
|
91 return true; |
|
92 }); |
|
93 }; |
|
94 |
|
95 exports.testSetGetRootString = function (assert, done) { |
|
96 setGetRoot(assert, done, "sho' 'nuff"); |
|
97 }; |
|
98 |
|
99 exports.testSetGetRootUndefined = function (assert, done) { |
|
100 setGetRootError(assert, done, undefined, "Setting storage to undefined should fail"); |
|
101 }; |
|
102 |
|
103 exports.testEmpty = function (assert) { |
|
104 let loader = Loader(module); |
|
105 let ss = loader.require("sdk/simple-storage"); |
|
106 loader.unload(); |
|
107 assert.ok(!file.exists(storeFilename), "Store file should not exist"); |
|
108 }; |
|
109 |
|
110 exports.testStorageDataRecovery = function(assert) { |
|
111 const data = { |
|
112 a: true, |
|
113 b: [3, 13], |
|
114 c: "guilty!", |
|
115 d: { e: 1, f: 2 } |
|
116 }; |
|
117 let stream = file.open(storeFilename, "w"); |
|
118 stream.write(JSON.stringify(data)); |
|
119 stream.close(); |
|
120 let loader = Loader(module); |
|
121 let ss = loader.require("sdk/simple-storage"); |
|
122 assert.deepEqual(ss.storage, data, "Recovered data should be the same as written"); |
|
123 file.remove(storeFilename); |
|
124 loader.unload(); |
|
125 } |
|
126 |
|
127 exports.testMalformed = function (assert) { |
|
128 let stream = file.open(storeFilename, "w"); |
|
129 stream.write("i'm not json"); |
|
130 stream.close(); |
|
131 let loader = Loader(module); |
|
132 let ss = loader.require("sdk/simple-storage"); |
|
133 let empty = true; |
|
134 for (let key in ss.storage) { |
|
135 empty = false; |
|
136 break; |
|
137 } |
|
138 assert.ok(empty, "Malformed storage should cause root to be empty"); |
|
139 file.remove(storeFilename); |
|
140 loader.unload(); |
|
141 }; |
|
142 |
|
143 // Go over quota and handle it by listener. |
|
144 exports.testQuotaExceededHandle = function (assert, done) { |
|
145 prefs.set(QUOTA_PREF, 18); |
|
146 |
|
147 let loader = Loader(module); |
|
148 let ss = loader.require("sdk/simple-storage"); |
|
149 ss.on("OverQuota", function () { |
|
150 assert.pass("OverQuota was emitted as expected"); |
|
151 assert.equal(this, ss, "`this` should be simple storage"); |
|
152 ss.storage = { x: 4, y: 5 }; |
|
153 |
|
154 manager(loader).jsonStore.onWrite = function () { |
|
155 loader = Loader(module); |
|
156 ss = loader.require("sdk/simple-storage"); |
|
157 let numProps = 0; |
|
158 for (let prop in ss.storage) |
|
159 numProps++; |
|
160 assert.ok(numProps, 2, |
|
161 "Store should contain 2 values: " + ss.storage.toSource()); |
|
162 assert.equal(ss.storage.x, 4, "x value should be correct"); |
|
163 assert.equal(ss.storage.y, 5, "y value should be correct"); |
|
164 manager(loader).jsonStore.onWrite = function (storage) { |
|
165 assert.fail("Nothing should be written since `storage` was not changed."); |
|
166 }; |
|
167 loader.unload(); |
|
168 prefs.reset(QUOTA_PREF); |
|
169 done(); |
|
170 }; |
|
171 loader.unload(); |
|
172 }); |
|
173 // This will be JSON.stringify()ed to: {"a":1,"b":2,"c":3} (19 bytes) |
|
174 ss.storage = { a: 1, b: 2, c: 3 }; |
|
175 manager(loader).jsonStore.write(); |
|
176 }; |
|
177 |
|
178 // Go over quota but don't handle it. The last good state should still persist. |
|
179 exports.testQuotaExceededNoHandle = function (assert, done) { |
|
180 prefs.set(QUOTA_PREF, 5); |
|
181 |
|
182 let loader = Loader(module); |
|
183 let ss = loader.require("sdk/simple-storage"); |
|
184 |
|
185 manager(loader).jsonStore.onWrite = function (storage) { |
|
186 loader = Loader(module); |
|
187 ss = loader.require("sdk/simple-storage"); |
|
188 assert.equal(ss.storage, val, |
|
189 "Value should have persisted: " + ss.storage); |
|
190 ss.storage = "some very long string that is very long"; |
|
191 ss.on("OverQuota", function () { |
|
192 assert.pass("OverQuota emitted as expected"); |
|
193 manager(loader).jsonStore.onWrite = function () { |
|
194 assert.fail("Over-quota value should not have been written"); |
|
195 }; |
|
196 loader.unload(); |
|
197 |
|
198 loader = Loader(module); |
|
199 ss = loader.require("sdk/simple-storage"); |
|
200 assert.equal(ss.storage, val, |
|
201 "Over-quota value should not have been written, " + |
|
202 "old value should have persisted: " + ss.storage); |
|
203 manager(loader).jsonStore.onWrite = function (storage) { |
|
204 assert.fail("Nothing should be written since `storage` was not changed."); |
|
205 }; |
|
206 loader.unload(); |
|
207 prefs.reset(QUOTA_PREF); |
|
208 done(); |
|
209 }); |
|
210 manager(loader).jsonStore.write(); |
|
211 }; |
|
212 |
|
213 let val = "foo"; |
|
214 ss.storage = val; |
|
215 loader.unload(); |
|
216 }; |
|
217 |
|
218 exports.testQuotaUsage = function (assert, done) { |
|
219 let quota = 21; |
|
220 prefs.set(QUOTA_PREF, quota); |
|
221 |
|
222 let loader = Loader(module); |
|
223 let ss = loader.require("sdk/simple-storage"); |
|
224 |
|
225 // {"a":1} (7 bytes) |
|
226 ss.storage = { a: 1 }; |
|
227 assert.equal(ss.quotaUsage, 7 / quota, "quotaUsage should be correct"); |
|
228 |
|
229 // {"a":1,"bb":2} (14 bytes) |
|
230 ss.storage = { a: 1, bb: 2 }; |
|
231 assert.equal(ss.quotaUsage, 14 / quota, "quotaUsage should be correct"); |
|
232 |
|
233 // {"a":1,"bb":2,"cc":3} (21 bytes) |
|
234 ss.storage = { a: 1, bb: 2, cc: 3 }; |
|
235 assert.equal(ss.quotaUsage, 21 / quota, "quotaUsage should be correct"); |
|
236 |
|
237 manager(loader).jsonStore.onWrite = function () { |
|
238 prefs.reset(QUOTA_PREF); |
|
239 done(); |
|
240 }; |
|
241 loader.unload(); |
|
242 }; |
|
243 |
|
244 exports.testUninstall = function (assert, done) { |
|
245 let loader = Loader(module); |
|
246 let ss = loader.require("sdk/simple-storage"); |
|
247 manager(loader).jsonStore.onWrite = function () { |
|
248 assert.ok(file.exists(storeFilename), "Store file should exist"); |
|
249 |
|
250 loader = Loader(module); |
|
251 ss = loader.require("sdk/simple-storage"); |
|
252 loader.unload("uninstall"); |
|
253 assert.ok(!file.exists(storeFilename), "Store file should be removed"); |
|
254 done(); |
|
255 }; |
|
256 ss.storage.foo = "foo"; |
|
257 loader.unload(); |
|
258 }; |
|
259 |
|
260 exports.testChangeInnerArray = function(assert, done) { |
|
261 prefs.set(WRITE_PERIOD_PREF, 10); |
|
262 |
|
263 let expected = { |
|
264 x: [5, 7], |
|
265 y: [7, 28], |
|
266 z: [6, 2] |
|
267 }; |
|
268 |
|
269 // Load the module, set a value. |
|
270 let loader = Loader(module); |
|
271 let ss = loader.require("sdk/simple-storage"); |
|
272 manager(loader).jsonStore.onWrite = function (storage) { |
|
273 assert.ok(file.exists(storeFilename), "Store file should exist"); |
|
274 |
|
275 // Load the module again and check the result |
|
276 loader = Loader(module); |
|
277 ss = loader.require("sdk/simple-storage"); |
|
278 assert.equal(JSON.stringify(ss.storage), |
|
279 JSON.stringify(expected), "Should see the expected object"); |
|
280 |
|
281 // Add a property |
|
282 ss.storage.x.push(["bar"]); |
|
283 expected.x.push(["bar"]); |
|
284 manager(loader).jsonStore.onWrite = function (storage) { |
|
285 assert.equal(JSON.stringify(ss.storage), |
|
286 JSON.stringify(expected), "Should see the expected object"); |
|
287 |
|
288 // Modify a property |
|
289 ss.storage.y[0] = 42; |
|
290 expected.y[0] = 42; |
|
291 manager(loader).jsonStore.onWrite = function (storage) { |
|
292 assert.equal(JSON.stringify(ss.storage), |
|
293 JSON.stringify(expected), "Should see the expected object"); |
|
294 |
|
295 // Delete a property |
|
296 delete ss.storage.z[1]; |
|
297 delete expected.z[1]; |
|
298 manager(loader).jsonStore.onWrite = function (storage) { |
|
299 assert.equal(JSON.stringify(ss.storage), |
|
300 JSON.stringify(expected), "Should see the expected object"); |
|
301 |
|
302 // Modify the new inner-object |
|
303 ss.storage.x[2][0] = "baz"; |
|
304 expected.x[2][0] = "baz"; |
|
305 manager(loader).jsonStore.onWrite = function (storage) { |
|
306 assert.equal(JSON.stringify(ss.storage), |
|
307 JSON.stringify(expected), "Should see the expected object"); |
|
308 |
|
309 manager(loader).jsonStore.onWrite = function (storage) { |
|
310 assert.fail("Nothing should be written since `storage` was not changed."); |
|
311 }; |
|
312 loader.unload(); |
|
313 |
|
314 // Load the module again and check the result |
|
315 loader = Loader(module); |
|
316 ss = loader.require("sdk/simple-storage"); |
|
317 assert.equal(JSON.stringify(ss.storage), |
|
318 JSON.stringify(expected), "Should see the expected object"); |
|
319 loader.unload(); |
|
320 file.remove(storeFilename); |
|
321 prefs.reset(WRITE_PERIOD_PREF); |
|
322 done(); |
|
323 }; |
|
324 }; |
|
325 }; |
|
326 }; |
|
327 }; |
|
328 |
|
329 ss.storage = { |
|
330 x: [5, 7], |
|
331 y: [7, 28], |
|
332 z: [6, 2] |
|
333 }; |
|
334 assert.equal(JSON.stringify(ss.storage), |
|
335 JSON.stringify(expected), "Should see the expected object"); |
|
336 |
|
337 loader.unload(); |
|
338 }; |
|
339 |
|
340 exports.testChangeInnerObject = function(assert, done) { |
|
341 prefs.set(WRITE_PERIOD_PREF, 10); |
|
342 |
|
343 let expected = { |
|
344 x: { |
|
345 a: 5, |
|
346 b: 7 |
|
347 }, |
|
348 y: { |
|
349 c: 7, |
|
350 d: 28 |
|
351 }, |
|
352 z: { |
|
353 e: 6, |
|
354 f: 2 |
|
355 } |
|
356 }; |
|
357 |
|
358 // Load the module, set a value. |
|
359 let loader = Loader(module); |
|
360 let ss = loader.require("sdk/simple-storage"); |
|
361 manager(loader).jsonStore.onWrite = function (storage) { |
|
362 assert.ok(file.exists(storeFilename), "Store file should exist"); |
|
363 |
|
364 // Load the module again and check the result |
|
365 loader = Loader(module); |
|
366 ss = loader.require("sdk/simple-storage"); |
|
367 assert.equal(JSON.stringify(ss.storage), |
|
368 JSON.stringify(expected), "Should see the expected object"); |
|
369 |
|
370 // Add a property |
|
371 ss.storage.x.g = {foo: "bar"}; |
|
372 expected.x.g = {foo: "bar"}; |
|
373 manager(loader).jsonStore.onWrite = function (storage) { |
|
374 assert.equal(JSON.stringify(ss.storage), |
|
375 JSON.stringify(expected), "Should see the expected object"); |
|
376 |
|
377 // Modify a property |
|
378 ss.storage.y.c = 42; |
|
379 expected.y.c = 42; |
|
380 manager(loader).jsonStore.onWrite = function (storage) { |
|
381 assert.equal(JSON.stringify(ss.storage), |
|
382 JSON.stringify(expected), "Should see the expected object"); |
|
383 |
|
384 // Delete a property |
|
385 delete ss.storage.z.f; |
|
386 delete expected.z.f; |
|
387 manager(loader).jsonStore.onWrite = function (storage) { |
|
388 assert.equal(JSON.stringify(ss.storage), |
|
389 JSON.stringify(expected), "Should see the expected object"); |
|
390 |
|
391 // Modify the new inner-object |
|
392 ss.storage.x.g.foo = "baz"; |
|
393 expected.x.g.foo = "baz"; |
|
394 manager(loader).jsonStore.onWrite = function (storage) { |
|
395 assert.equal(JSON.stringify(ss.storage), |
|
396 JSON.stringify(expected), "Should see the expected object"); |
|
397 |
|
398 manager(loader).jsonStore.onWrite = function (storage) { |
|
399 assert.fail("Nothing should be written since `storage` was not changed."); |
|
400 }; |
|
401 loader.unload(); |
|
402 |
|
403 // Load the module again and check the result |
|
404 loader = Loader(module); |
|
405 ss = loader.require("sdk/simple-storage"); |
|
406 assert.equal(JSON.stringify(ss.storage), |
|
407 JSON.stringify(expected), "Should see the expected object"); |
|
408 loader.unload(); |
|
409 file.remove(storeFilename); |
|
410 prefs.reset(WRITE_PERIOD_PREF); |
|
411 done(); |
|
412 }; |
|
413 }; |
|
414 }; |
|
415 }; |
|
416 }; |
|
417 |
|
418 ss.storage = { |
|
419 x: { |
|
420 a: 5, |
|
421 b: 7 |
|
422 }, |
|
423 y: { |
|
424 c: 7, |
|
425 d: 28 |
|
426 }, |
|
427 z: { |
|
428 e: 6, |
|
429 f: 2 |
|
430 } |
|
431 }; |
|
432 assert.equal(JSON.stringify(ss.storage), |
|
433 JSON.stringify(expected), "Should see the expected object"); |
|
434 |
|
435 loader.unload(); |
|
436 }; |
|
437 |
|
438 exports.testSetNoSetRead = function (assert, done) { |
|
439 // Load the module, set a value. |
|
440 let loader = Loader(module); |
|
441 let ss = loader.require("sdk/simple-storage"); |
|
442 manager(loader).jsonStore.onWrite = function (storage) { |
|
443 assert.ok(file.exists(storeFilename), "Store file should exist"); |
|
444 |
|
445 // Load the module again but don't access ss.storage. |
|
446 loader = Loader(module); |
|
447 ss = loader.require("sdk/simple-storage"); |
|
448 manager(loader).jsonStore.onWrite = function (storage) { |
|
449 assert.fail("Nothing should be written since `storage` was not accessed."); |
|
450 }; |
|
451 loader.unload(); |
|
452 |
|
453 // Load the module a third time and make sure the value stuck. |
|
454 loader = Loader(module); |
|
455 ss = loader.require("sdk/simple-storage"); |
|
456 assert.equal(ss.storage.foo, val, "Value should persist"); |
|
457 manager(loader).jsonStore.onWrite = function (storage) { |
|
458 assert.fail("Nothing should be written since `storage` was not changed."); |
|
459 }; |
|
460 loader.unload(); |
|
461 file.remove(storeFilename); |
|
462 done(); |
|
463 }; |
|
464 let val = "foo"; |
|
465 ss.storage.foo = val; |
|
466 assert.equal(ss.storage.foo, val, "Value read should be value set"); |
|
467 loader.unload(); |
|
468 }; |
|
469 |
|
470 |
|
471 function setGetRoot(assert, done, val, compare) { |
|
472 compare = compare || function (a, b) a === b; |
|
473 |
|
474 // Load the module once, set a value. |
|
475 let loader = Loader(module); |
|
476 let ss = loader.require("sdk/simple-storage"); |
|
477 manager(loader).jsonStore.onWrite = function () { |
|
478 assert.ok(file.exists(storeFilename), "Store file should exist"); |
|
479 |
|
480 // Load the module again and make sure the value stuck. |
|
481 loader = Loader(module); |
|
482 ss = loader.require("sdk/simple-storage"); |
|
483 assert.ok(compare(ss.storage, val), "Value should persist"); |
|
484 manager(loader).jsonStore.onWrite = function (storage) { |
|
485 assert.fail("Nothing should be written since `storage` was not changed."); |
|
486 }; |
|
487 loader.unload(); |
|
488 file.remove(storeFilename); |
|
489 done(); |
|
490 }; |
|
491 ss.storage = val; |
|
492 assert.ok(compare(ss.storage, val), "Value read should be value set"); |
|
493 loader.unload(); |
|
494 } |
|
495 |
|
496 function setGetRootError(assert, done, val, msg) { |
|
497 let pred = new RegExp("storage must be one of the following types: " + |
|
498 "array, boolean, null, number, object, string"); |
|
499 let loader = Loader(module); |
|
500 let ss = loader.require("sdk/simple-storage"); |
|
501 assert.throws(function () ss.storage = val, pred, msg); |
|
502 done(); |
|
503 loader.unload(); |
|
504 } |
|
505 |
|
506 require('sdk/test').run(exports); |