Bug 846848 - UNC-formated paths. r=froydnj
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Fri, 29 Mar 2013 11:29:28 -0400
changeset 126708 8db756a2bcd17abb8c849847668b370091f18556
parent 126707 214782bec2a6cf03ac581b019e1e85986e6e4f8f
child 126709 6d50859261b0cdf4c5a6c05bc4a6387ebc9b9b31
push id25603
push userryanvm@gmail.com
push dateFri, 29 Mar 2013 15:29:15 +0000
treeherdermozilla-inbound@76d14e65da84 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs846848
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 846848 - UNC-formated paths. r=froydnj
toolkit/components/osfile/osfile_unix_allthreads.jsm
toolkit/components/osfile/osfile_win_allthreads.jsm
toolkit/components/osfile/ospath_unix_back.jsm
toolkit/components/osfile/ospath_win_back.jsm
toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js
toolkit/components/osfile/tests/mochi/worker_test_osfile_win.js
toolkit/components/osfile/tests/xpcshell/test_path.js
toolkit/components/osfile/tests/xpcshell/xpcshell.ini
--- a/toolkit/components/osfile/osfile_unix_allthreads.jsm
+++ b/toolkit/components/osfile/osfile_unix_allthreads.jsm
@@ -46,17 +46,18 @@ if (typeof Components != "undefined") {
     try {
       libc = ctypes.open(libc_candidates[i]);
       break;
     } catch (x) {
       LOG("Could not open libc ", libc_candidates[i]);
     }
   }
   if (!libc) {
-    throw new Error("Could not open any libc.");
+    // Note: If you change the string here, please adapt tests accordingly
+    throw new Error("Could not open system library: no libc");
   }
   exports.OS.Shared.Unix.libc = libc;
 
   // Define declareFFI
   let declareFFI = OS.Shared.declareFFI.bind(null, libc);
   exports.OS.Shared.Unix.declareFFI = declareFFI;
 
   // Define Error
--- a/toolkit/components/osfile/osfile_win_allthreads.jsm
+++ b/toolkit/components/osfile/osfile_win_allthreads.jsm
@@ -33,19 +33,23 @@ if (typeof Components != "undefined") {
     // Avoid double inclusion
     return;
   }
   exports.OS.Shared.Win = {};
 
   let LOG = OS.Shared.LOG.bind(OS.Shared, "Win", "allthreads");
 
   // Open libc
-  let libc = ctypes.open("kernel32.dll");
-  if (!libc) {
-    throw new Error("Could not open kernel32.dll");
+  let libc;
+  try {
+    libc = ctypes.open("kernel32.dll");
+  } catch (ex) {
+    // Note: If you change the string here, please adapt consumers and
+    // tests accordingly
+    throw new Error("Could not open system library: " + ex.message);
   }
   exports.OS.Shared.Win.libc = libc;
 
   // Define declareFFI
   let declareFFI = OS.Shared.declareFFI.bind(null, libc);
   exports.OS.Shared.Win.declareFFI = declareFFI;
 
   // Define Error
--- a/toolkit/components/osfile/ospath_unix_back.jsm
+++ b/toolkit/components/osfile/ospath_unix_back.jsm
@@ -11,17 +11,34 @@
  *
  * - paths are valid;
  * - paths are defined with one of the grammars that this module can
  *   parse (see later);
  * - all path concatenations go through function |join|.
  */
 if (typeof Components != "undefined") {
   this.EXPORTED_SYMBOLS = ["OS"];
-  Components.utils.import("resource://gre/modules/osfile/osfile_unix_allthreads.jsm", this);
+  let Scope = {};
+  Components.utils.import("resource://gre/modules/Services.jsm", Scope);
+
+  // Some tests need to import this module from any platform.
+  // We detect this by setting a bogus preference "toolkit.osfile.test.syslib_necessary"
+  // from the test suite
+  let syslib_necessary = true;
+  try {
+    syslib_necessary = Scope.Services.prefs.getBoolPref("toolkit.osfile.test.syslib_necessary");
+  } catch (x) {
+    // Ignore errors
+  }
+
+  try {
+    Components.utils.import("resource://gre/modules/osfile/osfile_unix_allthreads.jsm", this);
+  } catch (ex if !syslib_necessary && ex.message.startsWith("Could not open system library:")) {
+    // Executing this module without a libc is acceptable for this test
+  }
 }
 (function(exports) {
    "use strict";
    if (!exports.OS) {
      exports.OS = {};
    }
    if (!exports.OS.Unix) {
      exports.OS.Unix = {};
--- a/toolkit/components/osfile/ospath_win_back.jsm
+++ b/toolkit/components/osfile/ospath_win_back.jsm
@@ -12,24 +12,42 @@
  *
  * Limitations of this implementation.
  *
  * Windows supports 6 distinct grammars for paths. For the moment, this
  * implementation supports the following subset:
  *
  * - drivename:backslash-separated components
  * - backslash-separated components
+ * - \\drivename\ followed by backslash-separated components
  *
  * Additionally, |normalize| can convert a path containing slash-
  * separated components to a path containing backslash-separated
  * components.
  */
 if (typeof Components != "undefined") {
   this.EXPORTED_SYMBOLS = ["OS"];
-  Components.utils.import("resource://gre/modules/osfile/osfile_win_allthreads.jsm", this);
+  let Scope = {};
+  Components.utils.import("resource://gre/modules/Services.jsm", Scope);
+
+  // Some tests need to import this module from any platform.
+  // We detect this by setting a bogus preference "toolkit.osfile.test.syslib_necessary"
+  // from the test suite
+  let syslib_necessary = true;
+  try {
+    syslib_necessary = Scope.Services.prefs.getBoolPref("toolkit.osfile.test.syslib_necessary");
+  } catch (x) {
+    // Ignore errors
+  }
+
+  try {
+    Components.utils.import("resource://gre/modules/osfile/osfile_win_allthreads.jsm", this);
+  } catch (ex if !syslib_necessary && ex.message.startsWith("Could not open system library:")) {
+    // Executing this module without a libc is acceptable for this test
+  }
 }
 (function(exports) {
    "use strict";
    if (!exports.OS) {
      exports.OS = {};
    }
    if (!exports.OS.Win) {
      exports.OS.Win = {};
@@ -38,52 +56,69 @@ if (typeof Components != "undefined") {
      return; // Avoid double-initialization
    }
    exports.OS.Win.Path = {
      /**
       * Return the final part of the path.
       * The final part of the path is everything after the last "\\".
       */
      basename: function basename(path) {
-       ensureNotUNC(path);
+       if (path.startsWith("\\\\")) {
+         // UNC-style path
+         let index = path.lastIndexOf("\\");
+         if (index != 1) {
+           return path.slice(index + 1);
+         }
+         return ""; // Degenerate case
+       }
        return path.slice(Math.max(path.lastIndexOf("\\"),
-         path.lastIndexOf(":")) + 1);
+                                  path.lastIndexOf(":")) + 1);
      },
 
      /**
-      * Return the directory part of the path.  The directory part
-      * of the path is everything before the last "\\", including
-      * the drive/server name.
+      * Return the directory part of the path.
       *
       * If the path contains no directory, return the drive letter,
       * or "." if the path contains no drive letter or if option
       * |winNoDrive| is set.
       *
+      * Otherwise, return everything before the last backslash,
+      * including the drive/server name.
+      *
+      *
       * @param {string} path The path.
       * @param {*=} options Platform-specific options controlling the behavior
       * of this function. This implementation supports the following options:
       *  - |winNoDrive| If |true|, also remove the letter from the path name.
       */
      dirname: function dirname(path, options) {
-       ensureNotUNC(path);
        let noDrive = (options && options.winNoDrive);
 
        // Find the last occurrence of "\\"
        let index = path.lastIndexOf("\\");
        if (index == -1) {
          // If there is no directory component...
          if (!noDrive) {
            // Return the drive path if possible, falling back to "."
            return this.winGetDrive(path) || ".";
          } else {
            // Or just "."
            return ".";
          }
        }
 
+       if (index == 1 && path.charAt(0) == "\\") {
+         // The path is reduced to a UNC drive
+         if (noDrive) {
+           return ".";
+         } else {
+           return path;
+         }
+       }
+
        // Ignore any occurrence of "\\: immediately before that one
        while (index >= 0 && path[index] == "\\") {
          --index;
        }
 
        // Compute what is left, removing the drive name if necessary
        let start;
        if (noDrive) {
@@ -109,17 +144,22 @@ if (typeof Components != "undefined") {
        let paths = [];
        let root;
        let absolute = false;
        for each(let subpath in arguments) {
          let drive = this.winGetDrive(subpath);
          let abs   = this.winIsAbsolute(subpath);
          if (drive) {
            root = drive;
-           paths = [trimBackslashes(subpath.slice(drive.length))];
+           let component = trimBackslashes(subpath.slice(drive.length));
+           if (component) {
+             paths = [component];
+           } else {
+             paths = [];
+           }
            absolute = abs;
          } else if (abs) {
            paths = [trimBackslashes(subpath)];
            absolute = true;
          } else {
            paths.push(trimBackslashes(subpath));
          }
        }
@@ -130,41 +170,48 @@ if (typeof Components != "undefined") {
        if (absolute) {
          result += "\\";
        }
        result += paths.join("\\");
        return result;
      },
 
      /**
-      * Return the drive letter of a path, or |null| if the
-      * path does not contain a drive letter.
+      * Return the drive name of a path, or |null| if the path does
+      * not contain a drive name.
+      *
+      * Drive name appear either as "DriveName:..." (the return drive
+      * name includes the ":") or "\\\\DriveName..." (the returned drive name
+      * includes "\\\\").
       */
      winGetDrive: function winGetDrive(path) {
-       ensureNotUNC(path);
+       if (path.startsWith("\\\\")) {
+         // UNC path
+         if (path.length == 2) {
+           return null;
+         }
+         let index = path.indexOf("\\", 2);
+         if (index == -1) {
+           return path;
+         }
+         return path.slice(0, index);
+       }
+       // Non-UNC path
        let index = path.indexOf(":");
        if (index <= 0) return null;
        return path.slice(0, index + 1);
      },
 
      /**
       * Return |true| if the path is absolute, |false| otherwise.
       *
       * We consider that a path is absolute if it starts with "\\"
       * or "driveletter:\\".
       */
      winIsAbsolute: function winIsAbsolute(path) {
-       ensureNotUNC(path);
-       return this._winIsAbsolute(path);
-     },
-     /**
-      * As |winIsAbsolute|, but does not check for UNC.
-      * Useful mostly as an internal utility, for normalization.
-      */
-     _winIsAbsolute: function _winIsAbsolute(path) {
        let index = path.indexOf(":");
        return path.length > index + 1 && path[index + 1] == "\\";
      },
 
      /**
       * Normalize a path by removing any unneeded ".", "..", "\\".
       * Also convert any "/" to a "\\".
       */
@@ -172,18 +219,18 @@ if (typeof Components != "undefined") {
        let stack = [];
 
        // Remove the drive (we will put it back at the end)
        let root = this.winGetDrive(path);
        if (root) {
          path = path.slice(root.length);
        }
 
-       // Remember whether we need to restore a leading "\\".
-       let absolute = this._winIsAbsolute(path);
+       // Remember whether we need to restore a leading "\\" or drive name.
+       let absolute = this.winIsAbsolute(path);
 
        // Normalize "/" to "\\"
        path = path.replace("/", "\\");
 
        // And now, fill |stack| from the components,
        // popping whenever there is a ".."
        path.split("\\").forEach(function loop(v) {
          switch (v) {
@@ -237,32 +284,16 @@ if (typeof Components != "undefined") {
          absolute: this.winIsAbsolute(path),
          winDrive: this.winGetDrive(path),
          components: path.split("\\")
        };
      }
    };
 
     /**
-     * Utility function: check that a path is not a UNC path,
-     * as the current implementation of |Path| does not support
-     * UNC grammars.
-     *
-     * We consider that any path starting with "\\\\" is a UNC path.
-     */
-    let ensureNotUNC = function ensureNotUNC(path) {
-       if (!path) {
-          throw new TypeError("Expecting a non-null path");
-       }
-       if (path.length >= 2 && path[0] == "\\" && path[1] == "\\") {
-          throw new Error("Module Path cannot handle UNC-formatted paths yet: " + path);
-       }
-    };
-
-    /**
      * Utility function: Remove any leading/trailing backslashes
      * from a string.
      */
     let trimBackslashes = function trimBackslashes(string) {
       return string.replace(/^\\+|\\+$/g,'');
     };
 
    exports.OS.Path = exports.OS.Win.Path;
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js
@@ -9,17 +9,16 @@ 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_path();
   finish();
 };
 
 function test_init() {
   info("Starting test_init");
   importScripts("resource://gre/modules/osfile.jsm");
 }
 
@@ -177,35 +176,8 @@ function test_read_write()
   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_path()
-{
-  info("test_path: starting");
-  is(OS.Unix.Path.basename("a/b"), "b", "basename of a/b");
-  is(OS.Unix.Path.basename("a/b/"), "", "basename of a/b/");
-  is(OS.Unix.Path.basename("abc"), "abc", "basename of abc");
-  is(OS.Unix.Path.dirname("a/b"), "a", "basename of a/b");
-  is(OS.Unix.Path.dirname("a/b/"), "a/b", "basename of a/b/");
-  is(OS.Unix.Path.dirname("a////b"), "a", "basename of a///b");
-  is(OS.Unix.Path.dirname("abc"), ".", "basename of abc");
-  is(OS.Unix.Path.normalize("/a/b/c"), "/a/b/c", "normalize /a/b/c");
-  is(OS.Unix.Path.normalize("/a/b////c"), "/a/b/c", "normalize /a/b////c");
-  is(OS.Unix.Path.normalize("////a/b/c"), "/a/b/c", "normalize ///a/b/c");
-  is(OS.Unix.Path.normalize("/a/b/c///"), "/a/b/c", "normalize /a/b/c///");
-  is(OS.Unix.Path.normalize("/a/b/c/../../../d/e/f"), "/d/e/f", "normalize /a/b/c/../../../d/e/f");
-  is(OS.Unix.Path.normalize("a/b/c/../../../d/e/f"), "d/e/f", "normalize a/b/c/../../../d/e/f");
-  let error = false;
-  try {
-    OS.Unix.Path.normalize("/a/b/c/../../../../d/e/f");
-  } catch (x) {
-    error = true;
-  }
-  ok(error, "cannot normalize /a/b/c/../../../../d/e/f");
-  is(OS.Unix.Path.join("/tmp", "foo", "bar"), "/tmp/foo/bar", "join /tmp,foo,bar");
-  is(OS.Unix.Path.join("/tmp", "/foo", "bar"), "/foo/bar", "join /tmp,/foo,bar");
-  info("test_path: complete");
-}
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_win.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_win.js
@@ -8,17 +8,16 @@ self.onmessage = function(msg) {
     log("ignored message "+JSON.stringify(msg.data));
   };
 
   test_init();
   test_GetCurrentDirectory();
   test_OpenClose();
   test_CreateFile();
   test_ReadWrite();
-  test_path();
   finish();
 };
 
 function test_init() {
   info("Starting test_init");
   importScripts("resource://gre/modules/osfile.jsm");
 }
 
@@ -184,68 +183,8 @@ function test_ReadWrite()
   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_path()
-{
-  info("test_path: starting");
-  is(OS.Win.Path.basename("a\\b"), "b", "basename of a\\b");
-  is(OS.Win.Path.basename("a\\b\\"), "", "basename of a\\b\\");
-  is(OS.Win.Path.basename("abc"), "abc", "basename of abc");
-  is(OS.Win.Path.dirname("a\\b"), "a", "dirname of a\\b");
-  is(OS.Win.Path.dirname("a\\b\\"), "a\\b", "dirname of a\\b\\");
-  is(OS.Win.Path.dirname("a\\\\\\\\b"), "a", "dirname of a\\\\\\b");
-  is(OS.Win.Path.dirname("abc"), ".", "dirname of abc");
-  is(OS.Win.Path.normalize("\\a\\b\\c"), "\\a\\b\\c", "normalize \\a\\b\\c");
-  is(OS.Win.Path.normalize("\\a\\b\\\\\\\\c"), "\\a\\b\\c", "normalize \\a\\b\\\\\\\\c");
-  is(OS.Win.Path.normalize("\\a\\b\\c\\\\\\"), "\\a\\b\\c", "normalize \\a\\b\\c\\\\\\");
-  is(OS.Win.Path.normalize("\\a\\b\\c\\..\\..\\..\\d\\e\\f"), "\\d\\e\\f", "normalize \\a\\b\\c\\..\\..\\..\\d\\e\\f");
-  is(OS.Win.Path.normalize("a\\b\\c\\..\\..\\..\\d\\e\\f"), "d\\e\\f", "normalize a\\b\\c\\..\\..\\..\\d\\e\\f");
-  let error = false;
-  try {
-    OS.Win.Path.normalize("\\a\\b\\c\\..\\..\\..\\..\\d\\e\\f");
-  } catch (x) {
-    error = true;
-  }
-  ok(error, "cannot normalize \\a\\b\\c\\..\\..\\..\\..\\d\\e\\f");
-  is(OS.Win.Path.join("\\tmp", "foo", "bar"), "\\tmp\\foo\\bar", "join \\tmp,foo,bar");
-  is(OS.Win.Path.join("\\tmp", "\\foo", "bar"), "\\foo\\bar", "join \\tmp,\\foo,bar");
-
-  is(OS.Win.Path.basename("c:a\\b"), "b", "basename of c:a\\b");
-  is(OS.Win.Path.basename("c:a\\b\\"), "", "basename of c:a\\b\\");
-  is(OS.Win.Path.basename("c:abc"), "abc", "basename of c:abc");
-  is(OS.Win.Path.dirname("c:a\\b"), "c:a", "dirname of c:a\\b");
-  is(OS.Win.Path.dirname("c:a\\b\\"), "c:a\\b", "dirname of c:a\\b\\");
-  is(OS.Win.Path.dirname("c:a\\\\\\\\b"), "c:a", "dirname of c:a\\\\\\b");
-  is(OS.Win.Path.dirname("c:abc"), "c:", "dirname of c:abc");
-  let options = {
-    winNoDrive: true
-  };
-  is(OS.Win.Path.dirname("c:a\\b", options), "a", "dirname of c:a\\b");
-  is(OS.Win.Path.dirname("c:a\\b\\", options), "a\\b", "dirname of c:a\\b\\");
-  is(OS.Win.Path.dirname("c:a\\\\\\\\b", options), "a", "dirname of c:a\\\\\\b");
-  is(OS.Win.Path.dirname("c:abc", options), ".", "dirname of c:abc");
-
-  is(OS.Win.Path.normalize("c:\\a\\b\\c"), "c:\\a\\b\\c", "normalize c:\\a\\b\\c");
-  is(OS.Win.Path.normalize("c:\\a\\b\\\\\\\\c"), "c:\\a\\b\\c", "normalize c:\\a\\b\\\\\\\\c");
-  is(OS.Win.Path.normalize("c:\\\\\\\\a\\b\\c"), "c:\\a\\b\\c", "normalize c:\\\\\\a\\b\\c");
-  is(OS.Win.Path.normalize("c:\\a\\b\\c\\\\\\"), "c:\\a\\b\\c", "normalize c:\\a\\b\\c\\\\\\");
-  is(OS.Win.Path.normalize("c:\\a\\b\\c\\..\\..\\..\\d\\e\\f"), "c:\\d\\e\\f", "normalize c:\\a\\b\\c\\..\\..\\..\\d\\e\\f");
-  is(OS.Win.Path.normalize("c:a\\b\\c\\..\\..\\..\\d\\e\\f"), "c:d\\e\\f", "normalize c:a\\b\\c\\..\\..\\..\\d\\e\\f");
-  error = false;
-  try {
-    OS.Win.Path.normalize("c:\\a\\b\\c\\..\\..\\..\\..\\d\\e\\f");
-  } catch (x) {
-    error = true;
-  }
-  ok(error, "cannot normalize c:\\a\\b\\c\\..\\..\\..\\..\\d\\e\\f");
-  is(OS.Win.Path.join("c:\\tmp", "foo", "bar"), "c:\\tmp\\foo\\bar", "join c:\\tmp,foo,bar");
-  is(OS.Win.Path.join("c:\\tmp", "\\foo", "bar"), "c:\\foo\\bar", "join c:\\tmp,\\foo,bar");
-  is(OS.Win.Path.join("c:\\tmp", "c:\\foo", "bar"), "c:\\foo\\bar", "join c:\\tmp,c:\\foo,bar");
-  is(OS.Win.Path.join("c:\\tmp", "c:foo", "bar"), "c:foo\\bar", "join c:\\tmp,c:foo,bar");
-
-  info("test_path: complete");
-}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/osfile/tests/xpcshell/test_path.js
@@ -0,0 +1,130 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+Components.utils.import("resource://gre/modules/Services.jsm", this);
+Services.prefs.setBoolPref("toolkit.osfile.test.syslib_necessary", false);
+  // We don't need libc/kernel32.dll for this test
+
+let ImportWin = {};
+let ImportUnix = {};
+Components.utils.import("resource://gre/modules/osfile/ospath_win_back.jsm", ImportWin);
+Components.utils.import("resource://gre/modules/osfile/ospath_unix_back.jsm", ImportUnix);
+
+let Win = ImportWin.OS;
+let Unix = ImportUnix.OS;
+
+function do_check_fail(f)
+{
+  try {
+    let result = f();
+    do_print("Failed do_check_fail: " + result);
+    do_check_true(false);
+  } catch (ex) {
+    do_check_true(true);
+  }
+};
+
+function run_test()
+{
+  do_print("Testing Windows paths");
+
+  do_print("Backslash-separated, no drive");
+  do_check_eq(Win.Path.basename("a\\b"), "b");
+  do_check_eq(Win.Path.basename("a\\b\\"), "");
+  do_check_eq(Win.Path.basename("abc"), "abc");
+  do_check_eq(Win.Path.dirname("a\\b"), "a");
+  do_check_eq(Win.Path.dirname("a\\b\\"), "a\\b");
+  do_check_eq(Win.Path.dirname("a\\\\\\\\b"), "a");
+  do_check_eq(Win.Path.dirname("abc"), ".");
+  do_check_eq(Win.Path.normalize("\\a\\b\\c"), "\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("\\a\\b\\\\\\\\c"), "\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("\\a\\b\\c\\\\\\"), "\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("\\a\\b\\c\\..\\..\\..\\d\\e\\f"), "\\d\\e\\f");
+  do_check_eq(Win.Path.normalize("a\\b\\c\\..\\..\\..\\d\\e\\f"), "d\\e\\f");
+  do_check_fail(function() Win.Path.normalize("\\a\\b\\c\\..\\..\\..\\..\\d\\e\\f"));
+
+  do_check_eq(Win.Path.join("\\tmp", "foo", "bar"), "\\tmp\\foo\\bar", "join \\tmp,foo,bar");
+  do_check_eq(Win.Path.join("\\tmp", "\\foo", "bar"), "\\foo\\bar", "join \\tmp,\\foo,bar");
+  do_check_eq(Win.Path.winGetDrive("\\tmp"), null);
+  do_check_eq(Win.Path.winGetDrive("\\tmp\\a\\b\\c\\d\\e"), null);
+  do_check_eq(Win.Path.winGetDrive("\\"), null);
+
+
+  do_print("Backslash-separated, with a drive");
+  do_check_eq(Win.Path.basename("c:a\\b"), "b");
+  do_check_eq(Win.Path.basename("c:a\\b\\"), "");
+  do_check_eq(Win.Path.basename("c:abc"), "abc");
+  do_check_eq(Win.Path.dirname("c:a\\b"), "c:a");
+  do_check_eq(Win.Path.dirname("c:a\\b\\"), "c:a\\b");
+  do_check_eq(Win.Path.dirname("c:a\\\\\\\\b"), "c:a");
+  do_check_eq(Win.Path.dirname("c:abc"), "c:");
+  let options = {
+    winNoDrive: true
+  };
+  do_check_eq(Win.Path.dirname("c:a\\b", options), "a");
+  do_check_eq(Win.Path.dirname("c:a\\b\\", options), "a\\b");
+  do_check_eq(Win.Path.dirname("c:a\\\\\\\\b", options), "a");
+  do_check_eq(Win.Path.dirname("c:abc", options), ".");
+
+  do_check_eq(Win.Path.normalize("c:\\a\\b\\c"), "c:\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("c:\\a\\b\\\\\\\\c"), "c:\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("c:\\\\\\\\a\\b\\c"), "c:\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("c:\\a\\b\\c\\\\\\"), "c:\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("c:\\a\\b\\c\\..\\..\\..\\d\\e\\f"), "c:\\d\\e\\f");
+  do_check_eq(Win.Path.normalize("c:a\\b\\c\\..\\..\\..\\d\\e\\f"), "c:d\\e\\f");
+  do_check_fail(function() Win.Path.normalize("c:\\a\\b\\c\\..\\..\\..\\..\\d\\e\\f"));
+
+  do_check_eq(Win.Path.join("c:\\tmp", "foo", "bar"), "c:\\tmp\\foo\\bar", "join c:\\tmp,foo,bar");
+  do_check_eq(Win.Path.join("c:\\tmp", "\\foo", "bar"), "c:\\foo\\bar", "join c:\\tmp,\\foo,bar");
+  do_check_eq(Win.Path.join("c:\\tmp", "c:\\foo", "bar"), "c:\\foo\\bar", "join c:\\tmp,c:\\foo,bar");
+  do_check_eq(Win.Path.join("c:\\tmp", "c:foo", "bar"), "c:foo\\bar", "join c:\\tmp,c:foo,bar");
+  do_check_eq(Win.Path.winGetDrive("c:"), "c:");
+  do_check_eq(Win.Path.winGetDrive("c:\\"), "c:");
+  do_check_eq(Win.Path.winGetDrive("c:abc"), "c:");
+  do_check_eq(Win.Path.winGetDrive("c:abc\\d\\e\\f\\g"), "c:");
+  do_check_eq(Win.Path.winGetDrive("c:\\abc"), "c:");
+  do_check_eq(Win.Path.winGetDrive("c:\\abc\\d\\e\\f\\g"), "c:");
+
+  do_print("Backslash-separated, UNC-style");
+  do_check_eq(Win.Path.basename("\\\\a\\b"), "b");
+  do_check_eq(Win.Path.basename("\\\\a\\b\\"), "");
+  do_check_eq(Win.Path.basename("\\\\abc"), "");
+  do_check_eq(Win.Path.dirname("\\\\a\\b"), "\\\\a");
+  do_check_eq(Win.Path.dirname("\\\\a\\b\\"), "\\\\a\\b");
+  do_check_eq(Win.Path.dirname("\\\\a\\\\\\\\b"), "\\\\a");
+  do_check_eq(Win.Path.dirname("\\\\abc"), "\\\\abc");
+  do_check_eq(Win.Path.normalize("\\\\a\\b\\c"), "\\\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("\\\\a\\b\\\\\\\\c"), "\\\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("\\\\a\\b\\c\\\\\\"), "\\\\a\\b\\c");
+  do_check_eq(Win.Path.normalize("\\\\a\\b\\c\\..\\..\\d\\e\\f"), "\\\\a\\d\\e\\f");
+  do_check_fail(function() Win.Path.normalize("\\\\a\\b\\c\\..\\..\\..\\d\\e\\f"));
+
+  do_check_eq(Win.Path.join("\\\\a\\tmp", "foo", "bar"), "\\\\a\\tmp\\foo\\bar");
+  do_check_eq(Win.Path.join("\\\\a\\tmp", "\\foo", "bar"), "\\\\a\\foo\\bar");
+  do_check_eq(Win.Path.join("\\\\a\\tmp", "\\\\foo\\", "bar"), "\\\\foo\\bar");
+  do_check_eq(Win.Path.winGetDrive("\\\\"), null);
+  do_check_eq(Win.Path.winGetDrive("\\\\c"), "\\\\c");
+  do_check_eq(Win.Path.winGetDrive("\\\\c\\abc"), "\\\\c");
+
+  do_print("Testing unix paths");
+  do_check_eq(Unix.Path.basename("a/b"), "b");
+  do_check_eq(Unix.Path.basename("a/b/"), "");
+  do_check_eq(Unix.Path.basename("abc"), "abc");
+  do_check_eq(Unix.Path.dirname("a/b"), "a");
+  do_check_eq(Unix.Path.dirname("a/b/"), "a/b");
+  do_check_eq(Unix.Path.dirname("a////b"), "a");
+  do_check_eq(Unix.Path.dirname("abc"), ".");
+  do_check_eq(Unix.Path.normalize("/a/b/c"), "/a/b/c");
+  do_check_eq(Unix.Path.normalize("/a/b////c"), "/a/b/c");
+  do_check_eq(Unix.Path.normalize("////a/b/c"), "/a/b/c");
+  do_check_eq(Unix.Path.normalize("/a/b/c///"), "/a/b/c");
+  do_check_eq(Unix.Path.normalize("/a/b/c/../../../d/e/f"), "/d/e/f");
+  do_check_eq(Unix.Path.normalize("a/b/c/../../../d/e/f"), "d/e/f");
+  do_check_fail(function() Unix.Path.normalize("/a/b/c/../../../../d/e/f"));
+
+  do_check_eq(Unix.Path.join("/tmp", "foo", "bar"), "/tmp/foo/bar", "join /tmp,foo,bar");
+  do_check_eq(Unix.Path.join("/tmp", "/foo", "bar"), "/foo/bar", "join /tmp,/foo,bar");
+}
--- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 head =
 tail =
 
+[test_path.js]
 [test_osfile_async.js]
 [test_profiledir.js]