diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /toolkit/components/osfile/tests/mochi | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/osfile/tests/mochi')
10 files changed, 2179 insertions, 0 deletions
diff --git a/toolkit/components/osfile/tests/mochi/chrome.ini b/toolkit/components/osfile/tests/mochi/chrome.ini new file mode 100644 index 0000000000..c36cf2c045 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/chrome.ini @@ -0,0 +1,15 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + main_test_osfile_async.js + worker_test_osfile_comms.js + worker_test_osfile_front.js + worker_test_osfile_unix.js + worker_test_osfile_win.js + +[test_osfile_async.xhtml] +[test_osfile_back.xhtml] +[test_osfile_comms.xhtml] +[test_osfile_front.xhtml] +skip-if = + win11_2009 && bits == 32 # Bug 1809355 diff --git a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js new file mode 100644 index 0000000000..0a1fe938d4 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js @@ -0,0 +1,501 @@ +"use strict"; + +const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); + +// The following are used to compare against a well-tested reference +// implementation of file I/O. +const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); +const { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); + +var myok = ok; +var myis = is; +var myinfo = info; +var myisnot = isnot; + +var isPromise = function ispromise(value) { + return value != null && typeof value == "object" && "then" in value; +}; + +var maketest = function(prefix, test) { + let utils = { + ok: function ok(t, m) { + myok(t, prefix + ": " + m); + }, + is: function is(l, r, m) { + myis(l, r, prefix + ": " + m); + }, + isnot: function isnot(l, r, m) { + myisnot(l, r, prefix + ": " + m); + }, + info: function info(m) { + myinfo(prefix + ": " + m); + }, + fail: function fail(m) { + utils.ok(false, m); + }, + okpromise: function okpromise(t, m) { + return t.then( + function onSuccess() { + utils.ok(true, m); + }, + function onFailure() { + utils.ok(false, m); + } + ); + }, + }; + return function runtest() { + utils.info("Entering"); + try { + let result = test.call(this, utils); + if (!isPromise(result)) { + throw new TypeError("The test did not return a promise"); + } + utils.info("This was a promise"); + // The test returns a promise + result = result.then( + function test_complete() { + utils.info("Complete"); + }, + function catch_uncaught_errors(err) { + utils.fail("Uncaught error " + err); + if (err && typeof err == "object" && "message" in err) { + utils.fail("(" + err.message + ")"); + } + if (err && typeof err == "object" && "stack" in err) { + utils.fail("at " + err.stack); + } + } + ); + return result; + } catch (x) { + utils.fail("Error " + x + " at " + x.stack); + return null; + } + }; +}; + +/** + * Fetch asynchronously the contents of a file using xpcom. + * + * Used for comparing xpcom-based results to os.file-based results. + * + * @param {string} path The _absolute_ path to the file. + * @return {promise} + * @resolves {string} The contents of the file. + */ +var reference_fetch_file = function reference_fetch_file(path, test) { + test.info("Fetching file " + path); + return new Promise((resolve, reject) => { + let file = new FileUtils.File(path); + NetUtil.asyncFetch( + { + uri: NetUtil.newURI(file), + loadUsingSystemPrincipal: true, + }, + function(stream, status) { + if (!Components.isSuccessCode(status)) { + reject(status); + return; + } + let result, reject; + try { + result = NetUtil.readInputStreamToString(stream, stream.available()); + } catch (x) { + reject = x; + } + stream.close(); + if (reject) { + reject(reject); + } else { + resolve(result); + } + } + ); + }); +}; + +var reference_dir_contents = function reference_dir_contents(path) { + let result = []; + let entries = new FileUtils.File(path).directoryEntries; + while (entries.hasMoreElements()) { + let entry = entries.nextFile; + result.push(entry.path); + } + return result; +}; + +// Set/Unset OS.Shared.DEBUG, OS.Shared.TEST and a console listener. +function toggleDebugTest(pref, consoleListener) { + Services.prefs.setBoolPref("toolkit.osfile.log", pref); + Services.prefs.setBoolPref("toolkit.osfile.log.redirect", pref); + Services.console[pref ? "registerListener" : "unregisterListener"]( + consoleListener + ); +} + +var test = maketest("Main", function main(test) { + return (async function() { + SimpleTest.waitForExplicitFinish(); + await test_stat(); + await test_debug(); + await test_info_features_detect(); + await test_position(); + await test_iter(); + await test_exists(); + await test_debug_test(); + info("Test is over"); + SimpleTest.finish(); + })(); +}); + +/** + * A file that we know exists and that can be used for reading. + */ +var EXISTING_FILE = OS.Path.join( + "chrome", + "toolkit", + "components", + "osfile", + "tests", + "mochi", + "main_test_osfile_async.js" +); + +/** + * Test OS.File.stat and OS.File.prototype.stat + */ +var test_stat = maketest("stat", function stat(test) { + return (async function() { + // Open a file and stat it + let file = await OS.File.open(EXISTING_FILE); + let stat1; + + try { + test.info("Stating file"); + stat1 = await file.stat(); + test.ok(true, "stat has worked " + stat1); + test.ok(stat1, "stat is not empty"); + } finally { + await file.close(); + } + + // Stat the same file without opening it + test.info("Stating a file without opening it"); + let stat2 = await OS.File.stat(EXISTING_FILE); + test.ok(true, "stat 2 has worked " + stat2); + test.ok(stat2, "stat 2 is not empty"); + for (let key in stat2) { + test.is( + "" + stat1[key], + "" + stat2[key], + "Stat field " + key + "is the same" + ); + } + })(); +}); + +/** + * Test feature detection using OS.File.Info.prototype on main thread + */ +var test_info_features_detect = maketest( + "features_detect", + function features_detect(test) { + return (async function() { + if (!OS.Constants.Win && OS.Constants.libc) { + // see if unixGroup is defined + if ("unixGroup" in OS.File.Info.prototype) { + test.ok(true, "unixGroup is defined"); + } else { + test.fail("unixGroup is not defined though we are under Unix"); + } + } + })(); + } +); + +/** + * Test file.{getPosition, setPosition} + */ +var test_position = maketest("position", function position(test) { + return (async function() { + let file = await OS.File.open(EXISTING_FILE); + + try { + let view = await file.read(); + test.info("First batch of content read"); + let CHUNK_SIZE = 178; // An arbitrary number of bytes to read from the file + let pos = await file.getPosition(); + test.info("Obtained position"); + test.is(pos, view.byteLength, "getPosition returned the end of the file"); + pos = await file.setPosition(-CHUNK_SIZE, OS.File.POS_END); + test.info("Changed position"); + test.is( + pos, + view.byteLength - CHUNK_SIZE, + "setPosition returned the correct position" + ); + + let view2 = await file.read(); + test.info("Read the end of the file"); + for (let i = 0; i < CHUNK_SIZE; ++i) { + if (view2[i] != view[i + view.byteLength - CHUNK_SIZE]) { + test.is( + view2[i], + view[i], + "setPosition put us in the right position" + ); + } + } + } finally { + await file.close(); + } + })(); +}); + +/** + * Test OS.File.prototype.{DirectoryIterator} + */ +var test_iter = maketest("iter", function iter(test) { + return (async function() { + let currentDir = await OS.File.getCurrentDirectory(); + + // Trivial walks through the directory + test.info("Preparing iteration"); + let iterator = new OS.File.DirectoryIterator(currentDir); + let temporary_file_name = OS.Path.join( + currentDir, + "empty-temporary-file.tmp" + ); + try { + await OS.File.remove(temporary_file_name); + } catch (err) { + // Ignore errors removing file + } + let allFiles1 = await iterator.nextBatch(); + test.info("Obtained all files through nextBatch"); + test.isnot(allFiles1.length, 0, "There is at least one file"); + test.isnot(allFiles1[0].path, null, "Files have a path"); + + // Ensure that we have the same entries with |reference_dir_contents| + let referenceEntries = new Set(); + for (let entry of reference_dir_contents(currentDir)) { + referenceEntries.add(entry); + } + test.is( + referenceEntries.size, + allFiles1.length, + "All the entries in the directory have been listed" + ); + for (let entry of allFiles1) { + test.ok( + referenceEntries.has(entry.path), + "File " + entry.path + " effectively exists" + ); + // Ensure that we have correct isDir and isSymLink + // Current directory is {objdir}/_tests/testing/mochitest/, assume it has some dirs and symlinks. + var f = new FileUtils.File(entry.path); + test.is( + entry.isDir, + f.isDirectory(), + "Get file " + entry.path + " isDir correctly" + ); + test.is( + entry.isSymLink, + f.isSymlink(), + "Get file " + entry.path + " isSymLink correctly" + ); + } + + await iterator.close(); + test.info("Closed iterator"); + + test.info("Double closing DirectoryIterator"); + iterator = new OS.File.DirectoryIterator(currentDir); + await iterator.close(); + await iterator.close(); // double closing |DirectoryIterator| + test.ok(true, "|DirectoryIterator| was closed twice successfully"); + + let allFiles2 = []; + let i = 0; + iterator = new OS.File.DirectoryIterator(currentDir); + await iterator.forEach(function(entry, index) { + test.is(i++, index, "Getting the correct index"); + allFiles2.push(entry); + }); + test.info("Obtained all files through forEach"); + is( + allFiles1.length, + allFiles2.length, + "Both runs returned the same number of files" + ); + for (let i = 0; i < allFiles1.length; ++i) { + if (allFiles1[i].path != allFiles2[i].path) { + test.is( + allFiles1[i].path, + allFiles2[i].path, + "Both runs return the same files" + ); + break; + } + } + + // Testing batch iteration + whether an iteration can be stopped early + let BATCH_LENGTH = 10; + test.info("Getting some files through nextBatch"); + await iterator.close(); + + iterator = new OS.File.DirectoryIterator(currentDir); + let someFiles1 = await iterator.nextBatch(BATCH_LENGTH); + let someFiles2 = await iterator.nextBatch(BATCH_LENGTH); + await iterator.close(); + + iterator = new OS.File.DirectoryIterator(currentDir); + await iterator.forEach(function cb(entry, index, iterator) { + if (index < BATCH_LENGTH) { + test.is( + entry.path, + someFiles1[index].path, + "Both runs return the same files (part 1)" + ); + } else if (index < 2 * BATCH_LENGTH) { + test.is( + entry.path, + someFiles2[index - BATCH_LENGTH].path, + "Both runs return the same files (part 2)" + ); + } else if (index == 2 * BATCH_LENGTH) { + test.info("Attempting to stop asynchronous forEach"); + return iterator.close(); + } else { + test.fail("Can we stop an asynchronous forEach? " + index); + } + return null; + }); + await iterator.close(); + + // Ensuring that we find new files if they appear + let file = await OS.File.open(temporary_file_name, { write: true }); + file.close(); + iterator = new OS.File.DirectoryIterator(currentDir); + try { + let files = await iterator.nextBatch(); + is( + files.length, + allFiles1.length + 1, + "The directory iterator has noticed the new file" + ); + let exists = await iterator.exists(); + test.ok( + exists, + "After nextBatch, iterator detects that the directory exists" + ); + } finally { + await iterator.close(); + } + + // Ensuring that opening a non-existing directory fails consistently + // once iteration starts. + try { + iterator = null; + iterator = new OS.File.DirectoryIterator("/I do not exist"); + let exists = await iterator.exists(); + test.ok( + !exists, + "Before any iteration, iterator detects that the directory doesn't exist" + ); + let exn = null; + try { + await iterator.next(); + } catch (ex) { + if (ex instanceof OS.File.Error && ex.becauseNoSuchFile) { + exn = ex; + let exists = await iterator.exists(); + test.ok( + !exists, + "After one iteration, iterator detects that the directory doesn't exist" + ); + } else { + throw ex; + } + } + test.ok( + exn, + "Iterating through a directory that does not exist has failed with becauseNoSuchFile" + ); + } finally { + if (iterator) { + iterator.close(); + } + } + test.ok( + !!iterator, + "The directory iterator for a non-existing directory was correctly created" + ); + })(); +}); + +/** + * Test OS.File.prototype.{exists} + */ +var test_exists = maketest("exists", function exists(test) { + return (async function() { + let fileExists = await OS.File.exists(EXISTING_FILE); + test.ok(fileExists, "file exists"); + fileExists = await OS.File.exists(EXISTING_FILE + ".tmp"); + test.ok(!fileExists, "file does not exists"); + })(); +}); + +/** + * Test changes to OS.Shared.DEBUG flag. + */ +var test_debug = maketest("debug", function debug(test) { + return (async function() { + function testSetDebugPref(pref) { + try { + Services.prefs.setBoolPref("toolkit.osfile.log", pref); + } catch (x) { + test.fail( + "Setting OS.Shared.DEBUG to " + pref + " should not cause error." + ); + } finally { + test.is(OS.Shared.DEBUG, pref, "OS.Shared.DEBUG is set correctly."); + } + } + testSetDebugPref(true); + let workerDEBUG = await OS.File.GET_DEBUG(); + test.is(workerDEBUG, true, "Worker's DEBUG is set."); + testSetDebugPref(false); + workerDEBUG = await OS.File.GET_DEBUG(); + test.is(workerDEBUG, false, "Worker's DEBUG is unset."); + })(); +}); + +/** + * Test logging in the main thread with set OS.Shared.DEBUG and + * OS.Shared.TEST flags. + */ +var test_debug_test = maketest("debug_test", function debug_test(test) { + return (async function() { + // Create a console listener. + let consoleListener = { + observe(aMessage) { + // Ignore unexpected messages. + if (!(aMessage instanceof Ci.nsIConsoleMessage)) { + return; + } + if (!aMessage.message.includes("TEST OS")) { + return; + } + test.ok(true, "DEBUG TEST messages are logged correctly."); + }, + }; + toggleDebugTest(true, consoleListener); + // Execution of OS.File.exist method will trigger OS.File.LOG several times. + await OS.File.exists(EXISTING_FILE); + toggleDebugTest(false, consoleListener); + })(); +}); diff --git a/toolkit/components/osfile/tests/mochi/test_osfile_async.xhtml b/toolkit/components/osfile/tests/mochi/test_osfile_async.xhtml new file mode 100644 index 0000000000..89d8eba473 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/test_osfile_async.xhtml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Testing async I/O with OS.File" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" + src="main_test_osfile_async.js"/> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/toolkit/components/osfile/tests/mochi/test_osfile_back.xhtml b/toolkit/components/osfile/tests/mochi/test_osfile_back.xhtml new file mode 100644 index 0000000000..2451bd13a7 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/test_osfile_back.xhtml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Testing OS.File on a chrome worker thread" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/WorkerHandler.js"/> + <script type="application/javascript"> + <![CDATA[ + +let worker; + +function test() { + ok(true, "test_osfile.xul: Starting test"); + if (navigator.platform.includes("Win")) { + ok(true, "test_osfile.xul: Using Windows test suite"); + worker = new ChromeWorker("worker_test_osfile_win.js"); + } else { + ok(true, "test_osfile.xul: Using Unix test suite"); + worker = new ChromeWorker("worker_test_osfile_unix.js"); + } + SimpleTest.waitForExplicitFinish(); + ok(true, "test_osfile.xul: Chrome worker created"); + dump("MAIN: go\n"); + listenForTests(worker); + worker.postMessage(0); + ok(true, "test_osfile.xul: Test in progress"); +}; +]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/toolkit/components/osfile/tests/mochi/test_osfile_comms.xhtml b/toolkit/components/osfile/tests/mochi/test_osfile_comms.xhtml new file mode 100644 index 0000000000..fd31e7bee8 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/test_osfile_comms.xhtml @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Testing OS.File on a chrome worker thread" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript"> + <![CDATA[ + +"use strict"; + +let worker; + +SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal", + true]]}); +let test = function test() { + SimpleTest.info("test_osfile_comms.xhtml: Starting test"); + // These are used in the worker. + // eslint-disable-next-line no-unused-vars + let { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); + // eslint-disable-next-line no-unused-vars + let { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); + worker = new ChromeWorker("worker_test_osfile_comms.js"); + SimpleTest.waitForExplicitFinish(); + try { + worker.onerror = function onerror(error) { + SimpleTest.ok(false, "received error "+error); + } + worker.onmessage = function onmessage(msg) { + Cu.forceShrinkingGC(); + switch (msg.data.kind) { + case "is": + SimpleTest.ok(msg.data.outcome, msg.data.description + + " ("+ msg.data.a + " ==? " + msg.data.b + ")" ); + return; + case "isnot": + SimpleTest.ok(msg.data.outcome, msg.data.description + + " ("+ msg.data.a + " !=? " + msg.data.b + ")" ); + return; + case "ok": + SimpleTest.ok(msg.data.condition, msg.data.description); + return; + case "info": + SimpleTest.info(msg.data.description); + return; + case "finish": + SimpleTest.finish(); + return; + case "value": + SimpleTest.ok(true, "test_osfile_comms.xhtml: Received value " + JSON.stringify(msg.data.value)); + // eslint-disable-next-line no-eval + let type = eval(msg.data.typename); + // eslint-disable-next-line no-eval + let check = eval(msg.data.check); + let value = msg.data.value; + let deserialized = type.fromMsg(value); + check(deserialized, "Main thread test: "); + return; + default: + SimpleTest.ok(false, "test_osfile_comms.xhtml: wrong message "+JSON.stringify(msg.data)); + } + }; + worker.postMessage(0) + ok(true, "Worker launched"); + } catch(x) { + // As we have set |waitForExplicitFinish|, we add this fallback + // in case of uncaught error, to ensure that the test does not + // just freeze. + ok(false, "Caught exception " + x + " at " + x.stack); + SimpleTest.finish(); + } +}; + +]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/toolkit/components/osfile/tests/mochi/test_osfile_front.xhtml b/toolkit/components/osfile/tests/mochi/test_osfile_front.xhtml new file mode 100644 index 0000000000..0a13e2d8c8 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/test_osfile_front.xhtml @@ -0,0 +1,42 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Testing OS.File on a chrome worker thread" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/WorkerHandler.js"/> + <script type="application/javascript"> + <![CDATA[ + +let worker; +let main = this; + +function test() { + info("test_osfile_front.xhtml: Starting test"); + + // Test the OS.File worker + + worker = new ChromeWorker("worker_test_osfile_front.js"); + SimpleTest.waitForExplicitFinish(); + info("test_osfile_front.xhtml: Chrome worker created"); + dump("MAIN: go\n"); + listenForTests(worker); + worker.postMessage(0); + ok(true, "test_osfile_front.xhtml: Test in progress"); +}; +]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/toolkit/components/osfile/tests/mochi/worker_test_osfile_comms.js b/toolkit/components/osfile/tests/mochi/worker_test_osfile_comms.js new file mode 100644 index 0000000000..0cc1c59339 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_comms.js @@ -0,0 +1,205 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/chrome-worker, node */ + +"use strict"; + +/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ +importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); + +// The set of samples for communications test. Declare as a global +// variable to prevent this from being garbage-collected too early. +var samples; + +self.onmessage = function(msg) { + info("Initializing"); + self.onmessage = function on_unexpected_message(msg) { + throw new Error("Unexpected message " + JSON.stringify(msg.data)); + }; + /* import-globals-from /toolkit/components/osfile/osfile.jsm */ + importScripts("resource://gre/modules/osfile.jsm"); + info("Initialization complete"); + + samples = [ + { + typename: "OS.Shared.Type.char.in_ptr", + valuedescr: "String", + value: "This is a test", + type: OS.Shared.Type.char.in_ptr, + check: function check_string(candidate, prefix) { + is(candidate, "This is a test", prefix); + }, + }, + { + typename: "OS.Shared.Type.char.in_ptr", + valuedescr: "Typed array", + value: (function() { + let view = new Uint8Array(15); + for (let i = 0; i < 15; ++i) { + view[i] = i; + } + return view; + })(), + type: OS.Shared.Type.char.in_ptr, + check: function check_ArrayBuffer(candidate, prefix) { + for (let i = 0; i < 15; ++i) { + is( + candidate[i], + i % 256, + prefix + + "Checking that the contents of the ArrayBuffer were preserved" + ); + } + }, + }, + { + typename: "OS.Shared.Type.char.in_ptr", + valuedescr: "Pointer", + value: new OS.Shared.Type.char.in_ptr.implementation(1), + type: OS.Shared.Type.char.in_ptr, + check: function check_ptr(candidate, prefix) { + let address = ctypes.cast(candidate, ctypes.uintptr_t).value.toString(); + is( + address, + "1", + prefix + "Checking that the pointer address was preserved" + ); + }, + }, + { + typename: "OS.Shared.Type.char.in_ptr", + valuedescr: "C array", + value: (function() { + let buf = new (ctypes.ArrayType(ctypes.uint8_t, 15))(); + for (let i = 0; i < 15; ++i) { + buf[i] = i % 256; + } + return buf; + })(), + type: OS.Shared.Type.char.in_ptr, + check: function check_array(candidate, prefix) { + let cast = ctypes.cast(candidate, ctypes.uint8_t.ptr); + for (let i = 0; i < 15; ++i) { + is( + cast.contents, + i % 256, + prefix + + "Checking that the contents of the C array were preserved, index " + + i + ); + cast = cast.increment(); + } + }, + }, + { + typename: "OS.File.Error", + valuedescr: "OS Error", + type: OS.File.Error, + value: new OS.File.Error("foo", 1), + check: function check_error(candidate, prefix) { + ok( + candidate instanceof OS.File.Error, + prefix + "Error is an OS.File.Error" + ); + ok( + candidate.unixErrno == 1 || candidate.winLastError == 1, + prefix + "Error code is correct" + ); + try { + let string = candidate.toString(); + info(prefix + ".toString() works " + string); + } catch (x) { + ok(false, prefix + ".toString() fails " + x); + } + }, + }, + ]; + samples.forEach(function test(sample) { + let type = sample.type; + let value = sample.value; + let check = sample.check; + info( + "Testing handling of type " + + sample.typename + + " communicating " + + sample.valuedescr + ); + + // 1. Test serialization + let serialized; + let exn; + try { + serialized = type.toMsg(value); + } catch (ex) { + exn = ex; + } + is( + exn, + null, + "Can I serialize the following value? " + + value + + " aka " + + JSON.stringify(value) + ); + if (exn) { + return; + } + + if ("data" in serialized) { + // Unwrap from `Meta` + serialized = serialized.data; + } + + // 2. Test deserialization + let deserialized; + try { + deserialized = type.fromMsg(serialized); + } catch (ex) { + exn = ex; + } + is( + exn, + null, + "Can I deserialize the following message? " + + serialized + + " aka " + + JSON.stringify(serialized) + ); + if (exn) { + return; + } + + // 3. Local test deserialized value + info( + "Running test on deserialized value " + + deserialized + + " aka " + + JSON.stringify(deserialized) + ); + check(deserialized, "Local test: "); + + // 4. Test sending serialized + info("Attempting to send message"); + try { + self.postMessage({ + kind: "value", + typename: sample.typename, + value: serialized, + check: check.toSource(), + }); + } catch (ex) { + exn = ex; + } + is( + exn, + null, + "Can I send the following message? " + + serialized + + " aka " + + JSON.stringify(serialized) + ); + }); + + finish(); +}; diff --git a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js new file mode 100644 index 0000000000..4f7e590102 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js @@ -0,0 +1,696 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/chrome-worker, node */ + +/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ +importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); +/* import-globals-from /toolkit/components/workerloader/require.js */ +importScripts("resource://gre/modules/workers/require.js"); + +var SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); +SharedAll.Config.DEBUG = true; + +function should_throw(f) { + try { + f(); + } catch (x) { + return x; + } + return null; +} + +self.onmessage = function onmessage_start(msg) { + self.onmessage = function onmessage_ignored(msg) { + log("ignored message " + JSON.stringify(msg.data)); + }; + try { + test_init(); + test_open_existing_file(); + test_open_non_existing_file(); + test_flush_open_file(); + test_copy_existing_file(); + test_position(); + test_move_file(); + test_iter_dir(); + test_info(); + test_path(); + test_exists_file(); + test_remove_file(); + } catch (x) { + log("Catching error: " + x); + log("Stack: " + x.stack); + log("Source: " + x.toSource()); + ok(false, x.toString() + "\n" + x.stack); + } + finish(); +}; + +function test_init() { + info("Starting test_init"); + /* import-globals-from /toolkit/components/osfile/osfile.jsm */ + importScripts("resource://gre/modules/osfile.jsm"); +} + +/** + * Test that we can open an existing file. + */ +function test_open_existing_file() { + info("Starting test_open_existing"); + let file = OS.File.open( + "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js" + ); + file.close(); +} + +/** + * Test that opening a file that does not exist fails with the right error. + */ +function test_open_non_existing_file() { + info("Starting test_open_non_existing"); + let exn; + try { + OS.File.open("/I do not exist"); + } catch (x) { + exn = x; + info("test_open_non_existing_file: Exception detail " + exn); + } + ok(!!exn, "test_open_non_existing_file: Exception was raised "); + ok( + exn instanceof OS.File.Error, + "test_open_non_existing_file: Exception was a OS.File.Error" + ); + ok( + exn.becauseNoSuchFile, + "test_open_non_existing_file: Exception confirms that the file does not exist" + ); +} + +/** + * Test that to ensure that |foo.flush()| does not + * cause an error, where |foo| is an open file. + */ +function test_flush_open_file() { + info("Starting test_flush_open_file"); + let tmp = "test_flush.tmp"; + let file = OS.File.open(tmp, { create: true, write: true }); + file.flush(); + file.close(); + OS.File.remove(tmp); +} + +/** + * Utility function for comparing two files (or a prefix of two files). + * + * This function returns nothing but fails of both files (or prefixes) + * are not identical. + * + * @param {string} test The name of the test (used for logging). + * @param {string} sourcePath The name of the first file. + * @param {string} destPath The name of the second file. + * @param {number=} prefix If specified, only compare the |prefix| + * first bytes of |sourcePath| and |destPath|. + */ +function compare_files(test, sourcePath, destPath, prefix) { + info(test + ": Comparing " + sourcePath + " and " + destPath); + let source = OS.File.open(sourcePath); + let dest = OS.File.open(destPath); + info("Files are open"); + let sourceResult, destResult; + try { + if (prefix != undefined) { + sourceResult = source.read(prefix); + destResult = dest.read(prefix); + } else { + sourceResult = source.read(); + destResult = dest.read(); + } + is( + sourceResult.length, + destResult.length, + test + ": Both files have the same size" + ); + for (let i = 0; i < sourceResult.length; ++i) { + if (sourceResult[i] != destResult[i]) { + is(sourceResult[i] != destResult[i], test + ": Comparing char " + i); + break; + } + } + } finally { + source.close(); + dest.close(); + } + info(test + ": Comparison complete"); +} + +/** + * Test that copying a file using |copy| works. + */ +function test_copy_existing_file() { + let src_file_name = OS.Path.join( + "chrome", + "toolkit", + "components", + "osfile", + "tests", + "mochi", + "worker_test_osfile_front.js" + ); + let tmp_file_name = "test_osfile_front.tmp"; + info("Starting test_copy_existing"); + OS.File.copy(src_file_name, tmp_file_name); + + info("test_copy_existing: Copy complete"); + compare_files("test_copy_existing", src_file_name, tmp_file_name); + + // Create a bogus file with arbitrary content, then attempt to overwrite + // it with |copy|. + let dest = OS.File.open(tmp_file_name, { trunc: true }); + let buf = new Uint8Array(50); + dest.write(buf); + dest.close(); + + OS.File.copy(src_file_name, tmp_file_name); + + compare_files("test_copy_existing 2", src_file_name, tmp_file_name); + + // Attempt to overwrite with noOverwrite + let exn; + try { + OS.File.copy(src_file_name, tmp_file_name, { noOverwrite: true }); + } catch (x) { + exn = x; + } + ok( + !!exn, + "test_copy_existing: noOverwrite prevents overwriting existing files" + ); + + info("test_copy_existing: Cleaning up"); + OS.File.remove(tmp_file_name); +} + +/** + * Test that moving a file works. + */ +function test_move_file() { + info("test_move_file: Starting"); + // 1. Copy file into a temporary file + let src_file_name = OS.Path.join( + "chrome", + "toolkit", + "components", + "osfile", + "tests", + "mochi", + "worker_test_osfile_front.js" + ); + let tmp_file_name = "test_osfile_front.tmp"; + let tmp2_file_name = "test_osfile_front.tmp2"; + OS.File.copy(src_file_name, tmp_file_name); + + info("test_move_file: Copy complete"); + + // 2. Move + OS.File.move(tmp_file_name, tmp2_file_name); + + info("test_move_file: Move complete"); + + // 3. Check that destination exists + compare_files("test_move_file", src_file_name, tmp2_file_name); + + // 4. Check that original file does not exist anymore + let exn; + try { + OS.File.open(tmp_file_name); + } catch (x) { + exn = x; + } + ok(!!exn, "test_move_file: Original file has been removed"); + + info("test_move_file: Cleaning up"); + OS.File.remove(tmp2_file_name); +} + +function test_iter_dir() { + info("test_iter_dir: Starting"); + + // Create a file, to be sure that it exists + let tmp_file_name = "test_osfile_front.tmp"; + let tmp_file = OS.File.open(tmp_file_name, { write: true, trunc: true }); + tmp_file.close(); + + let parent = OS.File.getCurrentDirectory(); + info("test_iter_dir: directory " + parent); + let iterator = new OS.File.DirectoryIterator(parent); + info("test_iter_dir: iterator created"); + let encountered_tmp_file = false; + for (let entry of iterator) { + // Checking that |name| can be decoded properly + info("test_iter_dir: encountering entry " + entry.name); + + if (entry.name == tmp_file_name) { + encountered_tmp_file = true; + isnot( + entry.isDir, + "test_iter_dir: The temporary file is not a directory" + ); + isnot(entry.isSymLink, "test_iter_dir: The temporary file is not a link"); + } + + let file; + let success = true; + try { + file = OS.File.open(entry.path); + } catch (x) { + if (x.becauseNoSuchFile) { + success = false; + } + } + if (file) { + file.close(); + } + ok(success, "test_iter_dir: Entry " + entry.path + " exists"); + + if (OS.Win) { + // We assume that the files are at least as recent as 2009. + // Since this test was written in 2011 and some of our packaging + // sets dates arbitrarily to 2010, this should be safe. + let year = new Date().getFullYear(); + + let lastWrite = entry.winLastWriteDate; + ok( + lastWrite, + "test_iter_dir: Windows lastWrite date exists: " + lastWrite + ); + ok( + lastWrite.getFullYear() >= 2009 && lastWrite.getFullYear() <= year, + "test_iter_dir: consistent lastWrite date" + ); + + let lastAccess = entry.winLastAccessDate; + ok( + lastAccess, + "test_iter_dir: Windows lastAccess date exists: " + lastAccess + ); + ok( + lastAccess.getFullYear() >= 2009 && lastAccess.getFullYear() <= year, + "test_iter_dir: consistent lastAccess date" + ); + } + } + ok(encountered_tmp_file, "test_iter_dir: We have found the temporary file"); + + info("test_iter_dir: Cleaning up"); + iterator.close(); + + // Testing nextBatch() + iterator = new OS.File.DirectoryIterator(parent); + let allentries = []; + for (let x of iterator) { + allentries.push(x); + } + iterator.close(); + + ok( + allentries.length >= 14, + "test_iter_dir: Meta-check: the test directory should contain at least 14 items" + ); + + iterator = new OS.File.DirectoryIterator(parent); + let firstten = iterator.nextBatch(10); + is(firstten.length, 10, "test_iter_dir: nextBatch(10) returns 10 items"); + for (let i = 0; i < firstten.length; ++i) { + is( + allentries[i].path, + firstten[i].path, + "test_iter_dir: Checking that batch returns the correct entries" + ); + } + let nextthree = iterator.nextBatch(3); + is(nextthree.length, 3, "test_iter_dir: nextBatch(3) returns 3 items"); + for (let i = 0; i < nextthree.length; ++i) { + is( + allentries[i + firstten.length].path, + nextthree[i].path, + "test_iter_dir: Checking that batch 2 returns the correct entries" + ); + } + let everythingelse = iterator.nextBatch(); + ok( + everythingelse.length >= 1, + "test_iter_dir: nextBatch() returns at least one item" + ); + for (let i = 0; i < everythingelse.length; ++i) { + is( + allentries[i + firstten.length + nextthree.length].path, + everythingelse[i].path, + "test_iter_dir: Checking that batch 3 returns the correct entries" + ); + } + is( + iterator.nextBatch().length, + 0, + "test_iter_dir: Once there is nothing left, nextBatch returns an empty array" + ); + iterator.close(); + + iterator = new OS.File.DirectoryIterator(parent); + iterator.close(); + is( + iterator.nextBatch().length, + 0, + "test_iter_dir: nextBatch on closed iterator returns an empty array" + ); + + iterator = new OS.File.DirectoryIterator(parent); + let allentries2 = iterator.nextBatch(); + is( + allentries.length, + allentries2.length, + "test_iter_dir: Checking that getBatch(null) returns the right number of entries" + ); + for (let i = 0; i < allentries.length; ++i) { + is( + allentries[i].path, + allentries2[i].path, + "test_iter_dir: Checking that getBatch(null) returns everything in the right order" + ); + } + iterator.close(); + + // Test forEach + iterator = new OS.File.DirectoryIterator(parent); + let index = 0; + iterator.forEach(function cb(entry, aIndex, aIterator) { + is(index, aIndex, "test_iter_dir: Checking that forEach index is correct"); + ok( + iterator == aIterator, + "test_iter_dir: Checking that right iterator is passed" + ); + if (index < 10) { + is( + allentries[index].path, + entry.path, + "test_iter_dir: Checking that forEach entry is correct" + ); + } else if (index == 10) { + iterator.close(); + } else { + ok(false, "test_iter_dir: Checking that forEach can be stopped early"); + } + ++index; + }); + iterator.close(); + + // test for prototype |OS.File.DirectoryIterator.unixAsFile| + if ("unixAsFile" in OS.File.DirectoryIterator.prototype) { + info("testing property unixAsFile"); + let path = OS.Path.join( + "chrome", + "toolkit", + "components", + "osfile", + "tests", + "mochi" + ); + iterator = new OS.File.DirectoryIterator(path); + + let dir_file = iterator.unixAsFile(); // return |File| + let stat0 = dir_file.stat(); + let stat1 = OS.File.stat(path); + + let unix_info_to_string = function unix_info_to_string(info) { + return ( + "| " + + info.unixMode + + " | " + + info.unixOwner + + " | " + + info.unixGroup + + " | " + + info.lastModificationDate + + " | " + + info.lastAccessDate + + " | " + + info.size + + " |" + ); + }; + + let s0_string = unix_info_to_string(stat0); + let s1_string = unix_info_to_string(stat1); + + ok(stat0.isDir, "unixAsFile returned a directory"); + is(s0_string, s1_string, "unixAsFile returned the correct file"); + dir_file.close(); + iterator.close(); + } + info("test_iter_dir: Complete"); +} + +function test_position() { + info("test_position: Starting"); + + ok("POS_START" in OS.File, "test_position: POS_START exists"); + ok("POS_CURRENT" in OS.File, "test_position: POS_CURRENT exists"); + ok("POS_END" in OS.File, "test_position: POS_END exists"); + + let ARBITRARY_POSITION = 321; + let src_file_name = OS.Path.join( + "chrome", + "toolkit", + "components", + "osfile", + "tests", + "mochi", + "worker_test_osfile_front.js" + ); + + let file = OS.File.open(src_file_name); + is(file.getPosition(), 0, "test_position: Initial position is 0"); + + let size = 0 + file.stat().size; // Hack: We can remove this 0 + once 776259 has landed + + file.setPosition(ARBITRARY_POSITION, OS.File.POS_START); + is( + file.getPosition(), + ARBITRARY_POSITION, + "test_position: Setting position from start" + ); + + file.setPosition(0, OS.File.POS_START); + is( + file.getPosition(), + 0, + "test_position: Setting position from start back to 0" + ); + + file.setPosition(ARBITRARY_POSITION); + is( + file.getPosition(), + ARBITRARY_POSITION, + "test_position: Setting position without argument" + ); + + file.setPosition(-ARBITRARY_POSITION, OS.File.POS_END); + is( + file.getPosition(), + size - ARBITRARY_POSITION, + "test_position: Setting position from end" + ); + + file.setPosition(ARBITRARY_POSITION, OS.File.POS_CURRENT); + is(file.getPosition(), size, "test_position: Setting position from current"); + + file.close(); + info("test_position: Complete"); +} + +function test_info() { + info("test_info: Starting"); + + let filename = "test_info.tmp"; + let size = 261; // An arbitrary file length + let start = new Date(); + + // Cleanup any leftover from previous tests + try { + OS.File.remove(filename); + info("test_info: Cleaned up previous garbage"); + } catch (x) { + if (!x.becauseNoSuchFile) { + throw x; + } + info("test_info: No previous garbage"); + } + + let file = OS.File.open(filename, { trunc: true }); + let buf = new ArrayBuffer(size); + file._write(buf, size); + file.close(); + + // Test OS.File.stat on new file + let stat = OS.File.stat(filename); + ok(!!stat, "test_info: info acquired"); + ok(!stat.isDir, "test_info: file is not a directory"); + is(stat.isSymLink, false, "test_info: file is not a link"); + is(stat.size.toString(), size, "test_info: correct size"); + + let stop = new Date(); + + // We round down/up by 1s as file system precision is lower than + // Date precision (no clear specifications about that, but it seems + // that this can be a little over 1 second under ext3 and 2 seconds + // under FAT). + let SLOPPY_FILE_SYSTEM_ADJUSTMENT = 3000; + let startMs = start.getTime() - SLOPPY_FILE_SYSTEM_ADJUSTMENT; + let stopMs = stop.getTime() + SLOPPY_FILE_SYSTEM_ADJUSTMENT; + info("Testing stat with bounds [ " + startMs + ", " + stopMs + " ]"); + + let change = stat.lastModificationDate; + info("Testing lastModificationDate: " + change); + ok( + change.getTime() >= startMs && change.getTime() <= stopMs, + "test_info: lastModificationDate is consistent" + ); + + // Test OS.File.prototype.stat on new file + file = OS.File.open(filename); + try { + stat = file.stat(); + } finally { + file.close(); + } + + ok(!!stat, "test_info: info acquired 2"); + ok(!stat.isDir, "test_info: file is not a directory 2"); + ok(!stat.isSymLink, "test_info: file is not a link 2"); + is(stat.size.toString(), size, "test_info: correct size 2"); + + stop = new Date(); + + // Round up/down as above + startMs = start.getTime() - SLOPPY_FILE_SYSTEM_ADJUSTMENT; + stopMs = stop.getTime() + SLOPPY_FILE_SYSTEM_ADJUSTMENT; + info("Testing stat 2 with bounds [ " + startMs + ", " + stopMs + " ]"); + + let access = stat.lastAccessDate; + info("Testing lastAccessDate: " + access); + ok( + access.getTime() >= startMs && access.getTime() <= stopMs, + "test_info: lastAccessDate is consistent" + ); + + change = stat.lastModificationDate; + info("Testing lastModificationDate 2: " + change); + ok( + change.getTime() >= startMs && change.getTime() <= stopMs, + "test_info: lastModificationDate 2 is consistent" + ); + + // Test OS.File.stat on directory + stat = OS.File.stat(OS.File.getCurrentDirectory()); + ok(!!stat, "test_info: info on directory acquired"); + ok(stat.isDir, "test_info: directory is a directory"); + + info("test_info: Complete"); +} + +// Note that most of the features of path are tested in +// worker_test_osfile_{unix, win}.js +function test_path() { + info("test_path: starting"); + let abcd = OS.Path.join("a", "b", "c", "d"); + is(OS.Path.basename(abcd), "d", "basename of a/b/c/d"); + + let abc = OS.Path.join("a", "b", "c"); + is(OS.Path.dirname(abcd), abc, "dirname of a/b/c/d"); + + let abdotsc = OS.Path.join("a", "b", "..", "c"); + is(OS.Path.normalize(abdotsc), OS.Path.join("a", "c"), "normalize a/b/../c"); + + let adotsdotsdots = OS.Path.join("a", "..", "..", ".."); + is( + OS.Path.normalize(adotsdotsdots), + OS.Path.join("..", ".."), + "normalize a/../../.." + ); + + info("test_path: Complete"); +} + +/** + * Test the file |exists| method. + */ +function test_exists_file() { + let file_name = OS.Path.join( + "chrome", + "toolkit", + "components", + "osfile", + "tests", + "mochi", + "test_osfile_front.xhtml" + ); + info("test_exists_file: starting"); + ok( + OS.File.exists(file_name), + "test_exists_file: file exists (OS.File.exists)" + ); + ok( + !OS.File.exists(file_name + ".tmp"), + "test_exists_file: file does not exists (OS.File.exists)" + ); + + let dir_name = OS.Path.join( + "chrome", + "toolkit", + "components", + "osfile", + "tests", + "mochi" + ); + ok(OS.File.exists(dir_name), "test_exists_file: directory exists"); + ok( + !OS.File.exists(dir_name) + ".tmp", + "test_exists_file: directory does not exist" + ); + + info("test_exists_file: complete"); +} + +/** + * Test the file |remove| method. + */ +function test_remove_file() { + let absent_file_name = "test_osfile_front_absent.tmp"; + + // Check that removing absent files is handled correctly + let exn = should_throw(function() { + OS.File.remove(absent_file_name, { ignoreAbsent: false }); + }); + ok(!!exn, "test_remove_file: throws if there is no such file"); + + exn = should_throw(function() { + OS.File.remove(absent_file_name, { ignoreAbsent: true }); + OS.File.remove(absent_file_name); + }); + ok(!exn, "test_remove_file: ignoreAbsent works"); + + if (OS.Win) { + let file_name = "test_osfile_front_file_to_remove.tmp"; + let file = OS.File.open(file_name, { write: true }); + file.close(); + ok(OS.File.exists(file_name), "test_remove_file: test file exists"); + OS.Win.File.SetFileAttributes( + file_name, + OS.Constants.Win.FILE_ATTRIBUTE_READONLY + ); + OS.File.remove(file_name); + ok( + !OS.File.exists(file_name), + "test_remove_file: test file has been removed" + ); + } +} diff --git a/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js b/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js new file mode 100644 index 0000000000..aa456da030 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js @@ -0,0 +1,257 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/chrome-worker, node */ + +/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ +importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); + +self.onmessage = function(msg) { + log("received message " + JSON.stringify(msg.data)); + self.onmessage = function(msg) { + log("ignored message " + JSON.stringify(msg.data)); + }; + test_init(); + test_getcwd(); + test_open_close(); + test_create_file(); + test_access(); + test_read_write(); + test_passing_undefined(); + finish(); +}; + +function test_init() { + info("Starting test_init"); + /* import-globals-from /toolkit/components/osfile/osfile.jsm */ + importScripts("resource://gre/modules/osfile.jsm"); +} + +function test_open_close() { + info("Starting test_open_close"); + is(typeof OS.Unix.File.open, "function", "OS.Unix.File.open is a function"); + let file = OS.Unix.File.open( + "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js", + OS.Constants.libc.O_RDONLY + ); + isnot(file, -1, "test_open_close: opening succeeded"); + info("Close: " + OS.Unix.File.close.toSource()); + let result = OS.Unix.File.close(file); + is(result, 0, "test_open_close: close succeeded"); + + file = OS.Unix.File.open("/i do not exist", OS.Constants.libc.O_RDONLY); + is(file, -1, "test_open_close: opening of non-existing file failed"); + is( + ctypes.errno, + OS.Constants.libc.ENOENT, + "test_open_close: error is ENOENT" + ); +} + +function test_create_file() { + info("Starting test_create_file"); + let file = OS.Unix.File.open( + "test.tmp", + OS.Constants.libc.O_RDWR | + OS.Constants.libc.O_CREAT | + OS.Constants.libc.O_TRUNC, + ctypes.int(OS.Constants.libc.S_IRWXU) + ); + isnot(file, -1, "test_create_file: file created"); + OS.Unix.File.close(file); +} + +function test_access() { + info("Starting test_access"); + let file = OS.Unix.File.open( + "test1.tmp", + OS.Constants.libc.O_RDWR | + OS.Constants.libc.O_CREAT | + OS.Constants.libc.O_TRUNC, + ctypes.int(OS.Constants.libc.S_IRWXU) + ); + let result = OS.Unix.File.access( + "test1.tmp", + OS.Constants.libc.R_OK | + OS.Constants.libc.W_OK | + OS.Constants.libc.X_OK | + OS.Constants.libc.F_OK + ); + is(result, 0, "first call to access() succeeded"); + OS.Unix.File.close(file); + + file = OS.Unix.File.open( + "test1.tmp", + OS.Constants.libc.O_WRONLY | + OS.Constants.libc.O_CREAT | + OS.Constants.libc.O_TRUNC, + ctypes.int(OS.Constants.libc.S_IWUSR) + ); + + info("test_access: preparing second call to access()"); + result = OS.Unix.File.access( + "test2.tmp", + OS.Constants.libc.R_OK | + OS.Constants.libc.W_OK | + OS.Constants.libc.X_OK | + OS.Constants.libc.F_OK + ); + is(result, -1, "test_access: second call to access() failed as expected"); + is(ctypes.errno, OS.Constants.libc.ENOENT, "This is the correct error"); + OS.Unix.File.close(file); +} + +function test_getcwd() { + let array = new (ctypes.ArrayType(ctypes.char, 32768))(); + let path = OS.Unix.File.getcwd(array, array.length); + if (ctypes.char.ptr(path).isNull()) { + ok(false, "test_get_cwd: getcwd returned null, errno: " + ctypes.errno); + } + let path2; + if (OS.Unix.File.get_current_dir_name) { + path2 = OS.Unix.File.get_current_dir_name(); + } else { + path2 = OS.Unix.File.getwd_auto(null); + } + if (ctypes.char.ptr(path2).isNull()) { + ok( + false, + "test_get_cwd: getwd_auto/get_current_dir_name returned null, errno: " + + ctypes.errno + ); + } + is( + path.readString(), + path2.readString(), + "test_get_cwd: getcwd and getwd return the same path" + ); +} + +function test_read_write() { + let output_name = "osfile_copy.tmp"; + // Copy file + let input = OS.Unix.File.open( + "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js", + OS.Constants.libc.O_RDONLY + ); + isnot(input, -1, "test_read_write: input file opened"); + let output = OS.Unix.File.open( + "osfile_copy.tmp", + OS.Constants.libc.O_RDWR | + OS.Constants.libc.O_CREAT | + OS.Constants.libc.O_TRUNC, + ctypes.int(OS.Constants.libc.S_IRWXU) + ); + isnot(output, -1, "test_read_write: output file opened"); + + let array = new (ctypes.ArrayType(ctypes.char, 4096))(); + let bytes = -1; + let total = 0; + while (true) { + bytes = OS.Unix.File.read(input, array, 4096); + ok(bytes != undefined, "test_read_write: bytes is defined"); + isnot(bytes, -1, "test_read_write: no read error"); + let write_from = 0; + if (bytes == 0) { + break; + } + while (bytes > 0) { + array.addressOfElement(write_from); + // Note: |write| launches an exception in case of error + let written = OS.Unix.File.write(output, array, bytes); + isnot(written, -1, "test_read_write: no write error"); + write_from += written; + bytes -= written; + } + total += write_from; + } + info("test_read_write: copy complete " + total); + + // Compare files + let result; + info("SEEK_SET: " + OS.Constants.libc.SEEK_SET); + info("Input: " + input + "(" + input.toSource() + ")"); + info("Output: " + output + "(" + output.toSource() + ")"); + result = OS.Unix.File.lseek(input, 0, OS.Constants.libc.SEEK_SET); + info("Result of lseek: " + result); + isnot(result, -1, "test_read_write: input seek succeeded " + ctypes.errno); + result = OS.Unix.File.lseek(output, 0, OS.Constants.libc.SEEK_SET); + isnot(result, -1, "test_read_write: output seek succeeded " + ctypes.errno); + + let array2 = new (ctypes.ArrayType(ctypes.char, 4096))(); + let bytes2 = -1; + let pos = 0; + while (true) { + bytes = OS.Unix.File.read(input, array, 4096); + isnot(bytes, -1, "test_read_write: input read succeeded"); + bytes2 = OS.Unix.File.read(output, array2, 4096); + isnot(bytes, -1, "test_read_write: output read succeeded"); + is( + bytes > 0, + bytes2 > 0, + "Both files contain data or neither does " + bytes + ", " + bytes2 + ); + if (bytes == 0) { + break; + } + if (bytes != bytes2) { + // This would be surprising, but theoretically possible with a + // remote file system, I believe. + bytes = Math.min(bytes, bytes2); + pos += bytes; + result = OS.Unix.File.lseek(input, pos, OS.Constants.libc.SEEK_SET); + isnot(result, -1, "test_read_write: input seek succeeded"); + result = OS.Unix.File.lseek(output, pos, OS.Constants.libc.SEEK_SET); + isnot(result, -1, "test_read_write: output seek succeeded"); + } else { + pos += bytes; + } + for (let i = 0; i < bytes; ++i) { + if (array[i] != array2[i]) { + ok( + false, + "Files do not match at position " + + i + + " (" + + array[i] + + "/" + + array2[i] + + ")" + ); + } + } + } + info("test_read_write test complete"); + result = OS.Unix.File.close(input); + isnot(result, -1, "test_read_write: input close succeeded"); + result = OS.Unix.File.close(output); + isnot(result, -1, "test_read_write: output close succeeded"); + result = OS.Unix.File.unlink(output_name); + isnot(result, -1, "test_read_write: input remove succeeded"); + info("test_read_write cleanup complete"); +} + +function test_passing_undefined() { + info( + "Testing that an exception gets thrown when an FFI function is passed undefined" + ); + let exceptionRaised = false; + + try { + OS.Unix.File.open( + undefined, + OS.Constants.libc.O_RDWR | + OS.Constants.libc.O_CREAT | + OS.Constants.libc.O_TRUNC, + ctypes.int(OS.Constants.libc.S_IRWXU) + ); + } catch (e) { + if (e instanceof TypeError && e.message.indexOf("open") > -1) { + exceptionRaised = true; + } else { + throw e; + } + } + + ok(exceptionRaised, "test_passing_undefined: exception gets thrown"); +} diff --git a/toolkit/components/osfile/tests/mochi/worker_test_osfile_win.js b/toolkit/components/osfile/tests/mochi/worker_test_osfile_win.js new file mode 100644 index 0000000000..5e02c03998 --- /dev/null +++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_win.js @@ -0,0 +1,310 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/chrome-worker, node */ + +/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ +importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); + +self.onmessage = function(msg) { + self.onmessage = function(msg) { + log("ignored message " + JSON.stringify(msg.data)); + }; + + test_init(); + test_GetCurrentDirectory(); + test_OpenClose(); + test_CreateFile(); + test_ReadWrite(); + test_passing_undefined(); + finish(); +}; + +function test_init() { + info("Starting test_init"); + /* import-globals-from /toolkit/components/osfile/osfile.jsm */ + importScripts("resource://gre/modules/osfile.jsm"); +} + +function test_OpenClose() { + info("Starting test_OpenClose"); + is( + typeof OS.Win.File.CreateFile, + "function", + "OS.Win.File.CreateFile is a function" + ); + is( + OS.Win.File.CloseHandle(OS.Constants.Win.INVALID_HANDLE_VALUE), + true, + "CloseHandle returns true given the invalid handle" + ); + is( + OS.Win.File.FindClose(OS.Constants.Win.INVALID_HANDLE_VALUE), + true, + "FindClose returns true given the invalid handle" + ); + isnot(OS.Constants.Win.GENERIC_READ, undefined, "GENERIC_READ exists"); + isnot(OS.Constants.Win.FILE_SHARE_READ, undefined, "FILE_SHARE_READ exists"); + isnot( + OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, + undefined, + "FILE_ATTRIBUTE_NORMAL exists" + ); + let file = OS.Win.File.CreateFile( + "chrome\\toolkit\\components\\osfile\\tests\\mochi\\worker_test_osfile_win.js", + OS.Constants.Win.GENERIC_READ, + 0, + null, + OS.Constants.Win.OPEN_EXISTING, + 0, + null + ); + info("test_OpenClose: Passed open"); + isnot( + file, + OS.Constants.Win.INVALID_HANDLE_VALUE, + "test_OpenClose: file opened" + ); + let result = OS.Win.File.CloseHandle(file); + isnot(result, 0, "test_OpenClose: close succeeded"); + + file = OS.Win.File.CreateFile( + "\\I do not exist", + OS.Constants.Win.GENERIC_READ, + OS.Constants.Win.FILE_SHARE_READ, + null, + OS.Constants.Win.OPEN_EXISTING, + OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, + null + ); + is( + file, + OS.Constants.Win.INVALID_HANDLE_VALUE, + "test_OpenClose: cannot open non-existing file" + ); + is( + ctypes.winLastError, + OS.Constants.Win.ERROR_FILE_NOT_FOUND, + "test_OpenClose: error is ERROR_FILE_NOT_FOUND" + ); +} + +function test_CreateFile() { + info("Starting test_CreateFile"); + let file = OS.Win.File.CreateFile( + "test.tmp", + OS.Constants.Win.GENERIC_READ | OS.Constants.Win.GENERIC_WRITE, + OS.Constants.Win.FILE_SHARE_READ | OS.Constants.FILE_SHARE_WRITE, + null, + OS.Constants.Win.CREATE_ALWAYS, + OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, + null + ); + isnot( + file, + OS.Constants.Win.INVALID_HANDLE_VALUE, + "test_CreateFile: opening succeeded" + ); + let result = OS.Win.File.CloseHandle(file); + isnot(result, 0, "test_CreateFile: close succeeded"); +} + +function test_GetCurrentDirectory() { + let array = new (ctypes.ArrayType(ctypes.char16_t, 4096))(); + let result = OS.Win.File.GetCurrentDirectory(4096, array); + ok(result < array.length, "test_GetCurrentDirectory: length sufficient"); + ok(result > 0, "test_GetCurrentDirectory: length != 0"); +} + +function test_ReadWrite() { + info("Starting test_ReadWrite"); + let output_name = "osfile_copy.tmp"; + // Copy file + let input = OS.Win.File.CreateFile( + "chrome\\toolkit\\components\\osfile\\tests\\mochi\\worker_test_osfile_win.js", + OS.Constants.Win.GENERIC_READ, + 0, + null, + OS.Constants.Win.OPEN_EXISTING, + 0, + null + ); + isnot( + input, + OS.Constants.Win.INVALID_HANDLE_VALUE, + "test_ReadWrite: input file opened" + ); + let output = OS.Win.File.CreateFile( + "osfile_copy.tmp", + OS.Constants.Win.GENERIC_READ | OS.Constants.Win.GENERIC_WRITE, + 0, + null, + OS.Constants.Win.CREATE_ALWAYS, + OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, + null + ); + isnot( + output, + OS.Constants.Win.INVALID_HANDLE_VALUE, + "test_ReadWrite: output file opened" + ); + let array = new (ctypes.ArrayType(ctypes.char, 4096))(); + let bytes_read = new ctypes.uint32_t(0); + let bytes_read_ptr = bytes_read.address(); + log("We have a pointer for bytes read: " + bytes_read_ptr); + let bytes_written = new ctypes.uint32_t(0); + let bytes_written_ptr = bytes_written.address(); + log("We have a pointer for bytes written: " + bytes_written_ptr); + log("test_ReadWrite: buffer and pointers ready"); + let result; + while (true) { + log("test_ReadWrite: reading"); + result = OS.Win.File.ReadFile(input, array, 4096, bytes_read_ptr, null); + isnot(result, 0, "test_ReadWrite: read success"); + let write_from = 0; + let bytes_left = bytes_read; + log("test_ReadWrite: read chunk complete " + bytes_left.value); + if (bytes_left.value == 0) { + break; + } + while (bytes_left.value > 0) { + log("test_ReadWrite: writing " + bytes_left.value); + array.addressOfElement(write_from); + // Note: |WriteFile| launches an exception in case of error + result = OS.Win.File.WriteFile( + output, + array, + bytes_left, + bytes_written_ptr, + null + ); + isnot(result, 0, "test_ReadWrite: write success"); + write_from += bytes_written; + bytes_left -= bytes_written; + } + } + info("test_ReadWrite: copy complete"); + + // Compare files + result = OS.Win.File.SetFilePointer( + input, + 0, + null, + OS.Constants.Win.FILE_BEGIN + ); + isnot( + result, + OS.Constants.Win.INVALID_SET_FILE_POINTER, + "test_ReadWrite: input reset" + ); + + result = OS.Win.File.SetFilePointer( + output, + 0, + null, + OS.Constants.Win.FILE_BEGIN + ); + isnot( + result, + OS.Constants.Win.INVALID_SET_FILE_POINTER, + "test_ReadWrite: output reset" + ); + + let array2 = new (ctypes.ArrayType(ctypes.char, 4096))(); + let bytes_read2 = new ctypes.uint32_t(0); + let bytes_read2_ptr = bytes_read2.address(); + let pos = 0; + while (true) { + result = OS.Win.File.ReadFile(input, array, 4096, bytes_read_ptr, null); + isnot(result, 0, "test_ReadWrite: input read succeeded"); + + result = OS.Win.File.ReadFile(output, array2, 4096, bytes_read2_ptr, null); + isnot(result, 0, "test_ReadWrite: output read succeeded"); + + is( + bytes_read.value > 0, + bytes_read2.value > 0, + "Both files contain data or neither does " + + bytes_read.value + + ", " + + bytes_read2.value + ); + if (bytes_read.value == 0) { + break; + } + let bytes; + if (bytes_read.value != bytes_read2.value) { + // This would be surprising, but theoretically possible with a + // remote file system, I believe. + bytes = Math.min(bytes_read.value, bytes_read2.value); + pos += bytes; + result = OS.Win.File.SetFilePointer( + input, + pos, + null, + OS.Constants.Win.FILE_BEGIN + ); + isnot(result, 0, "test_ReadWrite: input seek succeeded"); + + result = OS.Win.File.SetFilePointer( + output, + pos, + null, + OS.Constants.Win.FILE_BEGIN + ); + isnot(result, 0, "test_ReadWrite: output seek succeeded"); + } else { + bytes = bytes_read.value; + pos += bytes; + } + for (let i = 0; i < bytes; ++i) { + if (array[i] != array2[i]) { + ok( + false, + "Files do not match at position " + + i + + " (" + + array[i] + + "/" + + array2[i] + + ")" + ); + } + } + } + info("test_ReadWrite test complete"); + result = OS.Win.File.CloseHandle(input); + isnot(result, 0, "test_ReadWrite: inpout close succeeded"); + result = OS.Win.File.CloseHandle(output); + isnot(result, 0, "test_ReadWrite: outpout close succeeded"); + result = OS.Win.File.DeleteFile(output_name); + isnot(result, 0, "test_ReadWrite: output remove succeeded"); + info("test_ReadWrite cleanup complete"); +} + +function test_passing_undefined() { + info( + "Testing that an exception gets thrown when an FFI function is passed undefined" + ); + let exceptionRaised = false; + + try { + OS.Win.File.CreateFile( + undefined, + OS.Constants.Win.GENERIC_READ, + 0, + null, + OS.Constants.Win.OPEN_EXISTING, + 0, + null + ); + } catch (e) { + if (e instanceof TypeError && e.message.indexOf("CreateFile") > -1) { + exceptionRaised = true; + } else { + throw e; + } + } + + ok(exceptionRaised, "test_passing_undefined: exception gets thrown"); +} |