|
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 // Test nsIPartialFileInputStream |
|
6 // NOTE! These tests often use do_check_true(a == b) rather than |
|
7 // do_check_eq(a, b) to avoid outputting characters which confuse |
|
8 // the console |
|
9 |
|
10 const CC = Components.Constructor; |
|
11 const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", |
|
12 "nsIBinaryInputStream", |
|
13 "setInputStream"); |
|
14 const PR_RDONLY = 0x1; // see prio.h |
|
15 |
|
16 // We need the profile directory so the test harness will clean up our test |
|
17 // files. |
|
18 do_get_profile(); |
|
19 |
|
20 var binary_test_file_name = "data/image.png"; |
|
21 var text_test_file_name = "test_file_partial_inputstream.js"; |
|
22 // This is a global variable since if it's passed as an argument stack traces |
|
23 // become unreadable. |
|
24 var test_file_data; |
|
25 |
|
26 function run_test() |
|
27 { |
|
28 // Binary tests |
|
29 let binaryFile = do_get_file(binary_test_file_name); |
|
30 let size = binaryFile.fileSize; |
|
31 // Want to make sure we're working with a large enough file |
|
32 dump("**** binary file size is: " + size + " ****\n"); |
|
33 do_check_true(size > 65536); |
|
34 |
|
35 let binaryStream = new BinaryInputStream(new_file_input_stream(binaryFile)); |
|
36 test_file_data = ""; |
|
37 while ((avail = binaryStream.available()) > 0) { |
|
38 test_file_data += binaryStream.readBytes(avail); |
|
39 } |
|
40 do_check_eq(test_file_data.length, size); |
|
41 binaryStream.close(); |
|
42 |
|
43 test_binary_portion(0, 10); |
|
44 test_binary_portion(0, 20000); |
|
45 test_binary_portion(0, size); |
|
46 test_binary_portion(20000, 10); |
|
47 test_binary_portion(20000, 20000); |
|
48 test_binary_portion(20000, size-20000); |
|
49 test_binary_portion(size-10, 10); |
|
50 test_binary_portion(size-20000, 20000); |
|
51 test_binary_portion(0, 0); |
|
52 test_binary_portion(20000, 0); |
|
53 test_binary_portion(size-1, 1); |
|
54 |
|
55 |
|
56 // Text-file tests |
|
57 let textFile = do_get_file(binary_test_file_name); |
|
58 size = textFile.fileSize; |
|
59 // Want to make sure we're working with a large enough file |
|
60 dump("**** text file size is: " + size + " ****\n"); |
|
61 do_check_true(size > 7000); |
|
62 |
|
63 let textStream = new BinaryInputStream(new_file_input_stream(textFile)); |
|
64 test_file_data = ""; |
|
65 while ((avail = textStream.available()) > 0) |
|
66 test_file_data += textStream.readBytes(avail); |
|
67 do_check_eq(test_file_data.length, size); |
|
68 textStream.close(); |
|
69 |
|
70 test_text_portion(0, 100); |
|
71 test_text_portion(0, size); |
|
72 test_text_portion(5000, 1000); |
|
73 test_text_portion(size-10, 10); |
|
74 test_text_portion(size-5000, 5000); |
|
75 test_text_portion(10, 0); |
|
76 test_text_portion(size-1, 1); |
|
77 |
|
78 // Test auto-closing files |
|
79 // Test behavior when *not* autoclosing |
|
80 let tempFile = create_temp_file("01234567890123456789"); |
|
81 let tempInputStream = new_partial_file_input_stream(tempFile, 5, 10); |
|
82 tempInputStream.QueryInterface(Ci.nsILineInputStream); |
|
83 do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); |
|
84 try { |
|
85 // This fails on some platforms |
|
86 tempFile.remove(false); |
|
87 } |
|
88 catch (ex) { |
|
89 } |
|
90 tempInputStream.QueryInterface(Ci.nsISeekableStream); |
|
91 tempInputStream.seek(SET, 1); |
|
92 do_check_eq(read_line_stream(tempInputStream)[1], "678901234"); |
|
93 |
|
94 // Test removing the file when autoclosing |
|
95 tempFile = create_temp_file("01234567890123456789"); |
|
96 tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, |
|
97 Ci.nsIFileInputStream.CLOSE_ON_EOF | |
|
98 Ci.nsIFileInputStream.REOPEN_ON_REWIND); |
|
99 tempInputStream.QueryInterface(Ci.nsILineInputStream); |
|
100 do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); |
|
101 tempFile.remove(false); |
|
102 tempInputStream.QueryInterface(Ci.nsISeekableStream); |
|
103 try { |
|
104 // The seek should reopen the file, which should fail. |
|
105 tempInputStream.seek(SET, 1); |
|
106 do_check_true(false); |
|
107 } |
|
108 catch (ex) { |
|
109 } |
|
110 |
|
111 // Test editing the file when autoclosing |
|
112 tempFile = create_temp_file("01234567890123456789"); |
|
113 tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, |
|
114 Ci.nsIFileInputStream.CLOSE_ON_EOF | |
|
115 Ci.nsIFileInputStream.REOPEN_ON_REWIND); |
|
116 tempInputStream.QueryInterface(Ci.nsILineInputStream); |
|
117 do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); |
|
118 let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. |
|
119 createInstance(Ci.nsIFileOutputStream); |
|
120 ostream.init(tempFile, 0x02 | 0x08 | 0x20, // write, create, truncate |
|
121 0666, 0); |
|
122 let newData = "abcdefghijklmnopqrstuvwxyz"; |
|
123 ostream.write(newData, newData.length); |
|
124 ostream.close(); |
|
125 tempInputStream.QueryInterface(Ci.nsISeekableStream); |
|
126 tempInputStream.seek(SET, 1); |
|
127 do_check_eq(read_line_stream(tempInputStream)[1], newData.substr(6,9)); |
|
128 |
|
129 // Test auto-delete and auto-close together |
|
130 tempFile = create_temp_file("01234567890123456789"); |
|
131 tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, |
|
132 Ci.nsIFileInputStream.CLOSE_ON_EOF | |
|
133 Ci.nsIFileInputStream.DELETE_ON_CLOSE); |
|
134 tempInputStream.QueryInterface(Ci.nsILineInputStream); |
|
135 do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); |
|
136 do_check_false(tempFile.exists()); |
|
137 } |
|
138 |
|
139 function test_binary_portion(start, length) { |
|
140 let subFile = create_temp_file(test_file_data.substr(start, length)); |
|
141 |
|
142 let streamTests = [ |
|
143 test_4k_read, |
|
144 test_max_read, |
|
145 test_seek, |
|
146 test_seek_then_read, |
|
147 ]; |
|
148 |
|
149 for each(test in streamTests) { |
|
150 let fileStream = new_file_input_stream(subFile); |
|
151 let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), |
|
152 start, length); |
|
153 test(fileStream, partialStream, length); |
|
154 fileStream.close(); |
|
155 partialStream.close(); |
|
156 } |
|
157 } |
|
158 |
|
159 function test_4k_read(fileStreamA, fileStreamB) { |
|
160 fileStreamA.QueryInterface(Ci.nsISeekableStream); |
|
161 fileStreamB.QueryInterface(Ci.nsISeekableStream); |
|
162 let streamA = new BinaryInputStream(fileStreamA); |
|
163 let streamB = new BinaryInputStream(fileStreamB); |
|
164 |
|
165 while(1) { |
|
166 do_check_eq(fileStreamA.tell(), fileStreamB.tell()); |
|
167 |
|
168 let availA = streamA.available(); |
|
169 let availB = streamB.available(); |
|
170 do_check_eq(availA, availB); |
|
171 if (availA == 0) |
|
172 return; |
|
173 |
|
174 let readSize = availA > 4096 ? 4096 : availA; |
|
175 |
|
176 do_check_true(streamA.readBytes(readSize) == |
|
177 streamB.readBytes(readSize)); |
|
178 } |
|
179 } |
|
180 |
|
181 function test_max_read(fileStreamA, fileStreamB) { |
|
182 fileStreamA.QueryInterface(Ci.nsISeekableStream); |
|
183 fileStreamB.QueryInterface(Ci.nsISeekableStream); |
|
184 let streamA = new BinaryInputStream(fileStreamA); |
|
185 let streamB = new BinaryInputStream(fileStreamB); |
|
186 |
|
187 while(1) { |
|
188 do_check_eq(fileStreamA.tell(), fileStreamB.tell()); |
|
189 |
|
190 let availA = streamA.available(); |
|
191 let availB = streamB.available(); |
|
192 do_check_eq(availA, availB); |
|
193 if (availA == 0) |
|
194 return; |
|
195 |
|
196 do_check_true(streamA.readBytes(availA) == |
|
197 streamB.readBytes(availB)); |
|
198 } |
|
199 } |
|
200 |
|
201 const SET = Ci.nsISeekableStream.NS_SEEK_SET; |
|
202 const CUR = Ci.nsISeekableStream.NS_SEEK_CUR; |
|
203 const END = Ci.nsISeekableStream.NS_SEEK_END; |
|
204 function test_seek(dummy, partialFileStream, size) { |
|
205 // We can't test the "real" filestream here as our existing file streams |
|
206 // are very broken and allows searching past the end of the file. |
|
207 |
|
208 partialFileStream.QueryInterface(Ci.nsISeekableStream); |
|
209 |
|
210 tests = [ |
|
211 [SET, 0], |
|
212 [SET, 5], |
|
213 [SET, 1000], |
|
214 [SET, size-10], |
|
215 [SET, size-5], |
|
216 [SET, size-1], |
|
217 [SET, size], |
|
218 [SET, size+10], |
|
219 [SET, 0], |
|
220 [CUR, 5], |
|
221 [CUR, -5], |
|
222 [SET, 5000], |
|
223 [CUR, -100], |
|
224 [CUR, 200], |
|
225 [CUR, -5000], |
|
226 [CUR, 5000], |
|
227 [CUR, size * 2], |
|
228 [SET, 1], |
|
229 [CUR, -1], |
|
230 [CUR, -1], |
|
231 [CUR, -1], |
|
232 [CUR, -1], |
|
233 [CUR, -1], |
|
234 [SET, size-1], |
|
235 [CUR, 1], |
|
236 [CUR, 1], |
|
237 [CUR, 1], |
|
238 [CUR, 1], |
|
239 [CUR, 1], |
|
240 [END, 0], |
|
241 [END, -1], |
|
242 [END, -5], |
|
243 [END, -1000], |
|
244 [END, -size+10], |
|
245 [END, -size+5], |
|
246 [END, -size+1], |
|
247 [END, -size], |
|
248 [END, -size-10], |
|
249 [END, 10], |
|
250 [CUR, 10], |
|
251 [CUR, 10], |
|
252 [CUR, 100], |
|
253 [CUR, 1000], |
|
254 [END, -1000], |
|
255 [CUR, 100], |
|
256 [CUR, 900], |
|
257 [CUR, 100], |
|
258 [CUR, 100], |
|
259 ]; |
|
260 |
|
261 let pos = 0; |
|
262 for each(test in tests) { |
|
263 let didThrow = false; |
|
264 try { |
|
265 partialFileStream.seek(test[0], test[1]); |
|
266 } |
|
267 catch (ex) { |
|
268 didThrow = true; |
|
269 } |
|
270 |
|
271 let newPos = test[0] == SET ? test[1] : |
|
272 test[0] == CUR ? pos + test[1] : |
|
273 size + test[1]; |
|
274 if (newPos > size || newPos < 0) { |
|
275 do_check_true(didThrow); |
|
276 } |
|
277 else { |
|
278 do_check_false(didThrow); |
|
279 pos = newPos; |
|
280 } |
|
281 |
|
282 do_check_eq(partialFileStream.tell(), pos); |
|
283 do_check_eq(partialFileStream.available(), size - pos); |
|
284 } |
|
285 } |
|
286 |
|
287 function test_seek_then_read(fileStreamA, fileStreamB, size) { |
|
288 // For now we only test seeking inside the file since our existing file |
|
289 // streams behave very strange when seeking to past the end of the file. |
|
290 if (size < 20000) { |
|
291 return; |
|
292 } |
|
293 |
|
294 fileStreamA.QueryInterface(Ci.nsISeekableStream); |
|
295 fileStreamB.QueryInterface(Ci.nsISeekableStream); |
|
296 let streamA = new BinaryInputStream(fileStreamA); |
|
297 let streamB = new BinaryInputStream(fileStreamB); |
|
298 |
|
299 let read = {}; |
|
300 |
|
301 tests = [ |
|
302 [SET, 0], |
|
303 [read, 1000], |
|
304 [read, 1000], |
|
305 [SET, 5], |
|
306 [read, 1000], |
|
307 [read, 5000], |
|
308 [CUR, 100], |
|
309 [read, 1000], |
|
310 [read, 5000], |
|
311 [CUR, -100], |
|
312 [read, 1000], |
|
313 [CUR, -100], |
|
314 [read, 5000], |
|
315 [END, -10], |
|
316 [read, 10], |
|
317 [END, -100], |
|
318 [read, 101], |
|
319 [CUR, -100], |
|
320 [read, 10], |
|
321 [SET, 0], |
|
322 [read, 20000], |
|
323 [read, 1], |
|
324 [read, 100], |
|
325 ]; |
|
326 |
|
327 for each(test in tests) { |
|
328 if (test[0] === read) { |
|
329 |
|
330 let didThrowA = false; |
|
331 let didThrowB = false; |
|
332 |
|
333 let bytesA, bytesB; |
|
334 try { |
|
335 bytesA = streamA.readBytes(test[1]); |
|
336 } |
|
337 catch (ex) { |
|
338 didThrowA = true; |
|
339 } |
|
340 try { |
|
341 bytesB = streamB.readBytes(test[1]); |
|
342 } |
|
343 catch (ex) { |
|
344 didThrowB = true; |
|
345 } |
|
346 |
|
347 do_check_eq(didThrowA, didThrowB); |
|
348 do_check_true(bytesA == bytesB); |
|
349 } |
|
350 else { |
|
351 fileStreamA.seek(test[0], test[1]); |
|
352 fileStreamB.seek(test[0], test[1]); |
|
353 } |
|
354 do_check_eq(fileStreamA.tell(), fileStreamB.tell()); |
|
355 do_check_eq(fileStreamA.available(), fileStreamB.available()); |
|
356 } |
|
357 } |
|
358 |
|
359 function test_text_portion(start, length) { |
|
360 let subFile = create_temp_file(test_file_data.substr(start, length)); |
|
361 |
|
362 let streamTests = [ |
|
363 test_readline, |
|
364 test_seek_then_readline, |
|
365 ]; |
|
366 |
|
367 for each(test in streamTests) { |
|
368 let fileStream = new_file_input_stream(subFile) |
|
369 .QueryInterface(Ci.nsILineInputStream); |
|
370 let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), |
|
371 start, length) |
|
372 .QueryInterface(Ci.nsILineInputStream); |
|
373 test(fileStream, partialStream, length); |
|
374 fileStream.close(); |
|
375 partialStream.close(); |
|
376 } |
|
377 } |
|
378 |
|
379 function test_readline(fileStreamA, fileStreamB) |
|
380 { |
|
381 let moreA = true, moreB; |
|
382 while(moreA) { |
|
383 let lineA, lineB; |
|
384 [moreA, lineA] = read_line_stream(fileStreamA); |
|
385 [moreB, lineB] = read_line_stream(fileStreamB); |
|
386 do_check_eq(moreA, moreB); |
|
387 do_check_true(lineA.value == lineB.value); |
|
388 } |
|
389 } |
|
390 |
|
391 function test_seek_then_readline(fileStreamA, fileStreamB, size) { |
|
392 // For now we only test seeking inside the file since our existing file |
|
393 // streams behave very strange when seeking to past the end of the file. |
|
394 if (size < 100) { |
|
395 return; |
|
396 } |
|
397 |
|
398 fileStreamA.QueryInterface(Ci.nsISeekableStream); |
|
399 fileStreamB.QueryInterface(Ci.nsISeekableStream); |
|
400 |
|
401 let read = {}; |
|
402 |
|
403 tests = [ |
|
404 [SET, 0], |
|
405 [read, 5], |
|
406 [read, 5], |
|
407 [SET, 5], |
|
408 [read, 5], |
|
409 [read, 15], |
|
410 [CUR, 100], |
|
411 [read, 5], |
|
412 [read, 15], |
|
413 [CUR, -100], |
|
414 [read, 5], |
|
415 [CUR, -100], |
|
416 [read, 25], |
|
417 [END, -10], |
|
418 [read, 1], |
|
419 [END, -50], |
|
420 [read, 30], |
|
421 [read, 1], |
|
422 [read, 1], |
|
423 [CUR, -100], |
|
424 [read, 1], |
|
425 [SET, 0], |
|
426 [read, 10000], |
|
427 [read, 1], |
|
428 [read, 1], |
|
429 [SET, 0], |
|
430 [read, 1], |
|
431 ]; |
|
432 |
|
433 for each(test in tests) { |
|
434 if (test[0] === read) { |
|
435 |
|
436 for (let i = 0; i < test[1]; ++i) { |
|
437 let didThrowA = false; |
|
438 let didThrowB = false; |
|
439 |
|
440 let lineA, lineB, moreA, moreB; |
|
441 try { |
|
442 [moreA, lineA] = read_line_stream(fileStreamA); |
|
443 } |
|
444 catch (ex) { |
|
445 didThrowA = true; |
|
446 } |
|
447 try { |
|
448 [moreB, lineB] = read_line_stream(fileStreamB); |
|
449 } |
|
450 catch (ex) { |
|
451 didThrowB = true; |
|
452 } |
|
453 |
|
454 do_check_eq(didThrowA, didThrowB); |
|
455 do_check_eq(moreA, moreB); |
|
456 do_check_true(lineA == lineB); |
|
457 do_check_eq(fileStreamA.tell(), fileStreamB.tell()); |
|
458 do_check_eq(fileStreamA.available(), fileStreamB.available()); |
|
459 if (!moreA) |
|
460 break; |
|
461 } |
|
462 } |
|
463 else { |
|
464 if (!(test[0] == CUR && (test[1] > fileStreamA.available() || |
|
465 test[1] < -fileStreamA.tell()))) { |
|
466 fileStreamA.seek(test[0], test[1]); |
|
467 fileStreamB.seek(test[0], test[1]); |
|
468 do_check_eq(fileStreamA.tell(), fileStreamB.tell()); |
|
469 do_check_eq(fileStreamA.available(), fileStreamB.available()); |
|
470 } |
|
471 } |
|
472 } |
|
473 } |
|
474 |
|
475 function read_line_stream(stream) { |
|
476 let line = {}; |
|
477 let more = stream.readLine(line); |
|
478 return [more, line.value]; |
|
479 } |
|
480 |
|
481 function new_file_input_stream(file) { |
|
482 var stream = |
|
483 Cc["@mozilla.org/network/file-input-stream;1"] |
|
484 .createInstance(Ci.nsIFileInputStream); |
|
485 stream.init(file, PR_RDONLY, 0, 0); |
|
486 return stream.QueryInterface(Ci.nsIInputStream); |
|
487 } |
|
488 |
|
489 function new_partial_file_input_stream(file, start, length, flags) { |
|
490 var stream = |
|
491 Cc["@mozilla.org/network/partial-file-input-stream;1"] |
|
492 .createInstance(Ci.nsIPartialFileInputStream); |
|
493 stream.init(file, start, length, PR_RDONLY, 0, flags || 0); |
|
494 return stream.QueryInterface(Ci.nsIInputStream); |
|
495 } |
|
496 |
|
497 function create_temp_file(data) { |
|
498 let file = Cc["@mozilla.org/file/directory_service;1"]. |
|
499 getService(Ci.nsIProperties). |
|
500 get("ProfD", Ci.nsIFile); |
|
501 file.append("fileinputstream-test-file.tmp"); |
|
502 file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); |
|
503 |
|
504 let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. |
|
505 createInstance(Ci.nsIFileOutputStream); |
|
506 ostream.init(file, 0x02 | 0x08 | 0x20, // write, create, truncate |
|
507 0666, 0); |
|
508 do_check_eq(ostream.write(data, data.length), data.length); |
|
509 ostream.close(); |
|
510 |
|
511 return file; |
|
512 } |