1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
const IS_ANDROID = AppConstants.platform == "android";
add_task(async function test_canonicaljson() {
const records = [
{ id: "1", title: "title 1" },
{ id: "2", title: "title 2" },
];
const timestamp = 42;
const serialized = await RemoteSettingsWorker.canonicalStringify(
records,
timestamp
);
Assert.equal(
serialized,
'{"data":[{"id":"1","title":"title 1"},{"id":"2","title":"title 2"}],"last_modified":"42"}'
);
});
add_task(async function test_import_json_dump_into_idb() {
if (IS_ANDROID) {
// Skip test: we don't ship remote settings dumps on Android (see package-manifest).
return;
}
const client = new RemoteSettingsClient("language-dictionaries");
const before = await client.get({ syncIfEmpty: false });
Assert.equal(before.length, 0);
await RemoteSettingsWorker.importJSONDump("main", "language-dictionaries");
const after = await client.get({ syncIfEmpty: false });
Assert.ok(!!after.length);
let lastModifiedStamp = await client.getLastModified();
Assert.equal(
lastModifiedStamp,
Math.max(...after.map(record => record.last_modified)),
"Should have correct last modified timestamp"
);
// Force a DB close for shutdown so we can delete the DB later.
Database._shutdownHandler();
});
add_task(async function test_throws_error_if_worker_fails() {
let error;
try {
await RemoteSettingsWorker.canonicalStringify(null, 42);
} catch (e) {
error = e;
}
Assert.equal(error.message.endsWith("records is null"), true);
});
add_task(async function test_throws_error_if_worker_fails_async() {
if (IS_ANDROID) {
// Skip test: we don't ship dump, so importJSONDump() is no-op.
return;
}
// Delete the Remote Settings database, and try to import a dump.
// This is not supported, and the error thrown asynchronously in the worker
// should be reported to the caller.
await new Promise((resolve, reject) => {
const request = indexedDB.deleteDatabase("remote-settings");
request.onsuccess = () => resolve();
request.onblocked = () => reject(new Error("Cannot delete DB"));
request.onerror = event => reject(event.target.error);
});
let error;
try {
await RemoteSettingsWorker.importJSONDump("main", "language-dictionaries");
} catch (e) {
error = e;
}
Assert.ok(/IndexedDB: Error accessing remote-settings/.test(error.message));
});
add_task(async function test_throws_error_if_worker_crashes() {
// This simulates a crash at the worker level (not within a promise).
let error;
try {
await RemoteSettingsWorker._execute("unknown_method");
} catch (e) {
error = e;
}
Assert.equal(error.message, "TypeError: Agent[method] is not a function");
});
add_task(async function test_stops_worker_after_timeout() {
// Change the idle time.
Services.prefs.setIntPref(
"services.settings.worker_idle_max_milliseconds",
1
);
// Run a task:
let serialized = await RemoteSettingsWorker.canonicalStringify([], 42);
Assert.equal(serialized, '{"data":[],"last_modified":"42"}', "API works.");
// Check that the worker gets stopped now the task is done:
await TestUtils.waitForCondition(() => !RemoteSettingsWorker.worker);
// Ensure the worker stays alive for 10 minutes instead:
Services.prefs.setIntPref(
"services.settings.worker_idle_max_milliseconds",
600000
);
// Run another task:
serialized = await RemoteSettingsWorker.canonicalStringify([], 42);
Assert.equal(
serialized,
'{"data":[],"last_modified":"42"}',
"API still works."
);
Assert.ok(RemoteSettingsWorker.worker, "Worker should stay alive a bit.");
// Clear the pref.
Services.prefs.clearUserPref(
"services.settings.worker_idle_max_milliseconds"
);
});
|