|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* Any copyright is dedicated to the Public Domain. |
|
4 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
5 |
|
6 /** |
|
7 * Tests the DownloadStore object. |
|
8 */ |
|
9 |
|
10 "use strict"; |
|
11 |
|
12 //////////////////////////////////////////////////////////////////////////////// |
|
13 //// Globals |
|
14 |
|
15 XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore", |
|
16 "resource://gre/modules/DownloadStore.jsm"); |
|
17 XPCOMUtils.defineLazyModuleGetter(this, "OS", |
|
18 "resource://gre/modules/osfile.jsm") |
|
19 |
|
20 /** |
|
21 * Returns a new DownloadList object with an associated DownloadStore. |
|
22 * |
|
23 * @param aStorePath |
|
24 * String pointing to the file to be associated with the DownloadStore, |
|
25 * or undefined to use a non-existing temporary file. In this case, the |
|
26 * temporary file is deleted when the test file execution finishes. |
|
27 * |
|
28 * @return {Promise} |
|
29 * @resolves Array [ Newly created DownloadList , associated DownloadStore ]. |
|
30 * @rejects JavaScript exception. |
|
31 */ |
|
32 function promiseNewListAndStore(aStorePath) |
|
33 { |
|
34 return promiseNewList().then(function (aList) { |
|
35 let path = aStorePath || getTempFile(TEST_STORE_FILE_NAME).path; |
|
36 let store = new DownloadStore(aList, path); |
|
37 return [aList, store]; |
|
38 }); |
|
39 } |
|
40 |
|
41 //////////////////////////////////////////////////////////////////////////////// |
|
42 //// Tests |
|
43 |
|
44 /** |
|
45 * Saves downloads to a file, then reloads them. |
|
46 */ |
|
47 add_task(function test_save_reload() |
|
48 { |
|
49 let [listForSave, storeForSave] = yield promiseNewListAndStore(); |
|
50 let [listForLoad, storeForLoad] = yield promiseNewListAndStore( |
|
51 storeForSave.path); |
|
52 |
|
53 listForSave.add(yield promiseNewDownload(httpUrl("source.txt"))); |
|
54 listForSave.add(yield Downloads.createDownload({ |
|
55 source: { url: httpUrl("empty.txt"), |
|
56 referrer: TEST_REFERRER_URL }, |
|
57 target: getTempFile(TEST_TARGET_FILE_NAME), |
|
58 })); |
|
59 |
|
60 let legacyDownload = yield promiseStartLegacyDownload(); |
|
61 yield legacyDownload.cancel(); |
|
62 listForSave.add(legacyDownload); |
|
63 |
|
64 yield storeForSave.save(); |
|
65 yield storeForLoad.load(); |
|
66 |
|
67 let itemsForSave = yield listForSave.getAll(); |
|
68 let itemsForLoad = yield listForLoad.getAll(); |
|
69 |
|
70 do_check_eq(itemsForSave.length, itemsForLoad.length); |
|
71 |
|
72 // Downloads should be reloaded in the same order. |
|
73 for (let i = 0; i < itemsForSave.length; i++) { |
|
74 // The reloaded downloads are different objects. |
|
75 do_check_neq(itemsForSave[i], itemsForLoad[i]); |
|
76 |
|
77 // The reloaded downloads have the same properties. |
|
78 do_check_eq(itemsForSave[i].source.url, |
|
79 itemsForLoad[i].source.url); |
|
80 do_check_eq(itemsForSave[i].source.referrer, |
|
81 itemsForLoad[i].source.referrer); |
|
82 do_check_eq(itemsForSave[i].target.path, |
|
83 itemsForLoad[i].target.path); |
|
84 do_check_eq(itemsForSave[i].saver.toSerializable(), |
|
85 itemsForLoad[i].saver.toSerializable()); |
|
86 } |
|
87 }); |
|
88 |
|
89 /** |
|
90 * Checks that saving an empty list deletes any existing file. |
|
91 */ |
|
92 add_task(function test_save_empty() |
|
93 { |
|
94 let [list, store] = yield promiseNewListAndStore(); |
|
95 |
|
96 let createdFile = yield OS.File.open(store.path, { create: true }); |
|
97 yield createdFile.close(); |
|
98 |
|
99 yield store.save(); |
|
100 |
|
101 do_check_false(yield OS.File.exists(store.path)); |
|
102 |
|
103 // If the file does not exist, saving should not generate exceptions. |
|
104 yield store.save(); |
|
105 }); |
|
106 |
|
107 /** |
|
108 * Checks that loading from a missing file results in an empty list. |
|
109 */ |
|
110 add_task(function test_load_empty() |
|
111 { |
|
112 let [list, store] = yield promiseNewListAndStore(); |
|
113 |
|
114 do_check_false(yield OS.File.exists(store.path)); |
|
115 |
|
116 yield store.load(); |
|
117 |
|
118 let items = yield list.getAll(); |
|
119 do_check_eq(items.length, 0); |
|
120 }); |
|
121 |
|
122 /** |
|
123 * Loads downloads from a string in a predefined format. The purpose of this |
|
124 * test is to verify that the JSON format used in previous versions can be |
|
125 * loaded, assuming the file is reloaded on the same platform. |
|
126 */ |
|
127 add_task(function test_load_string_predefined() |
|
128 { |
|
129 let [list, store] = yield promiseNewListAndStore(); |
|
130 |
|
131 // The platform-dependent file name should be generated dynamically. |
|
132 let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
133 let filePathLiteral = JSON.stringify(targetPath); |
|
134 let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); |
|
135 let emptyUriLiteral = JSON.stringify(httpUrl("empty.txt")); |
|
136 let referrerUriLiteral = JSON.stringify(TEST_REFERRER_URL); |
|
137 |
|
138 let string = "{\"list\":[{\"source\":" + sourceUriLiteral + "," + |
|
139 "\"target\":" + filePathLiteral + "}," + |
|
140 "{\"source\":{\"url\":" + emptyUriLiteral + "," + |
|
141 "\"referrer\":" + referrerUriLiteral + "}," + |
|
142 "\"target\":" + filePathLiteral + "}]}"; |
|
143 |
|
144 yield OS.File.writeAtomic(store.path, |
|
145 new TextEncoder().encode(string), |
|
146 { tmpPath: store.path + ".tmp" }); |
|
147 |
|
148 yield store.load(); |
|
149 |
|
150 let items = yield list.getAll(); |
|
151 |
|
152 do_check_eq(items.length, 2); |
|
153 |
|
154 do_check_eq(items[0].source.url, httpUrl("source.txt")); |
|
155 do_check_eq(items[0].target.path, targetPath); |
|
156 |
|
157 do_check_eq(items[1].source.url, httpUrl("empty.txt")); |
|
158 do_check_eq(items[1].source.referrer, TEST_REFERRER_URL); |
|
159 do_check_eq(items[1].target.path, targetPath); |
|
160 }); |
|
161 |
|
162 /** |
|
163 * Loads downloads from a well-formed JSON string containing unrecognized data. |
|
164 */ |
|
165 add_task(function test_load_string_unrecognized() |
|
166 { |
|
167 let [list, store] = yield promiseNewListAndStore(); |
|
168 |
|
169 // The platform-dependent file name should be generated dynamically. |
|
170 let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
171 let filePathLiteral = JSON.stringify(targetPath); |
|
172 let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); |
|
173 |
|
174 let string = "{\"list\":[{\"source\":null," + |
|
175 "\"target\":null}," + |
|
176 "{\"source\":{\"url\":" + sourceUriLiteral + "}," + |
|
177 "\"target\":{\"path\":" + filePathLiteral + "}," + |
|
178 "\"saver\":{\"type\":\"copy\"}}]}"; |
|
179 |
|
180 yield OS.File.writeAtomic(store.path, |
|
181 new TextEncoder().encode(string), |
|
182 { tmpPath: store.path + ".tmp" }); |
|
183 |
|
184 yield store.load(); |
|
185 |
|
186 let items = yield list.getAll(); |
|
187 |
|
188 do_check_eq(items.length, 1); |
|
189 |
|
190 do_check_eq(items[0].source.url, httpUrl("source.txt")); |
|
191 do_check_eq(items[0].target.path, targetPath); |
|
192 }); |
|
193 |
|
194 /** |
|
195 * Loads downloads from a malformed JSON string. |
|
196 */ |
|
197 add_task(function test_load_string_malformed() |
|
198 { |
|
199 let [list, store] = yield promiseNewListAndStore(); |
|
200 |
|
201 let string = "{\"list\":[{\"source\":null,\"target\":null}," + |
|
202 "{\"source\":{\"url\":\"about:blank\"}}}"; |
|
203 |
|
204 yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string), |
|
205 { tmpPath: store.path + ".tmp" }); |
|
206 |
|
207 try { |
|
208 yield store.load(); |
|
209 do_throw("Exception expected when JSON data is malformed."); |
|
210 } catch (ex if ex.name == "SyntaxError") { |
|
211 do_print("The expected SyntaxError exception was thrown."); |
|
212 } |
|
213 |
|
214 let items = yield list.getAll(); |
|
215 |
|
216 do_check_eq(items.length, 0); |
|
217 }); |
|
218 |
|
219 /** |
|
220 * Saves downloads with unknown properties to a file and then reloads |
|
221 * them to ensure that these properties are preserved. |
|
222 */ |
|
223 add_task(function test_save_reload_unknownProperties() |
|
224 { |
|
225 let [listForSave, storeForSave] = yield promiseNewListAndStore(); |
|
226 let [listForLoad, storeForLoad] = yield promiseNewListAndStore( |
|
227 storeForSave.path); |
|
228 |
|
229 let download1 = yield promiseNewDownload(httpUrl("source.txt")); |
|
230 // startTime should be ignored as it is a known property, and error |
|
231 // is ignored by serialization |
|
232 download1._unknownProperties = { peanut: "butter", |
|
233 orange: "marmalade", |
|
234 startTime: 77, |
|
235 error: { message: "Passed" } }; |
|
236 listForSave.add(download1); |
|
237 |
|
238 let download2 = yield promiseStartLegacyDownload(); |
|
239 yield download2.cancel(); |
|
240 download2._unknownProperties = { number: 5, object: { test: "string" } }; |
|
241 listForSave.add(download2); |
|
242 |
|
243 let download3 = yield Downloads.createDownload({ |
|
244 source: { url: httpUrl("empty.txt"), |
|
245 referrer: TEST_REFERRER_URL, |
|
246 source1: "download3source1", |
|
247 source2: "download3source2" }, |
|
248 target: { path: getTempFile(TEST_TARGET_FILE_NAME).path, |
|
249 target1: "download3target1", |
|
250 target2: "download3target2" }, |
|
251 saver : { type: "copy", |
|
252 saver1: "download3saver1", |
|
253 saver2: "download3saver2" }, |
|
254 }); |
|
255 listForSave.add(download3); |
|
256 |
|
257 yield storeForSave.save(); |
|
258 yield storeForLoad.load(); |
|
259 |
|
260 let itemsForSave = yield listForSave.getAll(); |
|
261 let itemsForLoad = yield listForLoad.getAll(); |
|
262 |
|
263 do_check_eq(itemsForSave.length, itemsForLoad.length); |
|
264 |
|
265 do_check_eq(Object.keys(itemsForLoad[0]._unknownProperties).length, 2); |
|
266 do_check_eq(itemsForLoad[0]._unknownProperties.peanut, "butter"); |
|
267 do_check_eq(itemsForLoad[0]._unknownProperties.orange, "marmalade"); |
|
268 do_check_false("startTime" in itemsForLoad[0]._unknownProperties); |
|
269 do_check_false("error" in itemsForLoad[0]._unknownProperties); |
|
270 |
|
271 do_check_eq(Object.keys(itemsForLoad[1]._unknownProperties).length, 2); |
|
272 do_check_eq(itemsForLoad[1]._unknownProperties.number, 5); |
|
273 do_check_eq(itemsForLoad[1]._unknownProperties.object.test, "string"); |
|
274 |
|
275 do_check_eq(Object.keys(itemsForLoad[2].source._unknownProperties).length, 2); |
|
276 do_check_eq(itemsForLoad[2].source._unknownProperties.source1, |
|
277 "download3source1"); |
|
278 do_check_eq(itemsForLoad[2].source._unknownProperties.source2, |
|
279 "download3source2"); |
|
280 |
|
281 do_check_eq(Object.keys(itemsForLoad[2].target._unknownProperties).length, 2); |
|
282 do_check_eq(itemsForLoad[2].target._unknownProperties.target1, |
|
283 "download3target1"); |
|
284 do_check_eq(itemsForLoad[2].target._unknownProperties.target2, |
|
285 "download3target2"); |
|
286 |
|
287 do_check_eq(Object.keys(itemsForLoad[2].saver._unknownProperties).length, 2); |
|
288 do_check_eq(itemsForLoad[2].saver._unknownProperties.saver1, |
|
289 "download3saver1"); |
|
290 do_check_eq(itemsForLoad[2].saver._unknownProperties.saver2, |
|
291 "download3saver2"); |
|
292 }); |
|
293 |
|
294 //////////////////////////////////////////////////////////////////////////////// |
|
295 //// Termination |
|
296 |
|
297 let tailFile = do_get_file("tail.js"); |
|
298 Services.scriptloader.loadSubScript(NetUtil.newURI(tailFile).spec); |