Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
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
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
16 // We need the profile directory so the test harness will clean up our test
17 // files.
18 do_get_profile();
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;
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);
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();
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);
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);
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();
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);
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");
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 }
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));
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 }
139 function test_binary_portion(start, length) {
140 let subFile = create_temp_file(test_file_data.substr(start, length));
142 let streamTests = [
143 test_4k_read,
144 test_max_read,
145 test_seek,
146 test_seek_then_read,
147 ];
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 }
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);
165 while(1) {
166 do_check_eq(fileStreamA.tell(), fileStreamB.tell());
168 let availA = streamA.available();
169 let availB = streamB.available();
170 do_check_eq(availA, availB);
171 if (availA == 0)
172 return;
174 let readSize = availA > 4096 ? 4096 : availA;
176 do_check_true(streamA.readBytes(readSize) ==
177 streamB.readBytes(readSize));
178 }
179 }
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);
187 while(1) {
188 do_check_eq(fileStreamA.tell(), fileStreamB.tell());
190 let availA = streamA.available();
191 let availB = streamB.available();
192 do_check_eq(availA, availB);
193 if (availA == 0)
194 return;
196 do_check_true(streamA.readBytes(availA) ==
197 streamB.readBytes(availB));
198 }
199 }
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.
208 partialFileStream.QueryInterface(Ci.nsISeekableStream);
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 ];
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 }
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 }
282 do_check_eq(partialFileStream.tell(), pos);
283 do_check_eq(partialFileStream.available(), size - pos);
284 }
285 }
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 }
294 fileStreamA.QueryInterface(Ci.nsISeekableStream);
295 fileStreamB.QueryInterface(Ci.nsISeekableStream);
296 let streamA = new BinaryInputStream(fileStreamA);
297 let streamB = new BinaryInputStream(fileStreamB);
299 let read = {};
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 ];
327 for each(test in tests) {
328 if (test[0] === read) {
330 let didThrowA = false;
331 let didThrowB = false;
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 }
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 }
359 function test_text_portion(start, length) {
360 let subFile = create_temp_file(test_file_data.substr(start, length));
362 let streamTests = [
363 test_readline,
364 test_seek_then_readline,
365 ];
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 }
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 }
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 }
398 fileStreamA.QueryInterface(Ci.nsISeekableStream);
399 fileStreamB.QueryInterface(Ci.nsISeekableStream);
401 let read = {};
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 ];
433 for each(test in tests) {
434 if (test[0] === read) {
436 for (let i = 0; i < test[1]; ++i) {
437 let didThrowA = false;
438 let didThrowB = false;
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 }
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 }
475 function read_line_stream(stream) {
476 let line = {};
477 let more = stream.readLine(line);
478 return [more, line.value];
479 }
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 }
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 }
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);
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();
511 return file;
512 }