michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Test nsIPartialFileInputStream michael@0: // NOTE! These tests often use do_check_true(a == b) rather than michael@0: // do_check_eq(a, b) to avoid outputting characters which confuse michael@0: // the console michael@0: michael@0: const CC = Components.Constructor; michael@0: const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", michael@0: "nsIBinaryInputStream", michael@0: "setInputStream"); michael@0: const PR_RDONLY = 0x1; // see prio.h michael@0: michael@0: // We need the profile directory so the test harness will clean up our test michael@0: // files. michael@0: do_get_profile(); michael@0: michael@0: var binary_test_file_name = "data/image.png"; michael@0: var text_test_file_name = "test_file_partial_inputstream.js"; michael@0: // This is a global variable since if it's passed as an argument stack traces michael@0: // become unreadable. michael@0: var test_file_data; michael@0: michael@0: function run_test() michael@0: { michael@0: // Binary tests michael@0: let binaryFile = do_get_file(binary_test_file_name); michael@0: let size = binaryFile.fileSize; michael@0: // Want to make sure we're working with a large enough file michael@0: dump("**** binary file size is: " + size + " ****\n"); michael@0: do_check_true(size > 65536); michael@0: michael@0: let binaryStream = new BinaryInputStream(new_file_input_stream(binaryFile)); michael@0: test_file_data = ""; michael@0: while ((avail = binaryStream.available()) > 0) { michael@0: test_file_data += binaryStream.readBytes(avail); michael@0: } michael@0: do_check_eq(test_file_data.length, size); michael@0: binaryStream.close(); michael@0: michael@0: test_binary_portion(0, 10); michael@0: test_binary_portion(0, 20000); michael@0: test_binary_portion(0, size); michael@0: test_binary_portion(20000, 10); michael@0: test_binary_portion(20000, 20000); michael@0: test_binary_portion(20000, size-20000); michael@0: test_binary_portion(size-10, 10); michael@0: test_binary_portion(size-20000, 20000); michael@0: test_binary_portion(0, 0); michael@0: test_binary_portion(20000, 0); michael@0: test_binary_portion(size-1, 1); michael@0: michael@0: michael@0: // Text-file tests michael@0: let textFile = do_get_file(binary_test_file_name); michael@0: size = textFile.fileSize; michael@0: // Want to make sure we're working with a large enough file michael@0: dump("**** text file size is: " + size + " ****\n"); michael@0: do_check_true(size > 7000); michael@0: michael@0: let textStream = new BinaryInputStream(new_file_input_stream(textFile)); michael@0: test_file_data = ""; michael@0: while ((avail = textStream.available()) > 0) michael@0: test_file_data += textStream.readBytes(avail); michael@0: do_check_eq(test_file_data.length, size); michael@0: textStream.close(); michael@0: michael@0: test_text_portion(0, 100); michael@0: test_text_portion(0, size); michael@0: test_text_portion(5000, 1000); michael@0: test_text_portion(size-10, 10); michael@0: test_text_portion(size-5000, 5000); michael@0: test_text_portion(10, 0); michael@0: test_text_portion(size-1, 1); michael@0: michael@0: // Test auto-closing files michael@0: // Test behavior when *not* autoclosing michael@0: let tempFile = create_temp_file("01234567890123456789"); michael@0: let tempInputStream = new_partial_file_input_stream(tempFile, 5, 10); michael@0: tempInputStream.QueryInterface(Ci.nsILineInputStream); michael@0: do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); michael@0: try { michael@0: // This fails on some platforms michael@0: tempFile.remove(false); michael@0: } michael@0: catch (ex) { michael@0: } michael@0: tempInputStream.QueryInterface(Ci.nsISeekableStream); michael@0: tempInputStream.seek(SET, 1); michael@0: do_check_eq(read_line_stream(tempInputStream)[1], "678901234"); michael@0: michael@0: // Test removing the file when autoclosing michael@0: tempFile = create_temp_file("01234567890123456789"); michael@0: tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, michael@0: Ci.nsIFileInputStream.CLOSE_ON_EOF | michael@0: Ci.nsIFileInputStream.REOPEN_ON_REWIND); michael@0: tempInputStream.QueryInterface(Ci.nsILineInputStream); michael@0: do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); michael@0: tempFile.remove(false); michael@0: tempInputStream.QueryInterface(Ci.nsISeekableStream); michael@0: try { michael@0: // The seek should reopen the file, which should fail. michael@0: tempInputStream.seek(SET, 1); michael@0: do_check_true(false); michael@0: } michael@0: catch (ex) { michael@0: } michael@0: michael@0: // Test editing the file when autoclosing michael@0: tempFile = create_temp_file("01234567890123456789"); michael@0: tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, michael@0: Ci.nsIFileInputStream.CLOSE_ON_EOF | michael@0: Ci.nsIFileInputStream.REOPEN_ON_REWIND); michael@0: tempInputStream.QueryInterface(Ci.nsILineInputStream); michael@0: do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); michael@0: let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. michael@0: createInstance(Ci.nsIFileOutputStream); michael@0: ostream.init(tempFile, 0x02 | 0x08 | 0x20, // write, create, truncate michael@0: 0666, 0); michael@0: let newData = "abcdefghijklmnopqrstuvwxyz"; michael@0: ostream.write(newData, newData.length); michael@0: ostream.close(); michael@0: tempInputStream.QueryInterface(Ci.nsISeekableStream); michael@0: tempInputStream.seek(SET, 1); michael@0: do_check_eq(read_line_stream(tempInputStream)[1], newData.substr(6,9)); michael@0: michael@0: // Test auto-delete and auto-close together michael@0: tempFile = create_temp_file("01234567890123456789"); michael@0: tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, michael@0: Ci.nsIFileInputStream.CLOSE_ON_EOF | michael@0: Ci.nsIFileInputStream.DELETE_ON_CLOSE); michael@0: tempInputStream.QueryInterface(Ci.nsILineInputStream); michael@0: do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); michael@0: do_check_false(tempFile.exists()); michael@0: } michael@0: michael@0: function test_binary_portion(start, length) { michael@0: let subFile = create_temp_file(test_file_data.substr(start, length)); michael@0: michael@0: let streamTests = [ michael@0: test_4k_read, michael@0: test_max_read, michael@0: test_seek, michael@0: test_seek_then_read, michael@0: ]; michael@0: michael@0: for each(test in streamTests) { michael@0: let fileStream = new_file_input_stream(subFile); michael@0: let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), michael@0: start, length); michael@0: test(fileStream, partialStream, length); michael@0: fileStream.close(); michael@0: partialStream.close(); michael@0: } michael@0: } michael@0: michael@0: function test_4k_read(fileStreamA, fileStreamB) { michael@0: fileStreamA.QueryInterface(Ci.nsISeekableStream); michael@0: fileStreamB.QueryInterface(Ci.nsISeekableStream); michael@0: let streamA = new BinaryInputStream(fileStreamA); michael@0: let streamB = new BinaryInputStream(fileStreamB); michael@0: michael@0: while(1) { michael@0: do_check_eq(fileStreamA.tell(), fileStreamB.tell()); michael@0: michael@0: let availA = streamA.available(); michael@0: let availB = streamB.available(); michael@0: do_check_eq(availA, availB); michael@0: if (availA == 0) michael@0: return; michael@0: michael@0: let readSize = availA > 4096 ? 4096 : availA; michael@0: michael@0: do_check_true(streamA.readBytes(readSize) == michael@0: streamB.readBytes(readSize)); michael@0: } michael@0: } michael@0: michael@0: function test_max_read(fileStreamA, fileStreamB) { michael@0: fileStreamA.QueryInterface(Ci.nsISeekableStream); michael@0: fileStreamB.QueryInterface(Ci.nsISeekableStream); michael@0: let streamA = new BinaryInputStream(fileStreamA); michael@0: let streamB = new BinaryInputStream(fileStreamB); michael@0: michael@0: while(1) { michael@0: do_check_eq(fileStreamA.tell(), fileStreamB.tell()); michael@0: michael@0: let availA = streamA.available(); michael@0: let availB = streamB.available(); michael@0: do_check_eq(availA, availB); michael@0: if (availA == 0) michael@0: return; michael@0: michael@0: do_check_true(streamA.readBytes(availA) == michael@0: streamB.readBytes(availB)); michael@0: } michael@0: } michael@0: michael@0: const SET = Ci.nsISeekableStream.NS_SEEK_SET; michael@0: const CUR = Ci.nsISeekableStream.NS_SEEK_CUR; michael@0: const END = Ci.nsISeekableStream.NS_SEEK_END; michael@0: function test_seek(dummy, partialFileStream, size) { michael@0: // We can't test the "real" filestream here as our existing file streams michael@0: // are very broken and allows searching past the end of the file. michael@0: michael@0: partialFileStream.QueryInterface(Ci.nsISeekableStream); michael@0: michael@0: tests = [ michael@0: [SET, 0], michael@0: [SET, 5], michael@0: [SET, 1000], michael@0: [SET, size-10], michael@0: [SET, size-5], michael@0: [SET, size-1], michael@0: [SET, size], michael@0: [SET, size+10], michael@0: [SET, 0], michael@0: [CUR, 5], michael@0: [CUR, -5], michael@0: [SET, 5000], michael@0: [CUR, -100], michael@0: [CUR, 200], michael@0: [CUR, -5000], michael@0: [CUR, 5000], michael@0: [CUR, size * 2], michael@0: [SET, 1], michael@0: [CUR, -1], michael@0: [CUR, -1], michael@0: [CUR, -1], michael@0: [CUR, -1], michael@0: [CUR, -1], michael@0: [SET, size-1], michael@0: [CUR, 1], michael@0: [CUR, 1], michael@0: [CUR, 1], michael@0: [CUR, 1], michael@0: [CUR, 1], michael@0: [END, 0], michael@0: [END, -1], michael@0: [END, -5], michael@0: [END, -1000], michael@0: [END, -size+10], michael@0: [END, -size+5], michael@0: [END, -size+1], michael@0: [END, -size], michael@0: [END, -size-10], michael@0: [END, 10], michael@0: [CUR, 10], michael@0: [CUR, 10], michael@0: [CUR, 100], michael@0: [CUR, 1000], michael@0: [END, -1000], michael@0: [CUR, 100], michael@0: [CUR, 900], michael@0: [CUR, 100], michael@0: [CUR, 100], michael@0: ]; michael@0: michael@0: let pos = 0; michael@0: for each(test in tests) { michael@0: let didThrow = false; michael@0: try { michael@0: partialFileStream.seek(test[0], test[1]); michael@0: } michael@0: catch (ex) { michael@0: didThrow = true; michael@0: } michael@0: michael@0: let newPos = test[0] == SET ? test[1] : michael@0: test[0] == CUR ? pos + test[1] : michael@0: size + test[1]; michael@0: if (newPos > size || newPos < 0) { michael@0: do_check_true(didThrow); michael@0: } michael@0: else { michael@0: do_check_false(didThrow); michael@0: pos = newPos; michael@0: } michael@0: michael@0: do_check_eq(partialFileStream.tell(), pos); michael@0: do_check_eq(partialFileStream.available(), size - pos); michael@0: } michael@0: } michael@0: michael@0: function test_seek_then_read(fileStreamA, fileStreamB, size) { michael@0: // For now we only test seeking inside the file since our existing file michael@0: // streams behave very strange when seeking to past the end of the file. michael@0: if (size < 20000) { michael@0: return; michael@0: } michael@0: michael@0: fileStreamA.QueryInterface(Ci.nsISeekableStream); michael@0: fileStreamB.QueryInterface(Ci.nsISeekableStream); michael@0: let streamA = new BinaryInputStream(fileStreamA); michael@0: let streamB = new BinaryInputStream(fileStreamB); michael@0: michael@0: let read = {}; michael@0: michael@0: tests = [ michael@0: [SET, 0], michael@0: [read, 1000], michael@0: [read, 1000], michael@0: [SET, 5], michael@0: [read, 1000], michael@0: [read, 5000], michael@0: [CUR, 100], michael@0: [read, 1000], michael@0: [read, 5000], michael@0: [CUR, -100], michael@0: [read, 1000], michael@0: [CUR, -100], michael@0: [read, 5000], michael@0: [END, -10], michael@0: [read, 10], michael@0: [END, -100], michael@0: [read, 101], michael@0: [CUR, -100], michael@0: [read, 10], michael@0: [SET, 0], michael@0: [read, 20000], michael@0: [read, 1], michael@0: [read, 100], michael@0: ]; michael@0: michael@0: for each(test in tests) { michael@0: if (test[0] === read) { michael@0: michael@0: let didThrowA = false; michael@0: let didThrowB = false; michael@0: michael@0: let bytesA, bytesB; michael@0: try { michael@0: bytesA = streamA.readBytes(test[1]); michael@0: } michael@0: catch (ex) { michael@0: didThrowA = true; michael@0: } michael@0: try { michael@0: bytesB = streamB.readBytes(test[1]); michael@0: } michael@0: catch (ex) { michael@0: didThrowB = true; michael@0: } michael@0: michael@0: do_check_eq(didThrowA, didThrowB); michael@0: do_check_true(bytesA == bytesB); michael@0: } michael@0: else { michael@0: fileStreamA.seek(test[0], test[1]); michael@0: fileStreamB.seek(test[0], test[1]); michael@0: } michael@0: do_check_eq(fileStreamA.tell(), fileStreamB.tell()); michael@0: do_check_eq(fileStreamA.available(), fileStreamB.available()); michael@0: } michael@0: } michael@0: michael@0: function test_text_portion(start, length) { michael@0: let subFile = create_temp_file(test_file_data.substr(start, length)); michael@0: michael@0: let streamTests = [ michael@0: test_readline, michael@0: test_seek_then_readline, michael@0: ]; michael@0: michael@0: for each(test in streamTests) { michael@0: let fileStream = new_file_input_stream(subFile) michael@0: .QueryInterface(Ci.nsILineInputStream); michael@0: let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), michael@0: start, length) michael@0: .QueryInterface(Ci.nsILineInputStream); michael@0: test(fileStream, partialStream, length); michael@0: fileStream.close(); michael@0: partialStream.close(); michael@0: } michael@0: } michael@0: michael@0: function test_readline(fileStreamA, fileStreamB) michael@0: { michael@0: let moreA = true, moreB; michael@0: while(moreA) { michael@0: let lineA, lineB; michael@0: [moreA, lineA] = read_line_stream(fileStreamA); michael@0: [moreB, lineB] = read_line_stream(fileStreamB); michael@0: do_check_eq(moreA, moreB); michael@0: do_check_true(lineA.value == lineB.value); michael@0: } michael@0: } michael@0: michael@0: function test_seek_then_readline(fileStreamA, fileStreamB, size) { michael@0: // For now we only test seeking inside the file since our existing file michael@0: // streams behave very strange when seeking to past the end of the file. michael@0: if (size < 100) { michael@0: return; michael@0: } michael@0: michael@0: fileStreamA.QueryInterface(Ci.nsISeekableStream); michael@0: fileStreamB.QueryInterface(Ci.nsISeekableStream); michael@0: michael@0: let read = {}; michael@0: michael@0: tests = [ michael@0: [SET, 0], michael@0: [read, 5], michael@0: [read, 5], michael@0: [SET, 5], michael@0: [read, 5], michael@0: [read, 15], michael@0: [CUR, 100], michael@0: [read, 5], michael@0: [read, 15], michael@0: [CUR, -100], michael@0: [read, 5], michael@0: [CUR, -100], michael@0: [read, 25], michael@0: [END, -10], michael@0: [read, 1], michael@0: [END, -50], michael@0: [read, 30], michael@0: [read, 1], michael@0: [read, 1], michael@0: [CUR, -100], michael@0: [read, 1], michael@0: [SET, 0], michael@0: [read, 10000], michael@0: [read, 1], michael@0: [read, 1], michael@0: [SET, 0], michael@0: [read, 1], michael@0: ]; michael@0: michael@0: for each(test in tests) { michael@0: if (test[0] === read) { michael@0: michael@0: for (let i = 0; i < test[1]; ++i) { michael@0: let didThrowA = false; michael@0: let didThrowB = false; michael@0: michael@0: let lineA, lineB, moreA, moreB; michael@0: try { michael@0: [moreA, lineA] = read_line_stream(fileStreamA); michael@0: } michael@0: catch (ex) { michael@0: didThrowA = true; michael@0: } michael@0: try { michael@0: [moreB, lineB] = read_line_stream(fileStreamB); michael@0: } michael@0: catch (ex) { michael@0: didThrowB = true; michael@0: } michael@0: michael@0: do_check_eq(didThrowA, didThrowB); michael@0: do_check_eq(moreA, moreB); michael@0: do_check_true(lineA == lineB); michael@0: do_check_eq(fileStreamA.tell(), fileStreamB.tell()); michael@0: do_check_eq(fileStreamA.available(), fileStreamB.available()); michael@0: if (!moreA) michael@0: break; michael@0: } michael@0: } michael@0: else { michael@0: if (!(test[0] == CUR && (test[1] > fileStreamA.available() || michael@0: test[1] < -fileStreamA.tell()))) { michael@0: fileStreamA.seek(test[0], test[1]); michael@0: fileStreamB.seek(test[0], test[1]); michael@0: do_check_eq(fileStreamA.tell(), fileStreamB.tell()); michael@0: do_check_eq(fileStreamA.available(), fileStreamB.available()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: function read_line_stream(stream) { michael@0: let line = {}; michael@0: let more = stream.readLine(line); michael@0: return [more, line.value]; michael@0: } michael@0: michael@0: function new_file_input_stream(file) { michael@0: var stream = michael@0: Cc["@mozilla.org/network/file-input-stream;1"] michael@0: .createInstance(Ci.nsIFileInputStream); michael@0: stream.init(file, PR_RDONLY, 0, 0); michael@0: return stream.QueryInterface(Ci.nsIInputStream); michael@0: } michael@0: michael@0: function new_partial_file_input_stream(file, start, length, flags) { michael@0: var stream = michael@0: Cc["@mozilla.org/network/partial-file-input-stream;1"] michael@0: .createInstance(Ci.nsIPartialFileInputStream); michael@0: stream.init(file, start, length, PR_RDONLY, 0, flags || 0); michael@0: return stream.QueryInterface(Ci.nsIInputStream); michael@0: } michael@0: michael@0: function create_temp_file(data) { michael@0: let file = Cc["@mozilla.org/file/directory_service;1"]. michael@0: getService(Ci.nsIProperties). michael@0: get("ProfD", Ci.nsIFile); michael@0: file.append("fileinputstream-test-file.tmp"); michael@0: file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); michael@0: michael@0: let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. michael@0: createInstance(Ci.nsIFileOutputStream); michael@0: ostream.init(file, 0x02 | 0x08 | 0x20, // write, create, truncate michael@0: 0666, 0); michael@0: do_check_eq(ostream.write(data, data.length), data.length); michael@0: ostream.close(); michael@0: michael@0: return file; michael@0: }