michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: let Cc = Components.classes; michael@0: let Ci = Components.interfaces; 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: const OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/file-output-stream;1"; michael@0: const SAFE_OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/safe-file-output-stream;1"; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Helper Methods michael@0: michael@0: /** michael@0: * Generates a leafName for a file that does not exist, but does *not* michael@0: * create the file. Similar to createUnique except for the fact that createUnique michael@0: * does create the file. michael@0: * michael@0: * @param aFile michael@0: * The file to modify in order for it to have a unique leafname. michael@0: */ michael@0: function ensure_unique(aFile) michael@0: { michael@0: ensure_unique.fileIndex = ensure_unique.fileIndex || 0; michael@0: michael@0: var leafName = aFile.leafName; michael@0: while (aFile.clone().exists()) { michael@0: aFile.leafName = leafName + "_" + (ensure_unique.fileIndex++); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Tests for files being accessed at the right time. Streams that use michael@0: * DEFER_OPEN should only open or create the file when an operation is michael@0: * done, and not during Init(). michael@0: * michael@0: * Note that for writing, we check for actual writing in test_NetUtil (async) michael@0: * and in sync_operations in this file (sync), whereas in this function we michael@0: * just check that the file is *not* created during init. michael@0: * michael@0: * @param aContractId michael@0: * The contract ID to use for the output stream michael@0: * @param aDeferOpen michael@0: * Whether to check with DEFER_OPEN or not michael@0: * @param aTrickDeferredOpen michael@0: * Whether we try to 'trick' deferred opens by changing the file object before michael@0: * the actual open. The stream should have a clone, so changes to the file michael@0: * object after Init and before Open should not affect it. michael@0: */ michael@0: function check_access(aContractId, aDeferOpen, aTrickDeferredOpen) michael@0: { michael@0: const LEAF_NAME = "filestreams-test-file.tmp"; michael@0: const TRICKY_LEAF_NAME = "BetYouDidNotExpectThat.tmp"; 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(LEAF_NAME); michael@0: michael@0: // Writing michael@0: michael@0: ensure_unique(file); michael@0: let ostream = Cc[aContractId].createInstance(Ci.nsIFileOutputStream); michael@0: ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0); michael@0: do_check_eq(aDeferOpen, !file.clone().exists()); // If defer, should not exist and vice versa michael@0: if (aDeferOpen) { michael@0: // File should appear when we do write to it. michael@0: if (aTrickDeferredOpen) { michael@0: // See |@param aDeferOpen| in the JavaDoc comment for this function michael@0: file.leafName = TRICKY_LEAF_NAME; michael@0: } michael@0: ostream.write("data", 4); michael@0: if (aTrickDeferredOpen) { michael@0: file.leafName = LEAF_NAME; michael@0: } michael@0: // We did a write, so the file should now exist michael@0: do_check_true(file.clone().exists()); michael@0: } michael@0: ostream.close(); michael@0: michael@0: // Reading michael@0: michael@0: ensure_unique(file); michael@0: let istream = Cc["@mozilla.org/network/file-input-stream;1"]. michael@0: createInstance(Ci.nsIFileInputStream); michael@0: var initOk, getOk; michael@0: try { michael@0: istream.init(file, -1, 0, aDeferOpen ? Ci.nsIFileInputStream.DEFER_OPEN : 0); michael@0: initOk = true; michael@0: } michael@0: catch(e) { michael@0: initOk = false; michael@0: } michael@0: try { michael@0: let fstream = Cc["@mozilla.org/network/file-input-stream;1"]. michael@0: createInstance(Ci.nsIFileInputStream); michael@0: fstream.init(aFile, -1, 0, 0); michael@0: getOk = true; michael@0: } michael@0: catch(e) { michael@0: getOk = false; michael@0: } michael@0: michael@0: // If the open is deferred, then Init should succeed even though the file we michael@0: // intend to read does not exist, and then trying to read from it should michael@0: // fail. The other case is where the open is not deferred, and there we should michael@0: // get an error when we Init (and also when we try to read). michael@0: do_check_true( (aDeferOpen && initOk && !getOk) || michael@0: (!aDeferOpen && !initOk && !getOk) ); michael@0: istream.close(); michael@0: } michael@0: michael@0: /** michael@0: * We test async operations in test_NetUtil.js, and here test for simple sync michael@0: * operations on input streams. michael@0: * michael@0: * @param aDeferOpen michael@0: * Whether to use DEFER_OPEN in the streams. michael@0: */ michael@0: function sync_operations(aDeferOpen) michael@0: { michael@0: const TEST_DATA = "this is a test string"; michael@0: const LEAF_NAME = "filestreams-test-file.tmp"; michael@0: 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(LEAF_NAME); michael@0: file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); michael@0: michael@0: let ostream = Cc[OUTPUT_STREAM_CONTRACT_ID]. michael@0: createInstance(Ci.nsIFileOutputStream); michael@0: ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0); michael@0: michael@0: ostream.write(TEST_DATA, TEST_DATA.length); michael@0: ostream.close(); michael@0: michael@0: let fstream = Cc["@mozilla.org/network/file-input-stream;1"]. michael@0: createInstance(Ci.nsIFileInputStream); michael@0: fstream.init(file, -1, 0, aDeferOpen ? Ci.nsIFileInputStream.DEFER_OPEN : 0); michael@0: michael@0: let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]. michael@0: createInstance(Ci.nsIConverterInputStream); michael@0: cstream.init(fstream, "UTF-8", 0, 0); michael@0: michael@0: let string = {}; michael@0: cstream.readString(-1, string); michael@0: cstream.close(); michael@0: fstream.close(); michael@0: michael@0: do_check_eq(string.value, TEST_DATA); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Tests michael@0: michael@0: function test_access() michael@0: { michael@0: check_access(OUTPUT_STREAM_CONTRACT_ID, false, false); michael@0: } michael@0: michael@0: function test_access_trick() michael@0: { michael@0: check_access(OUTPUT_STREAM_CONTRACT_ID, false, true); michael@0: } michael@0: michael@0: function test_access_defer() michael@0: { michael@0: check_access(OUTPUT_STREAM_CONTRACT_ID, true, false); michael@0: } michael@0: michael@0: function test_access_defer_trick() michael@0: { michael@0: check_access(OUTPUT_STREAM_CONTRACT_ID, true, true); michael@0: } michael@0: michael@0: function test_access_safe() michael@0: { michael@0: check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, false, false); michael@0: } michael@0: michael@0: function test_access_safe_trick() michael@0: { michael@0: check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, false, true); michael@0: } michael@0: michael@0: function test_access_safe_defer() michael@0: { michael@0: check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, true, false); michael@0: } michael@0: michael@0: function test_access_safe_defer_trick() michael@0: { michael@0: check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, true, true); michael@0: } michael@0: michael@0: function test_sync_operations() michael@0: { michael@0: sync_operations(); michael@0: } michael@0: michael@0: function test_sync_operations_deferred() michael@0: { michael@0: sync_operations(true); michael@0: } michael@0: michael@0: function do_test_zero_size_buffered(disableBuffering) michael@0: { michael@0: const LEAF_NAME = "filestreams-test-file.tmp"; michael@0: const BUFFERSIZE = 4096; michael@0: 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(LEAF_NAME); michael@0: file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); michael@0: michael@0: let fstream = Cc["@mozilla.org/network/file-input-stream;1"]. michael@0: createInstance(Ci.nsIFileInputStream); michael@0: fstream.init(file, -1, 0, michael@0: Ci.nsIFileInputStream.CLOSE_ON_EOF | michael@0: Ci.nsIFileInputStream.REOPEN_ON_REWIND); michael@0: michael@0: var buffered = Cc["@mozilla.org/network/buffered-input-stream;1"]. michael@0: createInstance(Ci.nsIBufferedInputStream); michael@0: buffered.init(fstream, BUFFERSIZE); michael@0: michael@0: if (disableBuffering) { michael@0: buffered.QueryInterface(Ci.nsIStreamBufferAccess).disableBuffering(); michael@0: } michael@0: michael@0: // Scriptable input streams clamp read sizes to the return value of michael@0: // available(), so don't quite do what we want here. michael@0: let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]. michael@0: createInstance(Ci.nsIConverterInputStream); michael@0: cstream.init(buffered, "UTF-8", 0, 0); michael@0: michael@0: do_check_eq(buffered.available(), 0); michael@0: michael@0: // Now try reading from this stream michael@0: let string = {}; michael@0: do_check_eq(cstream.readString(BUFFERSIZE, string), 0); michael@0: do_check_eq(string.value, ""); michael@0: michael@0: // Now check that available() throws michael@0: var exceptionThrown = false; michael@0: try { michael@0: do_check_eq(buffered.available(), 0); michael@0: } catch (e) { michael@0: exceptionThrown = true; michael@0: } michael@0: do_check_true(exceptionThrown); michael@0: michael@0: // OK, now seek back to start michael@0: buffered.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); michael@0: michael@0: // Now check that available() does not throw michael@0: exceptionThrown = false; michael@0: try { michael@0: do_check_eq(buffered.available(), 0); michael@0: } catch (e) { michael@0: exceptionThrown = true; michael@0: } michael@0: do_check_false(exceptionThrown); michael@0: } michael@0: michael@0: function test_zero_size_buffered() michael@0: { michael@0: do_test_zero_size_buffered(false); michael@0: do_test_zero_size_buffered(true); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Test Runner michael@0: michael@0: let tests = [ michael@0: test_access, michael@0: test_access_trick, michael@0: test_access_defer, michael@0: test_access_defer_trick, michael@0: test_access_safe, michael@0: test_access_safe_trick, michael@0: test_access_safe_defer, michael@0: test_access_safe_defer_trick, michael@0: test_sync_operations, michael@0: test_sync_operations_deferred, michael@0: test_zero_size_buffered, michael@0: ]; michael@0: michael@0: function run_test() michael@0: { michael@0: tests.forEach(function(test) { michael@0: test(); michael@0: }); michael@0: } michael@0: